C#Socket BeginReceive / EndReceive捕获多条消息

时间:2010-08-17 16:19:29

标签: c# sockets

问题:

当我做这样的事情时:

for (int i = 0; i < 100; i++)
{
    SendMessage( sometSocket, i.ToString());
    Thread.Sleep(250); // works with this, doesn't work without
}

无论有没有睡眠,服务器都会记录发送单独的消息。但是,如果没有睡眠,客户端最终会在单个OnDataReceived中接收多条消息,因此客户端将收到以下消息:

0, 1, 2, 34, 5, 678, 9 ....

服务器发送代码:

private void SendMessage(Socket socket, string message)
{
    logger.Info("SendMessage: Preparing to send message:" + message);            

    byte[] byteData = Encoding.ASCII.GetBytes(message);

    if (socket == null) return;
    if (!socket.Connected) return;

    logger.Info("SendMessage: Sending message to non " +
                "null and connected socket with ip:" + socket.RemoteEndPoint);

    // Record this message so unit testing can very this works.

    socket.Send(byteData);
}

客户收到代码:

private void OnDataReceived(IAsyncResult asyn)
{
    logger.Info("OnDataReceived: Data received.");

    try
    {
        SocketPacket theSockId = (SocketPacket)asyn.AsyncState;
        int iRx = theSockId.Socket.EndReceive(asyn);
        char[] chars = new char[iRx + 1];
        System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
        int charLen = d.GetChars(theSockId.DataBuffer, 0, iRx, chars, 0);
        System.String szData = new System.String(chars);

        logger.Info("OnDataReceived: Received message:" + szData);

        InvokeMessageReceived(new SocketMessageEventArgs(szData));

        WaitForData();  // .....  

套接字数据包:

public class SocketPacket
{
    private Socket _socket;
    private readonly int _clientNumber;
    private byte[] _dataBuffer = new byte[1024]; ....

我的预感是它与缓冲区大小有关,或者只是在OnDataReceived和EndReceive之间我们收到多条消息。

更新:当我在OnDataReceived的开头放置一个Thread.Sleep时,它会得到每条消息。唯一的解决方案是将此消息包装在长度前缀和字符串中以表示结束吗?

3 个答案:

答案 0 :(得分:11)

这是预期行为。 TCP套接字表示线性字节流,一系列分隔良好的“数据包”。您不能假设您收到的数据与发送时的数据相同。

请注意,这有两个后果:

  1. 两条消息可能会合并为一个回调调用。 (你注意到了这个。)
  2. 单个邮件可能会(在任何时候)分成两个单独的回调调用。
  3. 您的代码必须编写才能处理这两种情况,否则会有错误。

答案 1 :(得分:0)

没有必要放弃Tcp,因为它是面向流的。

您可以通过实施邮件框架来解决您遇到的问题。

http://blogs.msdn.com/malarch/archive/2006/06/26/647993.aspx

也:

http://nitoprograms.blogspot.com/2009/04/message-framing.html

答案 2 :(得分:-2)

TCP套接字并不总是立即发送数据 - 为了最大限度地减少网络流量,TCP / IP实现通常会将数据缓冲一段时间,并在看到暂停时(或当缓冲区已满时)发送数据

如果要确保逐个处理消息,则需要设置socket.NoDelay = true(这可能没有多大帮助,因为收到的数据可能仍然在接收缓冲区中聚集在一起) ,实现一些协议来分离流中的消息(比如为每个消息添加长度,或者使用CR / LF将它们分开),或者使用面向消息的协议,如SCTP(如果没有附加软件可能不支持)或UDP(如果你可以处理丢失的消息)。