C# Socket.Receive 消息长度 [英] C# Socket.Receive message length

查看:163
本文介绍了C# Socket.Receive 消息长度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在开发可以接受来自多台客户端计算机的多个连接的 C# Socket 服务器.服务器的目标是允许客户端订阅"和取消订阅"服务器事件.

I'm currently in the process of developing a C# Socket server that can accept multiple connections from multiple client computers. The objective of the server is to allow clients to "subscribe" and "un-subscribe" from server events.

到目前为止,我已经愉快地浏览了此处:http://msdn.microsoft.com/en-us/library/5w7b7x5f(v=VS.100).aspxhttp://msdn.microsoft.com/en-us/library/fx6588te.aspx 获取想法.

So far I've taken a jolly good look over 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 the messages I send are encrypted, so I take the string message that I wish to send, convert it into a byte[] array and then encrypt the data before pre-pending the message length to the data and sending it out over the connection.

让我印象深刻的一件事是:在接收端,Socket.EndReceive()(或关联的回调)似乎有可能在只收到一半消息时返回.有没有一种简单的方法可以确保每条消息都完整"地收到并且一次只收到一条消息?

One thing that strikes me as an issue is this: on the receiving end it seems possible that Socket.EndReceive() (or the associated callback) could return when only half of the message has been received. Is there an easy way to ensure each message is received "complete" and only one message at a time?

例如,我认为 .NET/Windows 套接字不会包装"消息以确保在一个 Socket.Receive 中接收使用 Socket.Send() 发送的单个消息() 称呼?或者是吗?

For example, I take it .NET / Windows sockets does not "wrap" the messages to ensure that a single message sent with Socket.Send() is received in one Socket.Receive() call? Or does it?

到目前为止我的实现:

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);
    }         
}

推荐答案

是的,您可能只有一部分消息每个接收,而且在仅传输过程中可能会更糟将发送部分消息.通常您可以在网络状况不佳或网络负载较重的情况下看到这一点.

Yes, it is possible you'll have only part of message per one receiving, also it can be even worse during transfer only part of message will be sent. Usually you can see that during bad network conditions or under heavy network load.

在网络级别要明确 TCP 保证按指定顺序传输您的数据,但不保证数据的一部分将与您发送的相同.使用该软件的原因有很多(例如,查看 Nagle 算法)、硬件(跟踪中的不同路由器)、操作系统实现,因此通常您不应假设数据的哪一部分已经传输或接收.

To be clear on network level TCP guaranteed to transfer your data in specified order but it not guaranteed that portions of data will be same as you sent. There are many reasons for that software (take a look to Nagle's algorithm for example), hardware (different routers in trace), OS implementation, so in general you should never assume what part of data already transferred or received.

对不起,介绍太长,下面是一些建议:

Sorry for long introduction, below some advices:

  1. 尝试为高性能套接字服务器使用相关的新"API,这里示例.NET v4.0 的网络示例

  1. Try to use relatevely "new" API for high-performance socket server, here samples Networking Samples for .NET v4.0

不要假设您总是发送完整的数据包.Socket.EndSend() 返回实际计划发送的字节数,它在重网络负载下甚至可以是 1-2 个字节.因此,您必须在需要时重新发送缓冲区的其余部分.

Do not assume you always send full packet. Socket.EndSend() returns number of bytes actually scheduled to send, it can be even 1-2 bytes under heavy network load. So you have to implement resend rest part of buffer when it required.

MSDN 上有警告:

不能保证数据你发送的会出现在网络上立即地.增加网络效率,底层系统可能延迟传输直到显着收集的传出数据量.圆满完成BeginSend 方法意味着底层系统有空间缓冲您的数据以供网络发送.

There is no guarantee that the data you send will appear on the network immediately. To increase network efficiency, the underlying system may delay transmission until a significant amount of outgoing data is collected. A successful completion of the BeginSend method means that the underlying system has had room to buffer your data for a network send.

  • 不要假设您总是收到完整的数据包.将接收到的数据加入某种缓冲区,并在有足够数据时对其进行分析.

  • Do not assume you always receive full packet. Join received data in some kind of buffer and analyze it when it have enough data.

    通常,对于二进制协议,我添加字段来指示传入的数据量、带有消息类型的字段(或者您可以使用每种消息类型的固定长度(通常不好,例如版本控制问题))、版本字段(在适用的情况下)并将 CRC 字段添加到消息末尾.

    Usually, for binary protocols, I add field to indicate how much data incoming, field with message type (or you can use fixed length per message type (generally not good, e.g. versioning problem)), version field (where applicable) and add CRC-field to end of message.

    它不是真的需要阅读,有点旧并且直接适用于 Winsock 但也许值得研究:Winsock 程序员常见问题解答

    It not really required to read, a bit old and applies directly to Winsock but maybe worth to study: Winsock Programmer's FAQ

    看看ProtocolBuffers,值得学习:http://code.google.com/p/protobuf-csharp-port/,http://code.google.com/p/protobuf-net/p>

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

    希望有帮助.

    附言遗憾的是,您在 MSDN 上引用的示例有效地破坏了其他答案中所述的异步范式.

    P.S. Sadly sample on MSDN you refer in question effectively ruin async paradigm as stated in other answers.

    这篇关于C# Socket.Receive 消息长度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