Greetings,

here is a small problem i have.. the client sends a request to the server for a file. the server receives the request from the client, forms a header which contains info about the file to be trasmitted, sends the header and then sends the actual file. The problem is that the client however does not nessesarily receive the info in the same order ! is this correct or am i doing something wrong?
What happens is that i want to receive the header first, get the info about the file (size, name, blah) and then recieve the actual file..

is there a way to accomblish this?
(thomas, random are you reading ;) )

thanks.
Posted on 2001-11-27 15:33:32 by Ray
You're doing something wrong because whatever you send will be received in the same order... Well not at the hardware & IP level, but the TCP level ensures you get the data in the right order..

So there's probably something wrong with sending the data. How are you sending it?

Thomas
Posted on 2001-11-27 15:48:05 by Thomas
Thomas,
:


; Handle socket messages
mov eax, lParam
.IF ax == FD_ACCEPT
shr eax, 16
.if eax == NULL
invoke accept, sock, NULL,NULL
.if eax != INVALID_SOCKET
mov sock2, eax
invoke WSAAsyncSelect, sock2, hWin, WM_CLIENTSOCK, FD_WRITE OR FD_READ OR FD_CLOSE
.endif
.endif

.ELSEIF ax == FD_READ
shr eax, 16
.if eax == NULL
invoke ioctlsocket, sock2, FIONREAD, ADDR dataAmount
.if eax==NULL
invoke recv, sock2, ADDR buffer1, dataAmount, 0
invoke ContructHeader, ADDR buffer
invoke send, sock2, ADDR HeaderBuffer, 250, NULL ; <<---Send Header Here

.endif
.endif


.ENDIF

.....
.elseif uMsg == WM_CLIENTSOCK
mov eax, lParam
.IF ax == FD_WRITE
shr eax, 16
.if eax == NULL
; wParam is client socket handle here
invoke send, wParam, pMem, FileSize, NULL ; <<-- Send actual file here
.IF eax==SOCKET_ERROR
MsgBox 'Error sending to clientsock' ;error occurred!!!
.ELSE
;here, eax is number of bytes actually sent. Note that this
;may be less than 4096!!!!!
.ENDIF



:grin:
Posted on 2001-11-27 16:12:31 by Ray
The problem is the design of the synchronisation, the code you posted does exactly this:


when data arrives at socket -> read data and send header
when client is ready for writing -> send file


This looks okay when all the events happen at the right place, but this is rarely the case..

First of all, remember you are using non-blocking sockets, which means that send will not necessairily send all the bytes you feed it, and that recv may receive less bytes than you ask. Using ioctlsocket is good because you will receive the maximum number of bytes available at that moment, but it doesn't mean you've received the full request, as it may be split in multiple parts!. So using single recv's and send's here will cause problems.

The received data (from the client) may come in parts, which means you get multipe FD_READ messages, and everytime the header is sent. This isn't correct.

The FD_WRITE message simply means 'client is ready for writing'. This message is sent when the client is ready after you called WSAAsyncSelect, and thereafter when send fails with WSAEWOULDBLOCK. So this event is of no use to time the sending of the file.

I'll give an example of something that may happen:


- server accepts connection
- wsaasyncselect is called
- client socket is ready for writing, FD_WRITE msg sent
- [b]server sends file[/b]
- client sends first part of request (this can happen)
- server receives data and [b]sends header[/b]
- client sends second part of request
- server receives data and [b]sends header again[/b]
- (Imagine sending of the header failed with WSAEWOULDBLOCK, because the client socket wasn't ready for writing)
- Winsock waits until client socket is ready for writing
- FD_WRITE msg sent
- [b]server sends file again[/b]


This example may be a little exaggerated, but it's perfectly possible that this would happen. In this case, the client would receive this: file,header,header again,file again

The solution is to create a bunch of flags and counters, to check what you were sending, how much data is already sent, etc...
Then each time before sending, check what you have to send, which part of it, and try to send it. If send fails (because it would block), wait for the FD_WRITE message and proceed sending.
Maybe you can add your own custom message (WM_CONTINUESEND or something) that sends the next part of the full response (= header and file)..

I'll try to explain a bit more tomorrow, right now I'm a bit tired.

Thomas
Posted on 2001-11-27 16:41:44 by Thomas
Good morning,
Thomas,

so you are suggesting to use a flag of somekind (on the server side) to await for the complete header to send. the header is 250 bytes long, so i guess i can use this correct? So, unless the header is sent i should not send anything else to the client.
After succesful sent of the header, i can proceed with the actual file send :)

If you can change a bit the above code to accomodate this changes i would appreciate it.

thanks.
Posted on 2001-11-28 01:22:07 by Ray

so you are suggesting to use a flag of somekind (on the server side) to await for the complete header to send. the header is 250 bytes long, so i guess i can use this correct? So, unless the header is sent i should not send anything else to the client.
After succesful sent of the header, i can proceed with the actual file send.

Yes that's right, one thing to add: you also have to wait for the complete request to be received. Never assume one recv will receive all the data you need, although this is true most of the time.

