C # socket. Getting message length

I am currently developing a Socket Socket server that can accept multiple connections from multiple client computers. The purpose of the server is to allow clients to "subscribe" and "unsubscribe" from server events.

So far I have looked good here: http://msdn.microsoft.com/en-us/library/5w7b7x5f(v=VS.100).aspx and http://msdn.microsoft.com/en-us/library /fx6588te.aspx for ideas.

All sent messages are encrypted, so I take the string message that I want to send, convert it to a byte [] array, and then encrypt the data before bringing the message length to the data and sending it over the connection.

One thing that strikes me as a problem is this: on the receiving side, it seems possible that Socket.EndReceive () (or the associated callback) can return when only half of the message is received. Is there an easy way to ensure that each message is received β€œcomplete” and only one message at a time?

EDIT: For example, I believe that .NET / Windows sockets do not "wrap" messages to ensure that one message sent from Socket.Send () is received in a single Socket.Receive () call? Or that?

My implementation so far:

private void StartListening() { IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName()); IPEndPoint localEP = new IPEndPoint(ipHostInfo.AddressList[0], Constants.PortNumber); Socket listener = new Socket(localEP.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); listener.Bind(localEP); listener.Listen(10); while (true) { // Reset the event. this.listenAllDone.Reset(); // Begin waiting for a connection listener.BeginAccept(new AsyncCallback(this.AcceptCallback), listener); // Wait for the event. this.listenAllDone.WaitOne(); } } private void AcceptCallback(IAsyncResult ar) { // Get the socket that handles the client request. Socket listener = (Socket) ar.AsyncState; Socket handler = listener.EndAccept(ar); // Signal the main thread to continue. this.listenAllDone.Set(); // Accept the incoming connection and save a reference to the new Socket in the client data. CClient client = new CClient(); client.Socket = handler; lock (this.clientList) { this.clientList.Add(client); } while (true) { this.readAllDone.Reset(); // Begin waiting on data from the client. handler.BeginReceive(client.DataBuffer, 0, client.DataBuffer.Length, 0, new AsyncCallback(this.ReadCallback), client); this.readAllDone.WaitOne(); } } private void ReadCallback(IAsyncResult asyn) { CClient theClient = (CClient)asyn.AsyncState; // End the receive and get the number of bytes read. int iRx = theClient.Socket.EndReceive(asyn); if (iRx != 0) { // Data was read from the socket. // So save the data byte[] recievedMsg = new byte[iRx]; Array.Copy(theClient.DataBuffer, recievedMsg, iRx); this.readAllDone.Set(); // Decode the message recieved and act accordingly. theClient.DecodeAndProcessMessage(recievedMsg); // Go back to waiting for data. this.WaitForData(theClient); } } 
+7
source share
4 answers

Yes, it is possible that you will only have a part of the message at a time, it can also be worse during transmission, only a part of the message will be sent. You can usually see that in poor network conditions or under heavy network load.

To be understood at the network level, TCP guaranteed the transfer of your data in the specified order, but did not guarantee that the parts of the data would be the same as you sent. There are many reasons for this software (look, for example, at the Nagle algorithm ), hardware (different routers in the trace), OS implementation, so in general, you should never assume how much of the data has already been transmitted or received.

Sorry for the long introduction, below are some tips:

  • Try using the relatedvely "new" API for a high-performance socket server, here are examples of networking examples for .NET v4.0

  • Do not assume that you always send the full package. Socket.EndSend () returns the number of bytes actually sent, it can even be 1-2 bytes under heavy network load. Thus, you need to resubmit the rest of the buffer when necessary.

    MSDN has a warning:

    There is no guarantee that the data you send is online. To increase network efficiency, the base system can delay transmission up to a significant amount of outgoing data. Successful completion The BeginSend method means that the underlying system has had a place to buffer its data to send over the network.

  • Do not assume that you always get the full package. Attach the received data in some buffer and analyze it when it has enough data.

  • Usually for binary protocols I add a field to indicate the amount of incoming data, a field with a message type (or you can use a fixed length for a message type (usually not very good, for example, a problem with the version)), a version field (where applicable) and add a CRC field at the end of the message.

  • No need to read, get a little outdated and directly access Winsock, but it might be worth exploring: Frequently Asked Questions about the Winsock Programmer

  • Take a look at ProtocolBuffers , worth knowing: http://code.google.com/p/protobuf-csharp-port/ , http://code.google.com/p/protobuf-net/

Hope this helps.

PS Unfortunately, the MSDN sample you are referencing effectively destroys the asynchronous paradigm, as indicated in other answers.

+10
source

Your code is very wrong. Performing such loops strikes the goal of asynchronous programming. Async IO is used to not block the flow, but to allow them to continue to do other work. Looping this way, you block the flow.

 void StartListening() { _listener.BeginAccept(OnAccept, null); } void OnAccept(IAsyncResult res) { var clientSocket = listener.EndAccept(res); //begin accepting again _listener.BeginAccept(OnAccept, null); clientSocket.BeginReceive(xxxxxx, OnRead, clientSocket); } void OnReceive(IAsyncResult res) { var socket = (Socket)res.Asyncstate; var bytesRead = socket.EndReceive(res); socket.BeginReceive(xxxxx, OnReceive, socket); //handle buffer here. } 

Please note that I removed all error handling to clear the code. This code does not block the thread and therefore is much more efficient. I would break the code in two classes: server processing code and client processing code. This simplifies maintenance and expansion.

The next thing to understand is that TCP is a streaming protocol. It does not guarantee that a message arrives at one Recipient. Therefore, you should know how big the message is or when it ends.

The first solution is to prefix each message with a heading that you first parse and then continue to read until you get the full body / message text.

The second solution is to put some control character at the end of each message and continue reading until the control character is read. Keep in mind that you must encode this character if it can exist in the actual message.

+3
source

You need to send messages with a fixed length or include the length of the message in the header. Try to have something that clearly identifies the beginning of the package.

+2
source

All Articles