高性能异步等待插座 [英] High performance asynchronous awaiting sockets

查看:142
本文介绍了高性能异步等待插座的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写一个应用程序,将需要进行数百个socket连接通过TCP读/写数据。

我所遇到这个code片段这里,我不知道我怎样才能使这个更强劲。

这是目前我怎么了调用code:

 的foreach(在listofIps VAR IP)
{
   IPEndPoint remoteEP =新IPEndPoint(IPAddress.Parse(IP),4001);
   客户端的Socket =新的Socket(AddressFamily.InterNetwork,
                           SocketType.Stream,ProtocolType.Tcp);
   client.Connect(remoteEP);
   等待ReadAsync(客户端);
}


  1. 这有什么错上述,以及它如何进行优化,使得它同时运行?

    在code段中,缓冲区的大小设置为1000。正如一个简单的例子,如果我试图打印出只收到的字节数,而不是剩余0x00s,我不得不做一些像这样的:

     ,而(真)
    {
        等待s.ReceiveAsync(awaitable);
        INT读取动作= args.BytesTransferred;
        如果(读取动作< = 0)中断;
        VAR十六进制=新的StringBuilder(读取动作* 2);
        VAR味精=新的字节[读取动作]    的for(int i = 0; I<读取动作,我++)
            味精[I] = args.Buffer [I]    的foreach(味精BYTE B)
            hex.AppendFormat({0:X2},B);    AppendLog(的String.Format(RX:{0},十六进制));
    }


  2. 是否有这样做的更有效的方法? previously,我会遍历整个缓冲区和数据打印出来,但会给我一大堆尾随0x00s作为我的协议是之间的任何地方60到70字节长。



解决方案

  

我写一个应用程序,将需要进行数百个socket连接通过TCP读/写数据。


您不需要高性能插座了点。在code就简单得多了与常规性能插槽。

对于初学者来说,不要从链接中使用自定义awaitables您发布。它们是一些人完全没有(完全​​稳健),但你并不需要它们,你的code会更简单,没有他们。


  

      
  1. 这有什么错的上方,可以将其进一步优化?

  2.   

是的。你不应该阻止混合(连接)和异步的( ReadAsync )code。我建议是这样的:

 的foreach(在listofIps VAR IP)
{
  IPEndPoint remoteEP =新IPEndPoint(IPAddress.Parse(IP),4001);
  客户端的Socket =新的Socket(AddressFamily.InterNetwork,
                             SocketType.Stream,ProtocolType.Tcp);
  等待client.ConnectTaskAsync(remoteEP);
  ...
}

其中, ConnectTaskAsync 是一个标准TAP -over-APM包装

 公共静态任务ConnectTaskAsync(此Socket插座,端点endpoint)
{
  返回TaskFactory.FromAsync(socket.BeginConnect,socket.EndConnect,端点,NULL);
}

正如马克Gravell指出,这code(和你原来的code)的连接插座一次。你可以使用 Task.WhenAll 来他们都同时连接。


  

2)是否有这样做的更有效的方法?


首先,你应该定义一个TAP-过APM ReceiveTaskAsync 包装与上述类似。当二进制数据打交道,我也喜欢有字节数组的扩展方法为之倾倒:

 公共字符串DumpHex(这ArraySegment<位>数据)
{
  返回的string.join(,data.Select(二= GT; b.T​​oString(X2)));
}

然后你就可以有code是这样的:

 ,而(真)
{
  INT读取动作=等待socket.ReceiveTaskAsync(缓冲);
  如果(读取动作== 0)破;
  VAR数据=新ArraySegment<位>(缓冲,0,读取动作);
  AppendLog(RX:+ data.HexDump());
  ...
}

如果你做了很多的二进制操作的,你会发现我的 ArraySegments库有帮助的。


  

3),我应该怎样包括逻辑来检查,如果我的整个数据的单一读取内赶到


哦,这是比这更复杂。 :)套接字是一个的的抽象,而不是一个的的消息的抽象。所以,如果你想定义你的协议消息,你需要包括一个长preFIX或分隔符字节,从而可以检测消息边界。然后,你需要写code,将分析出您的邮件,牢记从套接字读取数据块可能只包含了部分消息(以便您有缓冲的话),一个完整的消息,多个完整的信息,并且也可以结束与一个部分消息(同样,缓冲)。你必须接受新的块时,还要考虑现有的缓冲区。

我有一个 TCP / IP套接字.NET 的常见问题在我的博客这解决这一专门并具有一定的例如code 用我个人的默认preference消息帧(4个字节的小-endian长度prefixing)。


  

4)如何应包括writeasync方法,这样我可以在读取中间通过套接字发送数据。


那一个令人惊讶的棘手:

 公共静态任务< INT> SendTaskAsync(此Socket套接字,字节[]缓冲区,诠释抵消,诠释大小的Socket标志)
{
  返回任务< INT> .Factory.FromAsync(socket.BeginSend,socket.EndSend,缓冲区,偏移量,大小,标志,NULL);
}
公共静态任务WriteAsync(此Socket套接字,字节[]缓冲区)
{
  INT bytesSent = 0;
  而(bytesSent!= buffer.Length)
  {
    bytesSent + =等待socket.SendTaskAsync(数据,bytesSent,buffer.Length - bytesSent,SocketFlags.None);
  }
}

