бредущий по жизни (svoysredychuzih) wrote in csharp,
бредущий по жизни
svoysredychuzih
csharp

Socket Programming

Hi,

I have created a socket connection as described on MDSN web site:
client: http://msdn.microsoft.com/en-us/library/bew39x2a(v=VS.85).aspx
server: http://msdn.microsoft.com/en-us/library/fx6588te(v=VS.85).aspx

It works fine. Events here are:
1) Client sends request
2) Server receives it
3) Server sends response
4) Server shutdown the socket
5) Client receives response

Then, I know, that between 2) an 3) might be a time gap. I want to send few interim responses like:
2a) Server sends response: Still working on your request.
The problem here is that I cannot send a response without shutting down the socket, but as I shout it down I am not able to send neither 3) nor another 2a)

Is it possible for client to get response without closing the socket connection? Something like .flush()?


corresponding piece of code:
...
   public class MathTask {
      public string inputData;
      public int MaxTryCount;
      public int DelayBetweenNotificationsSec;
      public bool complete = false;
      public MathTask(string _data, int _maxTry, int _delayBetweenNotifications) {
         inputData = _data;
         MaxTryCount = _maxTry;
         DelayBetweenNotificationsSec = _delayBetweenNotifications;
         complete = false;
      }
   }
...
...
      private static void Send(Socket handler, MathTask taskData)
      {
         // Convert the string data to byte data using ASCII encoding.
         byte[] byteData = Encoding.ASCII.GetBytes(taskData.inputData);

         if (taskData.complete || (taskData.MaxTryCount <= 0))
         { // final sending
            // Begin sending the data to the remote device.
            IAsyncResult res = handler.BeginSend(byteData, 0, byteData.Length, 0,
                new AsyncCallback(SendCallback), handler);

         }
         else
         { // interim (notification) sending
            // Begin sending the data to the remote device.
            StateObject so = new StateObject();
            so.workSocket = handler;
            so.sb.Append(taskData.inputData);
            IAsyncResult res = handler.BeginSend(byteData, 0, byteData.Length, 0,
                new AsyncCallback(SendInterimCallback), so);
         }
      }

      // after sending notification (task is running)
      private static void SendInterimCallback(IAsyncResult ar)
      {
         try
         {
            // Retrieve the socket from the state object.
            StateObject so = (StateObject)ar.AsyncState;
            int bytesSent = so.workSocket.EndSend(ar);
            
            Console.WriteLine("Sent {0} bytes to client.", bytesSent);

            // client receives response ONLY AFTER this statement
            so.workSocket.Shutdown(SocketShutdown.Both);
            so.workSocket.Close();

         }
         catch (Exception e)
         {
            Console.WriteLine(e.ToString());
         }
      }
...



Thanks for response, the problem solved.

That was not the server, but the client. Take a look at ReceiveCallback:
private static void ReceiveCallback( IAsyncResult ar ) {
        try {
            // Retrieve the state object and the client socket 
            // from the asynchronous state object.
            StateObject state = (StateObject) ar.AsyncState;
            Socket client = state.workSocket;

            // Read data from the remote device.
            int bytesRead = client.EndReceive(ar);

            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();
            }
        } catch (Exception e) {
            Console.WriteLine(e.ToString());
        }
    }


If client reads any bytesRead more than 0, it will store it in buffer and keep waiting for more data to come. Only when 0 bytes read it calls receiveDone.Set(); which finalize the receive process. Now I understand why it was working only after socket became closed.

  • Post a new comment

    Error

    default userpic
  • 5 comments
Perhaps I don't understand the questions, but why can't you send and receive multiple responses. I mean, sure, there will be some overhead of checking the responses, but you should be checking the responses.

I have a couple of services that I have written that do similar tasks. My standard rule in that a "Send" will send an Int64 with the data size, the raw data, and then receive an Int32 from the other side with a status code. Both sides can do unlimited amounts of this until someone calls Disconnect, Shutdown, Close, or however you decide to do it.

By doing this, this service can check a receive buffer, once something gets into it, it can grab the first 8 bytes to get a buffer length. It then receives that much data, what that data is and how it should deal with it, and respond with a known 4 byte return code.
Thanks, I have found the problem. It was the client, I have made an update to the original post.
That's why I send the length first. I had a hard time debugging a transmission once because I would drain the receive buffer before everything got sent. This cause another error down the line that was really difficult to trace.
Sorry for my bad english. I think there is also another problem. If some IP pockets lost, they are resend by TCP/IP. But if network is partially disconnected, TCP/IP does not throw Exception. It think than all is normal, and pockets are lost. So, we need to control any socked send operation at high level. TCP is not much differents from unreliable UDP. For example, does not have auto reconnect.
Your English is just perfect. :-) Thanks for reply, I have found the problem in my code. See the update to the original post if interested.