Here's an example design for your program:
(btw I don't know if your request has a fixed size too, but you'll have to make some code that checks if you've received the full request)



[b]data:[/b]
buffers:
- requestBuffer : a buffer for your request
- headerBuffer : the buffer that will contain your file header
- pMem & FileSize: you already have these ones.

flags:
- currentAction dd ?
; this flag tells you what you are currently doing
; make some flags for this, say:
;ACTION_RECEIVING_REQUEST equ 1
;ACTION_SENDING_HEADER equ 2
;ACTION_SENDING_FILE equ 2
;ACTION_DONE equ 3
; this flag has to be initialized to ACTION_RECEIVING_REQUEST before processing
; a request

vars:
- requestBytesReceived dd ? ; number of bytes of the request already received
- headerBytesSent dd ? ; number of bytes of the header already sent
- fileBytesSent dd ? ; number of bytes of the file already sent
; all these vars have to be initialized to zero before processing a request

constants:
WM_CONTINUE_SENDING equ WM_USER + 120

--CODE--

[b]on FD_READ:[/b]
.IF currentAction==ACTION_RECEIVING_REQUEST
; --- get maximum buffer size ---
mov eax, MAX_REQUEST_BUFFER_SIZE ;define this constant somewhere
; --- get number of bytes aready received ---
mov ecx, requestBytesReceived
; --- get number of bytes left in buffer (max - already done) ---
sub eax, ecx
; --- create current buffer pointer ---
add ecx, offset requestBuffer
; --- *add* data to buffer ---
invoke recv, sock2, ecx, eax, NULL
.IF eax==SOCKET_ERROR
invoke WSAGetLastError
.IF eax!=WSAEWOULDBLOCK ; if WSAEWOULDBLOCK, do nothing,
; just wait for more data
; error occurred!!!
.ENDIF
.ELSE
; update number of bytes received
add requestBytesReceived, eax
.ENDIF

invoke CheckIfFullRequestReceived ;create this proc yourself
.IF yes, fully received
; start with sending the header:
mov currentAction, ACTION_SENDING_HEADER
invoke SendMessage, hWin, WM_CONTINUE_SENDING, NULL, NULL
.ENDIF
.ELSE
;client sends data after the request,
;bad client :)
.ENDIF

[b]on FD_WRITE:[/b]
; if ready for writing, and currently sending something, continue
; with sending.
.IF currentAction==ACTION_SENDING_HEADER || \
currentAction==ACTION_SENDING_FILE
invoke SendMessage, hWin, WM_CONTINUE_SENDING, NULL, NULL
.ENDIF

[b]on WM_CONTINUE_SENDING:[/b]
.IF currentAction==ACTION_SENDING_HEADER

@send_next_header_part:
; get number of bytes already sent:
mov eax, headerBytesSent

; get number of bytes yet to do:
mov ecx, 250
sub ecx, eax

; get the right buffer pointer
add eax, offset headerBuffer

; send next part of data:
invoke send, sock2, eax, ecx, NULL

.IF eax==SOCKET_ERROR
invoke WSAGetLastError
.IF eax!=WSAEWOULDBLOCK
; error occurred!!
.ENDIF
;else, just wait for the FD_WRITE message, it will
;send the WM_CONTINUE_SENDING message again.

ret ;!!!!!!!! don't forget this one

.ELSE
;update counter:
add headerBytesSent, eax
.IF headerBytesSent==250 ; header done?
;start sending file
mov currentAction, ACTION_SENDING_FILE
jmp @send_next_file_part
.ENDIF
.ENDIF
; no WSAEWOULDBLOCK, client is still ready for writing, so send more:
jmp @send_next_header_part

.ELSEIF currentAction==ACTION_SENDING_FILE

@send_next_file_part:

; get number of bytes already sent:
mov eax, fileBytesSent

; get number of bytes yet to do:
mov ecx, FileSize
sub ecx, eax

; get the right buffer pointer
add eax, pMem

; send next part of data:
invoke send, sock2, eax, ecx, NULL

.IF eax==SOCKET_ERROR
invoke WSAGetLastError
.IF eax!=WSAEWOULDBLOCK
; error occurred!!
.ENDIF
;else, just wait for the FD_WRITE message, it will
;send the WM_CONTINUE_SENDING message again.

ret ;!!!!!!!! don't forget this one

.ELSE
;update counter:
add fileBytesSent, eax
mov eax, FileSize
.IF fileBytesSent==eax ; file done?
; DONE!!!!
mov currentAction, ACTION_DONE

ret ; !!! don't forget this one either

.ENDIF
.ENDIF
; no WSAEWOULDBLOCK, client is still ready for writing, so send more:
jmp @send_next_file_part
.ENDIF


Thomas
P.S. I have written this code without testing but it will probably work.
P.S.2. You'd better read it with a syntax highlighting editor, looks so much better :) .
Posted on 2001-11-28 04:58:01 by Thomas
Thanks Thomas,

let me spend some time working with this....
my first attempt gives me a socket error when trying to send the header. I am sure i messed up somehere :rolleyes:

'i'll be back'
Posted on 2001-11-29 01:42:54 by Ray