2013-06-13 6 views
4

Я пишу приложение, которое потребует сотни сокетов для подключения к tcp для чтения/записи данных.Высокопроизводительные асинхронные ожидающие сокеты

Я столкнулся с this code snippet here, и мне интересно, как я могу сделать это более надежным.

Это в настоящее время, как я звоню код:

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. Существует ничего плохого с вышеизложенным, и как она может быть оптимизирована таким образом, что он работает одновременно?

    В фрагменте кода размер буфера установлен в 1000. Как простая иллюстрация, если бы я попытался распечатать только полученные байты, а не оставшиеся 0x00, я должен сделать что-то вроде этого:

    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. Есть ли более эффективный способ сделать это? Раньше я перебирал весь буфер и распечатывал данные, но это даст мне целую цепочку конечных 0x00, так как мой протокол составляет от 60 до 70 байт.

+2

Вы не можете получить много ответов на этот вопрос, поскольку это является чрезмерно широким и спросите вы, такие как «это может быть дополнительно оптимизирована?». Ответы, как правило, громкие да. – MarcF

+0

Я указал области, которые я хотел, чтобы они были оптимизированы. Я не думаю, что этот вопрос настолько широк, насколько вы его озвучиваете. Я не спрашивал: «Можно ли это оптимизировать?» в общем –

+0

@PapyrusBit Но вы задали три или четыре вопроса, которые напрямую не связаны. Вы должны задать только один вопрос одновременно. – svick

ответ

17

Я пишу приложение, которое потребуется, чтобы сделать сотни соединений сокетов через TCP для чтения/записи данных.

Для этого вам не нужны «высокопроизводительные сокеты». Код намного проще с сокетами с нормальной производительностью.

Для начала не используйте пользовательские ожидания из указанной вами ссылки. Они отлично подходят для некоторых людей (и полностью «надежны»), но они вам не нужны, и ваш код будет проще без них.

  1. Существует ничего плохого с вышеизложенным, и она может быть оптимизирована?

Да. Вы не должны смешивать блокировку (Connect) и асинхронный (ReadAsync) код. Я бы рекомендовал что-то вроде этого:

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

Где ConnectTaskAsync является standard TAP-over-APM wrapper:

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

Как Марк Gravell отметил, этот код (и исходный код) подключаются сокеты по одному за раз. Вы можете использовать Task.WhenAll, чтобы соединить их все одновременно.

2) Есть ли более эффективный способ сделать это?

Во-первых, вы должны определить оболочку TAP-over-APM ReceiveTaskAsync, аналогичную описанной выше.При работе с двоичными данными, я также хотел бы иметь метод расширения байтовых массивов для захоронения:

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

Тогда вы можете иметь такой код:

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

Если вы много бинарных манипуляций , вы можете найти мой ArraySegments library полезным.

3) Где и как я должен включать в себя логику, чтобы проверить, все мои данные прибыли в один прочитать

О, это более сложный, чем это. :) Розетки - это поток абстракция, а не сообщение абстракция. Поэтому, если вы хотите определить «сообщения» в своем протоколе, вам нужно указать префикс длины или разделитель байта, чтобы вы могли обнаружить границы сообщений. Затем вам нужно написать код, который будет анализировать ваши сообщения, имея в виду, что блоки данных, считанные из сокета, могут содержать только частичное сообщение (поэтому вам нужно его буферировать), полное сообщение, несколько полных сообщений, а также может завершите частичное сообщение (опять-таки, буферизацию). И вы должны также рассмотреть свой существующий буфер при получении нового блока.

У меня есть TCP/IP .NET Sockets FAQ в моем блоге, что addresses this specifically и имеет некоторые example code, используя мое личное предпочтение по умолчанию для обрамления сообщений (префикс длины в байтах длиной 4 байта).

4) Как включить метод writeasync, чтобы я мог отправлять данные через сокет в середине чтения.

Это один удивительно сложно:

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

В вашей последней части кода: 1. 'SendTaskAsync()' должен возвращать 'Task ', а не просто 'int'. 2. Почему вы боксируете '0' как' state'? Я думаю, что «нуль» имеет больше смысла. 3. Не имело бы смысла использовать 'byte [], int, int' перегрузку вместо' IList > 'перегрузка? – svick

+0

@svick: Спасибо за исправления. Обновлено. «0» - это неудачная привычка, с тех пор как я сделал много встроенных программ в C за последние 18 месяцев, и нет никакого «нуля» в C. Я использовал перегрузку «ArraySegment», поэтому мне не пришлось бы копировать массивы вокруг, если запись только частично прошла (редко, но возможно). –

+0

С другой перегрузкой вам все равно ничего не нужно будет копировать, вы просто вызовите метод снова с помощью другого 'offset'. И вам не нужно будет создавать одноэлементный массив. – svick

Смежные вопросы