2012-06-28 3 views
2

Если у меня есть один сокет, разделяемый между двумя процессами (или двумя потоками), и в обоих из них я пытаюсь отправить большое сообщение (большее, чем буфера протокола подчеркивания), которое блокирует, гарантировано, что оба сообщения будут отправляться последовательно? Или возможно, что сообщения будут чередоваться внутри ядра?Socket send concurrency гарантирует

Меня больше всего интересует поведение TCP по IP, но было бы интересно узнать, зависит ли оно от протокола сокета.

ответ

4

Вы спрашиваете, есть ли сообщение write() A, а затем B в одном и том же гнезде, гарантировано ли оно до B? Для SOCK_STREAM (например, TCP) и SOCK_SEQPACKET (почти никогда не используемых) сокетов ответ является неквалифицированным да. Для SOCK_DGRAM через Интернет (то есть UDP-пакеты) ответ отрицательный: пакеты могут быть переупорядочены сетью. На одном хосте, сокет datagram домена unix (во всех системах, которые я знаю) сохранит порядок, но я не верю, что это гарантировано любым стандартом, и я уверен, что есть краевые случаи.

Или подождите: возможно, вы спрашиваете, не будут ли смешанные сообщения, написанные этими двумя процессами? Да: одиночные системные вызовы (write/writev/sendto/sendmsg) всегда помещают их содержимое в дескриптор файла атомарно. Но, очевидно, если вы или ваша библиотека расщепляетесь, что записываете в несколько вызовов, вы теряете эту гарантию.

1

Для UDP, если два потока одновременно записываются в дескриптор сокета, оба сообщения будут отправляться как отдельные датаграммы. Может произойти фрагментация IP, если пакет больше MTU, но результирующие дейтаграммы будут сохранены и правильно собраны приемником. Другими словами, вы безопасны для UDP, за исключением обычных проблем, связанных с UDP (переупорядочение датаграммы, потеря пакетов и т. Д.).

Для TCP, который основан на потоке, я не знаю. Ваш вопрос по существу задает эквивалент «если два потока пытаются записать в один и тот же дескриптор файла, будет ли файл по-прежнему читаться?» На самом деле я не знаю ответа.

Простейшая вещь, которую вы можете сделать, это просто использовать безопасную блокировку потока (mutex) для защиты вызовов отправки и записи в сокет, чтобы только поток мог записывать в сокет за раз.

Для TCP я предлагаю иметь выделенный поток для обработки всех сокетов io. Затем просто придумайте способ, с помощью которого сообщения от рабочих прерываний могут быть асинхронно поставлены в очередь на поток сокета для его отправки. Точка сокета также может обрабатывать вызовы recv() и уведомлять другие потоки, когда соединение сокета завершается удаленной стороной.

+0

Я пишу в сокет из двух разных процессов, и вы столкнулись с моей проблемой: избегайте передачи сообщений между процессами (как это было бы необходимо, если бы у меня была одна запись процесс), когда гораздо предпочтительнее просто отправлять их через сокет. Я мог бы синхронизировать запись с помощью семафора, но я предпочитаю избегать проблем, если ядро ​​все равно это сделает (это вопрос моего вопроса). – lvella

+0

Я столкнулся с этим ответом, пытаясь выяснить, является ли sendto() над UDP потокобезопасным. Так это так? Если это так, здорово! – fluffy

0

Если вы пытаетесь отправить большое сообщение в сокет STREAM, превышающий размер базового буфера, в значительной степени гарантируется, что вы получите короткую запись - вызов записи или отправки будет записывать только часть данных (сколько будет вписываться в буфер), а затем вернуть записанную сумму, оставив вас сделать еще одну запись для остальных данных.

Если вы делаете это в нескольких потоках или процессах, тогда каждая запись (или отправка) будет таким образом записывать небольшую часть сообщения в буфер отправки, но последующие записи могут происходить в любом порядке, в результате чего большие отправляемые буферы будут чередоваться.

Если вы отправляете сообщения на сокеты DGRAM, с другой стороны, либо все сообщение отправляется атомарно (в виде пакета с одним слоем 4, который может быть фрагментирован и повторно собран нижними уровнями стека протокола), либо вы (EMSGSIZE linux или другие варианты UNIX)

+0

Что делать, если мы используем флаг MSG_WAITALL для записи большого сообщения? Гарантируется ли это атомарным? – FaceBro

+0

'MSG_WAITALL' не поддерживается при записи, только чтение. –

+1

Нет, он может использоваться как для чтения, так и для записи. – FaceBro