C#/套接字:接收数据并从中构建消息

时间:2017-04-14 05:52:29

标签: c# .net sockets networking tcp

我正在尝试使用异步套接字实现可以发送和接收“Nessages”(自定义类型)的系统。每条消息包含三个元素:

首先我发送Message-ID(4个字节),接下来是数据的长度(4个字节),然后是实际数据。

TCP / IP的工作方式,我不知道完整的Message是否在我刚收到的数据缓冲区中。我需要先查找InfoHeader(ID和长度),然后使用剩余的数据来填充Message的DataBuffer。

public void ReadCallback(IAsyncResult ar)
    {
        try
        {
            // How many bytes were received? They are now stored in the PacketBuffer
            int bytesReceived = Socket.EndReceive(ar);

            if (bytesReceived != 0)
            {
                // We did receive some data
                if (MessageInfoHeader.Length < NetProtocol.NetInfoHeaderSize)
                {
                    // The InfoHeader we have is not complete yet. How many bytes are missing from it?
                    int infoHeaderDataInCurrentBuffer = NetProtocol.NetInfoHeaderSize - (int) MessageInfoHeader.Length;

                    // Check if we have read enough data to fill the entire missing InfoHeader
                    if (infoHeaderDataInCurrentBuffer > bytesReceived)
                    {
                        // Nope, we dont. Fill as much as we can
                        infoHeaderDataInCurrentBuffer = bytesReceived;

                        Log.Info("We do not have enough data to complete the InfoHeader yet. Fill as much as we can");
                    }
                    else
                    {
                        Log.Info("We do have enough data to complete the InfoHeader");
                    }

                    // Now fill it from the chunk of data we just received
                    MessageInfoHeader.Write(ReceivedData, 0, infoHeaderDataInCurrentBuffer);

                    // See if there is any data remaining in the buffer that we can store in the MessageDataBuffer
                    int dataRemainingInPacket = bytesReceived - infoHeaderDataInCurrentBuffer;
                    Log.Info("DataRemainingInPacket: " + dataRemainingInPacket);

                    // Check again if we have the full header
                    if (MessageInfoHeader.Length == NetProtocol.NetInfoHeaderSize)
                    {
                        Log.Info("We now have assembled the full InfoHeader");

                        // We do have the full InfoHeader. Use the rest of the data to fill the DataBuffer
                        using (MemoryStream ms = new MemoryStream())
                        {
                            byte[] b = MessageInfoHeader.GetBuffer();
                            ms.Write(b, 0, b.Length);
                            ms.Seek(0, SeekOrigin.Begin);

                            try
                            {
                                using (BinaryReader br = new BinaryReader(ms))
                                {
                                    CurrentMessageID = br.ReadInt32();
                                    CurrentMessageDataLength = br.ReadInt32();

                                    Log.Info("InfoHeader: MessageID=" + CurrentMessageID + " / DataLength=" + CurrentMessageDataLength);
                                }
                            }
                            catch (Exception ex)
                            {
                                throw ex;
                            }
                        }

                        if (dataRemainingInPacket > 0)
                        {
                            Log.Info("There are " + dataRemainingInPacket + " bytes remaining in the packet. Writing them to the PacketDataBuffer");

                            MessageDataBuffer.Write(ReceivedData, CurrentPacketBytesRead, ReceivedData.Length - CurrentPacketBytesRead);
                        }
                    }
                }
                else
                {
                    MessageDataBuffer.Write(ReceivedData, 0, ReceivedData.Length);

                    if(ReceivedData.Length <= NetDataBufferSize)
                    {
                        Log.Info("WE HAVE RECEIVED THE ENTIRE MESSAGE");
                    }
                }

                // Start receiving the new TCP-Packet
                Socket.BeginReceive(ReceivedData, 0, ReceivedData.Length, SocketFlags.None, new AsyncCallback(ReadCallback), null);
            }
            else
            {
                Log.Info("No data has been received");
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

private void AcceptCallback(IAsyncResult ar)
    {
        // Retrieve the listening Socket
        Socket listener = (Socket)ar.AsyncState;

        // Retrieve the Socket that is actually connected to the client
        Socket csocket = listener.EndAccept(ar);

        Log.Info("Accepted connection from: " + csocket.RemoteEndPoint.ToString());

        // Create a new client with the CSocket and add it to the list
        ClientConnection client = ClientManager.AddClient(csocket);

        // Start reading data from the Client's Socket and then enter the "read-loop" inside the client
        client.Socket.BeginReceive(client.ReceivedData, 0, (int) client.ReceivedData.Length, SocketFlags.None, new AsyncCallback(client.ReadCallback), null);

        // Turn the listener back into listening mode
        listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
    }

    public const int NetInfoHeaderSize = 8; // 2x int

    public static byte[] FrameMessage(NetMessage message)
    {
        byte[] result;

        using(MemoryStream ms = new MemoryStream())
        {
            using (BinaryWriter bw = new BinaryWriter(ms))
            {
                bw.Write(message.ID);
                bw.Write(message.Data.Length);
                bw.Write(message.Data);
            }

            result = ms.ToArray();
        }

        return result;
    }

这段代码显然无法正常工作。我多次收到“RECEIVED ENTIRE MESSAGE”行。

问题:我究竟做错了什么?我没能绕过处理这样的事情的最佳方法?提前感谢任何输入!

1 个答案:

答案 0 :(得分:0)

可能存在很多问题。但是我可以在“RECEIVED ENTIRE MESSAGE”循环中看到的第一个是你假设ReceivedData缓冲区完全被Socket.EndReceive(ar);填充 - 事实并非如此,所以第一个故障排除开始可能是重写这个:

MessageDataBuffer.Write(ReceivedData, 0, ReceivedData.Length);
if (ReceivedData.Length <= NetDataBufferSize)
{
    Log.Info("WE HAVE RECEIVED THE ENTIRE MESSAGE");
}

为:

MessageDataBuffer.Write(ReceivedData, 0, bytesReceived);
if (MessageDataBuffer.Length <= NetDataBufferSize)
{
    Log.Info("WE HAVE RECEIVED THE ENTIRE MESSAGE");
}

假设MessageDataBuffer旨在包含整个消息