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

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

问题描述

我目前在开发,可以接受来自多个客户端计算机的多个连接一个C#的Socket服务器的过程。服务器的目的是允许客户机订阅和从服务器的事件非订阅

到目前为止,我已经接管这里快活好好看看:<一href=\"http://msdn.microsoft.com/en-us/library/5w7b7x5f(v=VS.100).aspx\">http://msdn.microsoft.com/en-us/library/5w7b7x5f(v=VS.100).aspx和<一个href=\"http://msdn.microsoft.com/en-us/library/fx6588te.aspx\">http://msdn.microsoft.com/en-us/library/fx6588te.aspx的想法。

我所发送的邮件是加密的,所以我走,我想之前发送,将其转换成一个byte []数组,然后对数据进行加密的字符串消息pre-未决消息长度的数据和它发送出过连接。

这在我看来是一个问题,有一件事情是这样的:在接收端看来可能Socket.EndReceive()(或相关的回调)时,已收到只有消息的一半可以返回。有一种简单的方法,以确保每封邮件收到完整,并在同一时间只有一个消息?

编辑:例如,我把它.NET / Windows套接字没有包装的邮件,以确保与Socket.Send()发送一条在一个Socket.Receive收到()调用?还是没有呢?

我实施至今:

 私人无效StartListening()
{
    IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
    IPEndPoint localEP =新IPEndPoint(ipHostInfo.AddressList [0],Constants.PortNumber);    套接字监听=新的Socket(localEP.Address.AddressFamily,SocketType.Stream,ProtocolType.Tcp);
    listener.Bind(localEP);
    listener.Listen(10);    而(真)
    {
        //重置事件。
        this.listenAllDone.Reset();        //开始等待连接
        listener.BeginAccept(新的AsyncCallback(this.AcceptCallback),监听器);        //等待该事件。
        this.listenAllDone.WaitOne();
    }
}私人无效AcceptCallback(IAsyncResult的AR)
{
    //获取处理客户端请求的插口。
    套接字监听=(插座)ar.AsyncState;
    套接字句柄= listener.EndAccept(AR);    //信号中的主线程继续。
    this.listenAllDone.Set();    //接受传入连接,并保存在客户端数据到新的Socket的参考。
    CClient客户端=新CClient();
    client.Socket =处理程序;    锁定(this.clientList)
    {
        this.clientList.Add(客户端);
    }    而(真)
    {
        this.readAllDone.Reset();        //开始从客户端等待数据。
        handler.BeginReceive(client.DataBuffer,0,client.DataBuffer.Length,0,新的AsyncCallback(this.ReadCallback),客户端);        this.readAllDone.WaitOne();
    }
}私人无效ReadCallback(IAsyncResult的ASYN)
{
    CClient theClient =(CClient)asyn.AsyncState;    //结束接收并获取读的字节数。
    INT IRX = theClient.Socket.EndReceive(ASYN);
    如果(IRX!= 0)
    {
        //数据从套接字读取。
        //所以保存数据
        字节[] = recievedMsg新的字节[IRX]
        Array.Copy(theClient.DataBuffer,recievedMsg,IRX);        this.readAllDone.Set();        //德code收到并据此采取行动的消息。
        theClient.De codeAndProcessMessage(recievedMsg);        //返回等待数据。
        this.WaitForData(theClient);
    }
}


解决方案

是的,有可能你就会有消息的一部分的每一的接收,也可以是传输过程中更差仅消息的一部分将被发送。通常你可以看到,在恶劣网络条件或网络负载繁重。

要上保证按指定顺序传输数据的网络级别的TCP清楚,但它不能保证你发送的数据的部分将相同。有许多原因是什么软件(看看来内格尔的例如算法),硬件(以跟踪不同的路由器),OS实现,所以一般你不应该承担什么样的数据的一部分已经转移或接收。

对不起长介绍,以下一些建议:


  1. 尝试的使用relatevely新的高性能套接字服务器API,这里样品<一个href=\"http://archive.msdn.microsoft.com/nclsamples/Wiki/View.aspx?title=Socket%20Performance\">Networking对于.NET V4.0样品


  2. 的想你总是发送完整的数据包。 Socket.EndSend()返回实际计划要发送的字节数,它可以更1- 2字节的网络负载繁重。所以,你的必须的实施缓冲区重新发送剩余的部分时,需要它。

    有警告MSDN上:


      

    有不保证数据
      您发送会出现在网络上
      立即。为了增加网络
      效率,底层系统可
      延迟传输,直到一个显著
      传出数据的量被回收。
      的成功完成
      BeginSend方法意味着
      底层系统已经有房
      缓冲自己的数据网络发送。



  3. 的假定你总是收到完整的数据包。加入接收到的数据在某种缓冲,当它有足够的数据进行分析。


  4. 通常,对于二进制协议,我添加字段,以指示多少数据传入,消息类型字段(或者你可以使用每个消息类型固定长度(一般不是很好,比如版本问题)),版本字段(如适用),并添加CRC场结束的消息。


  5. 这不是真的需要阅读,有点老直接适用于对Winsock但也许值得研究:的Winsock编程常见问题解答


  6. 看一看到 ProtocolBuffers ,值得学习:<一href=\"http://$c$c.google.com/p/protobuf-csharp-port/\">http://$c$c.google.com/p/protobuf-csharp-port/, HTTP://$c$c.google.com/p/protobuf-net/


希望它帮助。

P.S。可悲的是MSDN上的样品,你有问题是指有效地毁了异步模式在其他的答案说。

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.

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.

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?

EDIT: 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?

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

解决方案

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.

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. Try to use relatevely "new" API for high-performance socket server, here samples Networking Samples for .NET v4.0

  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.

    There is warning on MSDN:

    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.

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

  4. 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.

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

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

Hope it helps.

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天全站免登陆