I am writing an app that will require to make hundreds of socket connections over tcp to read/write data.

I have come across this code snippet here and I'm wondering how I can make this more robust.

This is currently how I am calling the code:

foreach (var ip in listofIps)
{
   IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(ip), 4001);
   Socket client = new Socket(AddressFamily.InterNetwork,
                           SocketType.Stream, ProtocolType.Tcp);
   client.Connect(remoteEP);
   await ReadAsync(client);
}

  1. Is there anything wrong with the above, and how can it be optimized such that it runs concurrently?

    In the code snippet, the buffer size is set to 1000. Just as a simple illustration, if I were to attempt to print out only the bytes received, and not the remaining 0x00s, I have to do something like this:

    while (true)
    {
        await s.ReceiveAsync(awaitable);
        int bytesRead = args.BytesTransferred;
        if (bytesRead <= 0) break;
        var hex = new StringBuilder(bytesRead * 2);
        var msg = new byte[bytesRead];
    
        for (int i = 0; i < bytesRead; i++)                
            msg[i] = args.Buffer[i];                
    
        foreach (byte b in msg)                
            hex.AppendFormat("{0:x2} ", b);
    
        AppendLog(string.Format("RX: {0}", hex));
    }
    

  2. Is there a more efficient way of doing this? Previously, I would iterate the whole buffer and print out the data, but that will give me a whole bunch of trailing 0x00s as my protocol is anywhere between 60 to 70 bytes long.

解决方案

I am writing an app that will require to make hundreds of socket connections over tcp to read/write data.

You don't need "high-performance sockets" for that. The code is far simpler with regular-performance sockets.

For starters, don't use the custom awaitables from the link you posted. They are perfectly fine for some people (and completely "robust"), but you don't need them and your code will be simpler without them.

  1. Is there anything wrong with the above, and can it be further optimized?

Yes. You shouldn't mix blocking (Connect) and asynchronous (ReadAsync) code. I would recommend something like this:

foreach (var ip in listofIps)
{
  IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(ip), 4001);
  Socket client = new Socket(AddressFamily.InterNetwork,
                             SocketType.Stream, ProtocolType.Tcp);
  await client.ConnectTaskAsync(remoteEP);
  ...
}

Where ConnectTaskAsync is a standard TAP-over-APM wrapper:

public static Task ConnectTaskAsync(this Socket socket, EndPoint endpoint)
{
  return TaskFactory.FromAsync(socket.BeginConnect, socket.EndConnect, endpoint, null);
}

As Marc Gravell pointed out, this code (and your original code) is connecting the sockets one at a time. You could use Task.WhenAll to connect them all simultaneously.

2) Is there a more efficient way of doing this?

First, you should define a TAP-over-APM ReceiveTaskAsync wrapper similar to the above. When dealing with binary data, I also like to have an extension method on byte arrays for dumping:

public string DumpHex(this ArraySegment<byte> data)
{
  return string.Join(" ", data.Select(b => b.ToString("X2")));
}

Then you can have code like this:

while (true)
{
  int bytesRead = await socket.ReceiveTaskAsync(buffer);
  if (bytesRead == 0) break;
  var data = new ArraySegment<byte>(buffer, 0, bytesRead);
  AppendLog("RX: " + data.HexDump());
  ...
}

If you do a lot of binary manipulation, you may find my ArraySegments library helpful.

3) Where and how should I include the logic to check if my whole data has arrived within a single read

Oh, it's more complex than that. :) Sockets are a stream abstraction, not a message abstraction. So if you want to define "messages" in your protocol, you need to include a length prefix or delimiter byte so you can detect the message boundaries. Then you need to write code that will parse out your messages, keeping in mind that blocks of data read from the socket may contain only a partial message (so you have to buffer it), a complete message, multiple complete messages, and may also end with a partial message (again, buffering). And you have to also consider your existing buffer when receiving the new block.

I have a TCP/IP .NET Sockets FAQ on my blog that addresses this specifically and has some example code using my personal default preference for message framing (4-byte little-endian length prefixing).

4) How should I include a writeasync method such that I can send data through the socket in the middle of reads.

That one's surprisingly tricky:

public static Task<int> SendTaskAsync(this Socket socket, byte[] buffer, int offset, int size, SocketFlags flags)
{
  return Task<int>.Factory.FromAsync(socket.BeginSend, socket.EndSend, buffer, offset, size, flags, null);
}
public static Task WriteAsync(this Socket socket, byte[] buffer)
{
  int bytesSent = 0;
  while (bytesSent != buffer.Length)
  {
    bytesSent += await socket.SendTaskAsync(data, bytesSent, buffer.Length - bytesSent, SocketFlags.None);
  }
}

这篇关于高性能异步等待插座的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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