【问题标题】:Reliable way to determine variable message length when receiving from socket从套接字接收时确定可变消息长度的可靠方法
【发布时间】:2019-06-07 05:09:16
【问题描述】:

我有一些 Python 代码用于从相机捕获图像并将它们发送到 C# 服务器。

从客户端发送消息时,我在数据前面加上消息大小,这样我就知道要从套接字服务器端提取多少数据。

它似乎在大多数情况下运行良好,但偶尔 - 消息似乎不是以消息大小开头的。

我不确定为什么会发生这种情况,但我不知道如何处理它。

Python 代码:

while True:
    send_message("SEND_FRAME_DATA_HERE")

def send_message(message):

    message_size = len(message.encode())

    print (f"Message: {message_size} - {message}")

    my_socket.sendall(struct.pack(">L", message_size) + message.encode())

C#

private const int MESSAGE_CHUNK_SIZE = 4096;
private const int MESSAGE_PREFIX_SIZE = 4;

private void _receiveMessage(IAsyncResult ar)
{
    StateObject state = (StateObject)ar.AsyncState;
    Socket handler = state.workSocket;
    List<byte> messageBuffer = new List<byte>();
    byte[] tempBuffer = new byte[MESSAGE_CHUNK_SIZE];

    try
    {
        handler.EndReceive(ar);
        messageBuffer.AddRange(state.messageBuffer);

        while (true)
        {
            while (messageBuffer.Count < MESSAGE_PREFIX_SIZE)
            {
                handler.Receive(tempBuffer, 0, MESSAGE_CHUNK_SIZE, 0);
                messageBuffer.AddRange(tempBuffer);
            }

            int messageLength = _getMessageLength(messageBuffer);

            // Occasionally the four bytes determining message length
            // are read from what appears to be mid message
            if (messageLength > 20)
            {
                Console.Write("halp");
            }

            messageBuffer = messageBuffer.Skip(MESSAGE_PREFIX_SIZE).ToList();

            while (messageBuffer.Count < messageLength)
            {
                handler.Receive(tempBuffer, 0, StateObject.messageChunkSize, 0);
                messageBuffer.AddRange(tempBuffer);
            }

            var wholeMessage = messageBuffer.Take(messageLength).ToList();
            var messageString = Encoding.Default.GetString(wholeMessage.ToArray());

            Console.WriteLine(messageString);

            messageBuffer = messageBuffer.Skip(messageLength).ToList();
        }
    }
    catch (SocketException ex)
    {
        Console.WriteLine(ex.Message);
    }
}

private int _getMessageLength(List<byte> message)
{
    byte[] bytes = { message[3], message[2], message[1], message[0] };
    return BitConverter.ToInt32(bytes, 0);
}

消息缓冲区应如下所示:

运行良好:

运行不佳:

【问题讨论】:

  • 问题是您发送的大小错误,或者您没有从上一条消息中提取所有消息。 TCP有一个保持活动状态,它是一个零字节的数据报。所以忽略长度为零的消息。此外,您的字节数必须始终是相同的字节数。因此,如果您的大小为 0 65535(2 个字节),则不要发送一个字节作为大小。您收到的代码必须始终以两个字节为大小。
  • 客户端只是一遍又一遍地发送相同的字符串,所以我认为不可能是这样。我不确定您所说的字节数必须始终是相同的数字是什么意思?无论消息的大小如何,大小都应始终是整个消息的前四个字节。并且消息的长度可以是可变的

标签: c# .net sockets tcp


【解决方案1】:

问题似乎与此代码有关:

handler.Receive(tempBuffer, 0, StateObject.messageChunkSize, 0);
messageBuffer.AddRange(tempBuffer);

Socket.Receive() 返回实际读入tempBuffer 的字节数。您需要保存该值,然后使用它将正确的字节数复制到messageBuffer

int bytesRead = handler.Receive(tempBuffer, 0, StateObject.messageChunkSize, 0);
messageBuffer.AddRange(tempBuffer.Take(bytesRead));

【讨论】:

    猜你喜欢
    • 2014-03-02
    • 1970-01-01
    • 1970-01-01
    • 2015-01-02
    • 2011-10-18
    • 2016-02-28
    • 2013-02-06
    • 1970-01-01
    • 2017-09-04
    相关资源
    最近更新 更多