Допустимы ли параллельные вызовы send / recv на одном и том же сокете?

127
  1. Можем ли мы вызвать send из одного потока и recv из другого в том же сокете?
  2. Можно ли вызывать несколько отправок одновременно из разных потоков в одном сокете?

Я знаю, что хороший дизайн должен избегать этого, но я не понимаю, как эти системные API-интерфейсы будут себя вести. Я не могу найти хорошую документацию для того же самого.

Любые указатели направления будут полезны.

сойка
источник
3
почему вы утверждаете, что это плохая практика? Мне это кажется прекрасным, потому что вы слушаете и получаете сообщения в разных потоках.
TheMathNoob 02

Ответы:

92

POSIX определяет send / recv как атомарные операции, поэтому, если вы говорите о POSIX send / recv, тогда да, вы можете вызывать их одновременно из нескольких потоков, и все будет работать.

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

Если вы используете сокеты SOCK_STREAM, попытка делать что-то параллельное вряд ли будет полезно, поскольку send / recv может отправлять или получать только часть сообщения, что означает, что все может разделиться.

Блокирование send / recv на сокетах SOCK_STREAM блокируется только до тех пор, пока они не отправят или не получат хотя бы 1 байт, поэтому разница между блокировкой и неблокировкой бесполезна.

Крис Додд
источник
1
@Joao: сокет SOCK_DGRAM задокументирован как «сохраняющий границы сообщения», что не очень понятно. Глядя на исходники ядра Linux, вы можете по крайней мере увидеть, что каждый send и recv обрабатывает один пакет атомарно (по крайней мере, для udp).
Крис Додд
2
@ Кедар: не понимаю, о чем ты. A sendвозвращается, как только данные помещаются в буфер отправки, и данные отправляются через стек netowrk и выводятся в сеть асинхронно. Таким образом, если у вас есть один поток, отправляющий и один поток, принимающий, вполне возможно (даже вероятно) для отправляющего потока отправить много пакетов до того, как получающий поток получит первый пакет. Это полностью асинхронно и не одновременно.
Крис Додд
6
@ChrisDodd, не могли бы вы дать ссылку на "POSIX определяет send / recv как атомарные операции"?
суитянши
2
@suitianshi: В стандартном документе POSIX 1003.1c перечислены все функции в 1003.1, которые являются реентерабельными (безопасными для вызова из потоков), а какие нет. К сожалению, я не знаю, чтобы где-либо была доступна бесплатная онлайн-копия.
Крис Додд
2
@ChrisDodd Я нашел копию на unix-systems.org/version4, и я вижу список таблиц системных интерфейсов в главе 7.1, но не вижу, где они перечисляют функции как атомарные операции. Не сомневаюсь в вас, но не могли бы вы поделиться / отредактировать свой ответ, чтобы обосновать свою точку зрения в документе?
user153882 05
17

Дескриптор сокета принадлежит процессу, а не конкретному потоку. Следовательно, можно отправлять / получать в / из одного и того же сокета в разных потоках, ОС будет обрабатывать синхронизацию.

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

Адриан Вилленбюхер
источник
4

Я не понимаю, как параллельное получение может чего-либо достичь. Если у вас есть 3-байтовое сообщение, один поток может получить первые 2 байта, а другой - последний, но у вас не будет возможности определить, какой из них был каким. Если ваши сообщения не имеют длины всего байта, вы не сможете надежно заставить что-либо работать с получением нескольких потоков.

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

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

Ной
источник
9
Если вы используете сокеты SOCK_DGRAM, каждый recv получит одну дейтаграмму; он никогда не будет разделен между приемами
Крис Додд
2
@noah, я согласен, что параллельные recvs ничего не могут сделать. Вот почему я не спросил об этом. Мой вопрос: отправка / получение одновременно, а затем несколько отправок одновременно. Ваш ответ дает представление о параллельных отправках. Спасибо за то же самое.
Джей
1
@ Крис хороший замечание. Я предполагал TCP. @Jay Вы могли бы прояснить вопрос «Можем ли мы вызывать отправку / получение параллельно» звучит так, как будто вы хотите получать параллельно.
Ной