I'm writing an async socket server that transport custom serialized objects. However, I ran into a problem when sending a lot of messages in a loop from the server to the client. To simplify matters I shortened my test code:
Here I'm sending some data to the client...
for(int i = 0; i < 100; i++) { state.WorkSocket.BeginSend(b, 0, b.Length, SocketFlags.None, sendCallback, state); } |
The client receives the data and processes it:
StateObject state = (StateObject)ar.AsyncState; i = state.WorkSocket.EndReceive(ar); if (i == 0) { state.WorkSocket.Shutdown(SocketShutdown.Both); state.WorkSocket.Close(); } else { state.MemoryStream.Write(state.Buffer, 0, i); state.Buffer = new byte[StateObject.BufferSize]; MessageHandler.Handle(state); state.MemoryStream.Close(); state.MemoryStream = new MemoryStream(); state.WorkSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, receiveCallback, state); } |
However, here's the problem ... since the data is sent from the server very quickly and the socket doesn't block, I receive the data faster than it is processed, thus causing the MemoryStream filling up with data before I can close it.
What I'd like is that my ReceiveCallback method only may be called when I actually call WorkSocket.BeginReceive.
What's the best way to go about this
Thanks

Async sockets, receive callback problem
imyousuf
the thing is, even when I comment out BeginReceive in the client code, the ReceiveCallback method is called over and over again (100 times in this case). However, the ReceiveCallback never waits until processing the prior callback is finished but it calls directly into the method, thus filling up the MemoryStream with bytes before the code is reached where I clear the MemoryStream. This is because the socket is non blocking I think. Does this make sense
stephenstarring
thanks for you lengthy explanation. However, what I don't understand yet is this...
The ManualResetEvent signal the main thread to wait, right But I'd like that on a per socket basis. So, I call BeginSend on a socket but I don't want another BeginSend to be called before EndSend for THAT socket is called, right Because otherwise, sending data to all other sockets would wait as well, right
Thanks,
Tom
wayne_p
Malar has explained the sync/async details. If you have multiple sockets and
you want to maintain one send at a time you could declare a state object as follows
class state
{
Socket s;
AutoResetEvent are = new AutoResetEvent(false);
...
}
Then in you main loop you could simply send as many sends on different sockets
and wait for any of the events to get signaled. I believe there is a limit on the
number of number of events you could wait on.
In any case you should think about the design of you app so that you don't
get into these issues. You should be able to send as the data is available
and receive as the data is available.
As per the last question, you can receive 0 bytes when the peer
closes the connection gracefully
dev_kh
I beleive Durga is recommending against serializing your calls to Begin/EndSend for performance reasons as that way you can only service one request at a time.
SAinCA
You state:
What I'd like is that my ReceiveCallback method only may be called when I actually call WorkSocket.BeginReceive.
What's the best way to go about this
The callback method (delegate) passed to a BeginXXX call (BeginReceive in this case) is called once for each call you make to BeginReceive. So, in this case, the ReceiveCallback method will only be called once for each call you make to BeginReceive.
Please let me know if I am misinterpreting your question.
GAT2000
Yes, you are right about this but I did post that ;)
That's why I was asking how I can make the server wait to send data to that specific socket until EndSend for that socket has returned
CGS
I'm calling BeginSend from another class:
public static void Send(ISocketObject socketObject, StateObject state)
{
state.MemoryStream = new MemoryStream();
state.Buffer = new byte[StateObject.BufferSize];
MemoryStream stream = new MemoryStream();
BinaryWriter writer = new BinaryWriter(stream);
socketObject.Write(writer);
byte[] b = stream.ToArray();
state.WorkSocket.BeginSend(b, 0, b.Length, SocketFlags.None, sendCallback, state);
}
so, the BeginSend method is called 100 times without ever waiting on EndSend being called. I guess that's the problem why I get the problem on the client side, am I right
The question is, how can I make the server code wait for EndSend to be called before another class can call my static Send method again
Sudhaks
what I don't understand ... when I look at the async client sample from msdn:
if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
new AsyncCallback(ReceiveCallback), state);
} else {
// All the data has arrived; put it in response.
if (state.sb.Length > 1) {
response = state.sb.ToString();
}
// Signal that all bytes have been received.
receiveDone.Set();
}
... they are checking if bytes received > 0 or not. However, in my client/server, I never get a receiveCallback with 0 bytes received
mrrogers912
Hm, sorry I don't quite understand what you mean
Maybe someone could should shed some more light into this what the best approach for my scenario is in gerneal...
SanthoshKumar
OK May be I was not clear. You can do serializtion of objects.
What I was saying is to not serialize the begin and end Send calls
renato sabo
Like Mike said, the ReceiveCallback method will be called once and only once for each call you make to BeginReceive. You had mentioned
>even when I comment out BeginReceive in the client code, the ReceiveCallback >method is called over and over again (100 times in this case).
You might want to verify this.. You had pasted the ReceiveCallback section in the client .. How about the place where you had started issuing a beginreceive call..
I strongly suspect that you have a for loop there .. something like this
for(int i = 0; i < 100; i++)
{
state.WorkSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, receiveCallback, state);
}
Send and receive are not synchronized.. Dont expect a receive to be called for every send.. Send might send 1000 bytes in 10 packets, but there is a possibility that receive will get all the 1000 bytes in just 1 call. On a similar note, send might send 1000 bytes in a packet and but you might have to call receive 10 times to read all of them
Asynchronous calls are nonblocking.. but there is only one endXXX associated with each beginXXX...
The beginsend returns immediately... but endSend is the blocking call.. If the send has not completed by the time endsend is called, endsend will block. SendCallback is called only when send is complete. So a endSend inside a SendCallback returns immediately.
So if you are code is like this
socket.BeginSend()
socket.EndSend()
EndSend will block till send has completed and this defeats the whole purpose of asynchronous programming.. you could have just used synchronous call instead.. If you want only one send to be active at a time and want some work to be done before you call the next send, you can use resetEvents to synchronize them..
Something like this
socket.BeginSend(.... sendcallback, ...)
<do some work.. before next beginsend is called>
resetEvent.WaitOne() // wait till beginsend completes and sendcallback is called
socket.BeginSend(.... sendcallback, ...) //issue the next send
In the sendcallback
... sendcallback(..)
{
//get socket
socket.EndSend();
//do work
resetEvent.Set();
}
Hope this helps
New User1
so, when I was to use AutoResetEvent how would the following scenario be handled:
Assuming I have 10 sockets connected and then start a for loop to send data to each of the 10 sockets. Would data be sent to each of the 10 sockets one by one but simultaneously to the 10 sockets or would data be sent to the first socket then waits until endsend and sends data 1 to second socket, etc.
Also, instead of using AutoResetEvent, would it be better to check for a specific byte constellation in receive and handle the message when that constellation is found That way I wouldn't have to wait for sends to complete, right What about using a 10 byte long "terminator" and check every 10th position for the specific byte, if it matches I also check the previous 9 bytes if it's really the terminator. That way it should be pretty fast
davenitup
Fortey