Сокеты C ++ Send () Потоковая безопасность

Я кодирую сервер сокетов для 1000 клиентов maxmimum, сервер посвящен моей игре, я использую неблокирующие сокеты и около 10 потоков, которые одновременно получают данные из разных сокетов (первый поток получает от 0-100, второй от 101-200 и так далее..)

но если поток 1 хочет отправить данные всем 1000 клиентам, а поток 2 также хочет отправить данные всем 1000 клиентам одновременно, это безопасно? есть ли вероятность того, что данные будут испорчены на другой (клиентской) стороне?

Если да, я думаю, единственная проблема, которая может возникнуть, это то, что иногда клиент будет получать 2 или 10 пакетов как 1 пакет, это правильно? если да, есть ли решение этого :(


person Tenev    schedule 13.07.2010    source источник
comment
Согласен с ответом, предложенным ckv, но я предлагаю пересмотреть подход. В чем преимущество наличия 10 потоков, которые читаются из разных сокетов в разные промежутки времени? Может быть проще иметь потоки, читающие из сокетов и компонент асинхронной обработки в вашей архитектуре.   -  person Daniel H.    schedule 13.07.2010
comment
@ Дэниел, я думаю, ты имеешь в виду по одному потоку на каждый сокет. Правильно?   -  person ckv    schedule 13.07.2010
comment
Большинство потоков будут ждать, когда один из них будет читать. Использование чего-то вроде select(), вероятно, является лучшим выбором и снижает сложность. На самом деле, это могло быть даже быстрее.   -  person ereOn    schedule 13.07.2010
comment
да, ну, я использую select () и fd_set для 10 потоков, но я понятия не имею, как заставить новые 10 потоков использовать select () для отправки .. как мне запускать события в fd_set ..?   -  person Tenev    schedule 13.07.2010


Ответы (4)


Обычная схема работы со многими сокетами - это иметь специальный опрос потока для событий ввода-вывода с помощью select(2) , poll(2) или лучше _ 3_ или _ 4_ (в зависимости от платформы), действующий как диспетчер событий сокета. Сокеты обычно обрабатываются в неблокирующем режиме. Тогда у вас может быть пул потоков, реагирующих на события, которые либо выполняют чтение и запись напрямую, либо через буферы / очереди более низкого уровня.

Здесь применимы всевозможные техники - от очередей до досок подписки на мероприятия. Становится сложнее с мультиплексированием, принимает / читает / записывает / EOF на уровне ввода-вывода и с арбитражем событий на уровне приложения. Несколько библиотек, например libevent и _ 6_ поможет структурировать нижний уровень (библиотека ACE также находится в этом месте, но я бы не хотел рекомендовать ее кому-либо ). Вам придется самостоятельно создавать протоколы уровня приложений и конечные автоматы (опять же _ 7_ может помочь).

Несколько хороших ссылок, чтобы лучше понять, с чем вы сталкиваетесь (вероятно, это миллионный раз, когда они упоминаются здесь, на SO):

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

person Nikolai Fetissov    schedule 16.07.2010

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

person ckv    schedule 13.07.2010
comment
не совсем, каждый поток получает данные на разных сокетах, но он может отправлять данные на все сокеты .. - person Tenev; 13.07.2010
comment
Извините, но я не понял ваш комментарий. Не могли бы вы пояснить или отредактировать свой вопрос, чтобы сделать его более ясным. - person ckv; 13.07.2010
comment
потоки могут отправлять во все сокеты, но могут получать от определенных сокетов - person Tenev; 13.07.2010

Вы используете сокеты UDP или TCP?

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

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

person dascandy    schedule 14.07.2010
comment
TCP, синхронизировать ... я должен установить неблокирование или блокировку сокетов? если они не блокируются, думаю, я не смогу их синхронизировать, поскольку я использую Select () перед SEND (), я использую pthread_rwlock_wrlock (& ​​Players [socket] .islocked); после выбора ОТПРАВИТЬ (player [socket] .socket), наконец, разблокировать (& Players [socket] .islocked) это хорошее решение ^^? мне кажется, самый быстрый - person Tenev; 14.07.2010
comment
Под синхронизацией я имел в виду, что вам нужно вручную разделить байтовый поток на пакеты. Ваш клиент получит поток байтов независимо от того, как вы его отправите (асинхронный / неблокирующий не имеет значения). Попробуйте записать 5 байтов 10 раз подряд - скорее всего, другая сторона получит 50-байтовый блок. TCP - это поток байтов. Если вам нужны пакеты, вам придется их создавать. - person dascandy; 20.07.2010

send () не является атомарным в большинстве реализаций, поэтому отправка в 1000 разных сокетов из нескольких потоков может привести к получению смешанных сообщений на стороне клиента и всевозможным странностям. (I ничего не знаю, см. комментарии Николая и Роберта ниже, остальная часть моего комментария все еще в силе (с точки зрения решения вашей проблемы))

Я бы использовал потоки для отправки, как вы используете их для получения. Один поток для управления отправкой в ​​один (или несколько) сокетов, который гарантирует, что вы не будете писать в один сокет из нескольких потоков одновременно.

Также см. здесь дополнительное обсуждение и другие интересные ссылки. Если вы работаете в Windows, часто задаваемые вопросы о программистах winsock является бесценным ресурсом. Для решения вашей проблемы см. здесь.

person jilles de wit    schedule 13.07.2010
comment
хм, я думал об этом, я бы сделал это, если бы знал как, я использую fd_set для получения с помощью select (), но я не знаю, как запускать события в fd_set, если я использую select () для записи / отправки - person Tenev; 13.07.2010
comment
Ты уверен насчет этого? send(2) - это системный вызов, он должен быть атомарным. Поточная безопасность - это своего рода другой вопрос относительно того, сколько байтов фактически потребляет каждый send(2). - person Nikolai Fetissov; 14.07.2010
comment
@tenev, взгляните на libevent: monkey.org/~provos/libevent - значительно упрощает кодирование конечных автоматов протокола. - person Nikolai Fetissov; 14.07.2010
comment
@Nikolai, я не совсем понимаю, что вы имеете в виду, говоря "send (2)". send (...) включает буфер произвольной длины в качестве одного из своих параметров, и я думаю, что вызывающий его поток можно легко приостановить на полпути при работе с этим буфером. - person jilles de wit; 14.07.2010
comment
@tenev, если у вас есть только один поток, вызывающий send () для любого сокета (независимо от того, сколько других сокетов обрабатывает этот поток), все должно быть в порядке. - person jilles de wit; 14.07.2010
comment
@jilles de wit: -1 Извини, ты ошибаешься. Отправить - это 100% атомарная операция. Если данные слишком велики и не помещаются в выходной буфер, вызов будет заблокирован. Если вызов для отправки прерывается, данные не отправляются и возвращается ошибка. Вызовы на отправку всегда полностью завершаются успешно или полностью не выполняются. - person Robert S. Barnes; 14.07.2010
comment
@jilles, вы путаете повторный вход системного вызова и условия гонки приложений. Кстати (2) означает второй раздел руководства, то есть системные вызовы. - person Nikolai Fetissov; 14.07.2010
comment
@ Роберт, Николай, спасибо, что меня поправили. Я не знал об этом. Как бы вы ответили на исходный вопрос? - person jilles de wit; 15.07.2010
comment
Взгляните на этот отличный документ: pl.atyp.us/content/tech/servers. html Высокопроизводительная серверная архитектура. - person Nikolai Fetissov; 16.07.2010