UDP. Могу ли я отправить две части дейтаграммы и заставить принимающую сторону объединить их в одну?

Возможно, это глупый вопрос, но, поскольку я относительно новичок в UDP, здесь все идет ... Если у меня есть два отдельных массива байтов, которые мне нужно, чтобы принимающая сторона получила как один большой массив, например:

byte[] Array1 = {1,1,1}
byte[] Array2 = {2,2,2}

Могу ли я избежать необходимости создавать буфер и копировать в него каждый массив, а затем отправлять этот буфер, например:

byte[] Buffer= new byte[Array1.Length + Array2.Length];
Buffer.BlockCopy(Array1, 0, Buffer, 0, Array1.Length);
Buffer.BlockCopy(Array2, 0, Buffer, Array1.Length, Array2.Length);

udpClient.Send(Buffer, Buffer.Length);

Потому что, если они большие, а скорость передачи данных высокая, копирование использует много системных ресурсов... Итак, могу ли я как-то сообщить udpClient, что я запускаю фрагментацию UDP, а затем сделать так:

udpClient.ImStartingOneBigDatagram();

udpClient.Send(Array1, Array1.Length);
udpClient.Send(Array2, Array2.Length);

udpClient.ThatsAllFolks();

И быть уверенным, что принимающая сторона получит:

byte[] recv = {1,1,1,2,2,2}

Я использую для этого C#, и мне не нужно использовать UdpClient, я просто высказал свою точку зрения.


person Cipi    schedule 30.06.2011    source источник
comment
С UDP вы даже не можете быть уверены, что принимающая сторона получит их, не говоря уже о том, будут ли они получены по порядку. Дейтаграммы либо принимаются целиком, либо не принимаются вообще. Принимающая сторона не будет объединять несколько дейтаграмм в один прием.   -  person Mark H    schedule 30.06.2011
comment
@Mark H: А как насчет фрагментации UDP? В пакете UDP есть флаг MF (More Fragments), если бы я мог установить его на 1 в первом пакете и на 0 в последнем, это было бы так же, как IP выполняет фрагментацию пакета UDP... нет?   -  person Cipi    schedule 30.06.2011
comment
Cipi, флаг MF находится в IP-пакете, а не в UDP!   -  person user703016    schedule 30.06.2011
comment
Ах да, IP, извините... Я имел в виду это... Могу ли я установить этот флаг из C#??   -  person Cipi    schedule 01.07.2011
comment
Я не вижу ничего плохого в этом случае. У вас есть один Send и один Receive.   -  person Ben Voigt    schedule 01.10.2014
comment
BenVoigt: идея заключалась в том, что клиент отправляет 10 маленьких частей большего буфера, а чтение принимающей стороны считывает все это за 1 чтение (не 10 чтений).   -  person Cipi    schedule 14.01.2015
comment
@Cipi: я знаю, что это было опубликовано давным-давно, но я исключил обновленное решение из вопроса. Подумайте о том, чтобы опубликовать его как фактический ответ, если вы чувствуете, что он имеет ценность.   -  person Matt    schedule 24.06.2015


Ответы (2)


Используйте эквивалент Win32 API WSASendMsg. :

public int Send(
    IList<ArraySegment<byte>> buffers,
    SocketFlags socketFlags,
    out SocketError errorCode
)

http://msdn.microsoft.com/en-us/library/ms145161.aspx

Но в конечном итоге это преждевременная оптимизация, если вы проведете некоторое тестирование, вы увидите, что для использования массивов ввода-вывода с разбросом и сбором вам нужно как минимум 9 КБ данных для повышения производительности. Для небольших буферов, то есть менее размера страницы (4 КБ на x86), быстрее построить непрерывный буфер самостоятельно, прежде чем переходить к API сокета.

person Steve-o    schedule 01.07.2011
comment
Отправка сбора - это именно то, о чем идет речь, идеальный ответ. - person Ben Voigt; 01.07.2011
comment
ВООООООООООООООРКССССС!!!!!!!!!!!!! xDDD Проверьте мой обновленный вопрос в ближайшее время ... Спасибо, чувак! :D - person Cipi; 05.07.2011

Да, вы можете разделить любой блок данных на две части и отправить его в виде двух отдельных пакетов UDP. Однако UDP не гарантирует доставку, поэтому одна из частей может потеряться в пути. Что вы будете делать, если получатель получит только половину сообщения? Выбросьте это, очевидно, но кое-что нужно иметь в виду с UDP.

Итак, чтобы ответить на последнюю часть вашего вопроса "И быть уверенным, что принимающая сторона получит...", ответ - нет. Вы используете UDP, поэтому вы не можете быть уверены, что принимающая сторона что-нибудь получит.

Если вы хотите разбить отправку на несколько фрагментов для вашего удобства, а не из соображений размера сетевого пакета, вы, безусловно, можете написать свой собственный класс-оболочку, который находится над UDPclient и принимает несколько фрагментов данных через вызов метода Send и отправляет только все данные в udpclient.Send(), когда вы вызываете .ThatsAllFolks в своем классе-оболочке. Но это не уменьшит накладные расходы памяти, так как оболочка должна накапливать данные в памяти перед большой отправкой. Эта оболочка будет просто косметическим удобством для вашего клиентского кода.

person dthorpe    schedule 30.06.2011
comment
Хорошо, я готов рискнуть... Но как это сделать на C#?! Если я отправлю 2 массива, вызвав Send() 2 imes, принимающая сторона должна вызвать Receive() два раза, чтобы получить их, но я хочу, чтобы они пришли как один пакет, и мне все равно, потеряется ли один из них... :P - person Cipi; 30.06.2011
comment
AFAIK, если вы отправляете дважды, вы должны получить дважды. UDP не сливаются. - person dthorpe; 30.06.2011
comment
Подумайте об этом так: когда вы вызываете udpclient.Send(), данные немедленно передаются по сети. Когда вы вызываете .Send() во второй раз, первый UDP исчезает, поэтому вторая отправка отправляет второй UDP. - person dthorpe; 30.06.2011
comment
Итак, насколько я понимаю, сделать это невозможно... А как насчет флага MF протокола IP? Может ли он быть установлен пользователем или он автоматизирован по сети...? - person Cipi; 01.07.2011
comment
Я думаю, у вас будет такая же проблема с TCP Send() из-за встроенной семантики. Чтобы делать то, что вы просите, вам нужно работать на более низком уровне, где меньше семантической поддержки/меньше делается автоматически для вас за кулисами. Я не знаю, можете ли вы манипулировать стеком IP на этом уровне с C# или вам нужно переключиться на что-то еще, например, на сокеты. - person dthorpe; 01.07.2011
comment
Метафора, помогающая визуализировать то, против чего вы боретесь: Send() имеет встроенную семантику, заключающуюся в том, чтобы взять данные и отправить их по сети, завернутые в пакет, который говорит, что это полный пакет. Подумайте об автоматической двери в супермаркете: она открывается для вас, а затем закрывается. Его семантика открыта и закрыта. То, о чем вы просите, - это способ открыть, но не закрыть дверь, и это противоречит цели Send(). Вам нужно что-то другое, кроме Send, что-то более похожее на Put, которое не подразумевает завершение. - person dthorpe; 01.07.2011