При программировании сокетов вы создаете прослушивающий сокет, а затем для каждого подключающегося клиента вы получаете обычный потоковый сокет, который можно использовать для обработки запроса клиента. ОС незаметно управляет очередью входящих подключений.
Два процесса не могут одновременно подключаться к одному и тому же порту - в любом случае по умолчанию.
Мне интересно, есть ли способ (в любой известной ОС, особенно в Windows) запустить несколько экземпляров процесса, чтобы все они были привязаны к сокету и таким образом эффективно разделяли очередь. Тогда каждый экземпляр процесса может быть однопоточным; он просто блокируется при принятии нового соединения. Когда клиент подключился, один из экземпляров незанятого процесса примет этого клиента.
Это позволило бы каждому процессу иметь очень простую однопоточную реализацию, ничего не разделяя, кроме как через явную общую память, и пользователь мог бы регулировать пропускную способность обработки, запустив больше экземпляров.
Есть ли такая функция?
Изменить: для тех, кто спрашивает «Почему бы не использовать потоки?» Очевидно, потоки - это вариант. Но с несколькими потоками в одном процессе все объекты являются совместно используемыми, и необходимо проявлять большую осторожность, чтобы гарантировать, что объекты либо не являются общими, либо видны только одному потоку за раз, либо являются абсолютно неизменяемыми, и большинство популярных языков и средам выполнения не хватает встроенной поддержки для управления этой сложностью.
Запустив несколько идентичных рабочих процессов, вы получите параллельную систему, в которой по умолчанию не используется совместное использование, что значительно упрощает создание правильной и масштабируемой реализации.
источник
Ответы:
Вы можете использовать сокет между двумя (или более) процессами в Linux и даже Windows.
Под Linux (или ОС типа POSIX) использование
fork()
приведет к тому, что у разветвленного дочернего элемента будут копии всех дескрипторов родительских файлов. Все, что он не закрывает, будет по-прежнему использоваться совместно и (например, с сокетом прослушивания TCP) может использоваться дляaccept()
новых сокетов для клиентов. Вот сколько серверов, включая Apache в большинстве случаев, работает.В Windows то же самое в основном верно, за исключением того, что нет
fork()
системного вызова, поэтому родительский процесс должен будет использоватьCreateProcess
или что-то для создания дочернего процесса (который, конечно, может использовать тот же исполняемый файл) и должен передать ему наследуемый дескриптор.Сделать прослушивающий сокет наследуемым дескриптором - не совсем тривиальная задача, но и не слишком сложная.
DuplicateHandle()
необходимо использовать для создания дублирующего дескриптора (однако все еще в родительском процессе), для которого будет установлен наследуемый флаг. Тогда вы можете дать , что ручку вSTARTUPINFO
структуре дочернего процесса в CreateProcess , какSTDIN
,OUT
илиERR
ручка (если вы не хотите использовать его для чего - нибудь еще).РЕДАКТИРОВАТЬ:
Читая библиотеку MDSN, кажется, что
WSADuplicateSocket
это более надежный или правильный механизм для этого; это все еще нетривиально, потому что родительский / дочерний процессы должны решить, какой дескриптор должен быть продублирован каким-либо механизмом IPC (хотя это может быть так же просто, как файл в файловой системе)РАЗЪЯСНЕНИЕ:
В ответ на исходный вопрос OP: нет, несколько процессов не могут
bind()
; только первоначальный родительский процесс будет звонитьbind()
, иlisten()
т.д., дочерние процессы будут только обрабатывать запросы отaccept()
,send()
, иrecv()
т.д.источник
Большинство других предоставили технические причины, по которым это работает. Вот код Python, который вы можете запустить, чтобы продемонстрировать это самому:
import socket import os def main(): serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serversocket.bind(("127.0.0.1", 8888)) serversocket.listen(0) # Child Process if os.fork() == 0: accept_conn("child", serversocket) accept_conn("parent", serversocket) def accept_conn(message, s): while True: c, addr = s.accept() print 'Got connection from in %s' % message c.send('Thank you for your connecting to %s\n' % message) c.close() if __name__ == "__main__": main()
Обратите внимание, что на самом деле прослушиваются два идентификатора процесса:
Вот результаты запуска telnet и программы:
источник
Я хотел бы добавить, что сокеты могут совместно использоваться в Unix / Linux через сокеты AF__UNIX (межпроцессные сокеты). Похоже, что происходит создание нового дескриптора сокета, который является своего рода псевдонимом исходного. Этот новый дескриптор сокета отправляется через сокет AFUNIX другому процессу. Это особенно полезно в случаях, когда процесс не может fork () поделиться своими файловыми дескрипторами. Например, при использовании библиотек, предотвращающих это из-за проблем с потоками. Вы должны создать сокет домена Unix и использовать libancillary для отправки дескриптора.
Видеть:
Для создания сокетов AF_UNIX:
Например код:
источник
Похоже, на этот вопрос уже был дан полный ответ от MarkR и zackthehack, но я хотел бы добавить, что Nginx является примером модели наследования прослушивающих сокетов.
Вот хорошее описание:
источник
Не уверен, насколько это соответствует исходному вопросу, но в ядре Linux 3.9 есть патч, добавляющий функцию TCP / UDP: поддержка TCP и UDP для параметра сокета SO_REUSEPORT; Новая опция сокета позволяет нескольким сокетам на одном хосте связываться с одним и тем же портом и предназначена для повышения производительности приложений многопоточного сетевого сервера, работающих поверх многоядерных систем. дополнительную информацию можно найти в ссылке LWN LWN SO_REUSEPORT в Linux Kernel 3.9, как указано в ссылке ссылки:
опция SO_REUSEPORT нестандартна, но доступна в аналогичной форме в ряде других систем UNIX (в частности, в BSD, где возникла эта идея). Кажется, это полезная альтернатива для выжать максимальную производительность из сетевых приложений, работающих на многоядерных системах, без использования шаблона вилки.
источник
SO_REUSEPORT
создание пула потоков, где каждый сокет находится в другом потоке, но только один сокет в группе выполняетaccept
. Можете ли вы подтвердить, что все сокеты в группе получают копию данных?Начиная с Linux 3.9, вы можете установить SO_REUSEPORT для сокета, а затем использовать этот сокет для нескольких несвязанных процессов. Это проще, чем схема prefork, больше никаких проблем с сигналом, утечки fd в дочерние процессы и т. Д.
Linux 3.9 представил новый способ написания серверов сокетов
Параметр сокета SO_REUSEPORT
источник
Имейте единственную задачу, единственная задача которой - прослушивать входящие соединения. Когда соединение получено, оно принимает соединение - это создает отдельный дескриптор сокета. Принятый сокет передается одной из ваших доступных рабочих задач, а основная задача возвращается к прослушиванию.
источник
В Windows (и Linux) один процесс может открыть сокет, а затем передать этот сокет другому процессу, чтобы второй процесс также мог использовать этот сокет (и передавать его по очереди, если он захочет) .
Ключевым вызовом функции является WSADuplicateSocket ().
Это заполняет структуру информацией о существующем сокете. Затем эта структура через выбранный вами IPC-механизм передается другому существующему процессу (обратите внимание, что я говорю «существующий» - когда вы вызываете WSADuplicateSocket (), вы должны указать целевой процесс, который получит переданную информацию).
Затем принимающий процесс может вызвать WSASocket (), передав эту структуру информации, и получить дескриптор базового сокета.
Оба процесса теперь хранят дескриптор одного и того же базового сокета.
источник
Похоже, вы хотите, чтобы один процесс слушал новых клиентов, а затем передал соединение, как только вы его установили. Сделать это через потоки легко, и в .Net у вас даже есть методы BeginAccept и т. Д., Которые позаботятся о многих сантехнических работах за вас. Передача соединений через границы процесса будет сложной задачей и не даст никаких преимуществ в производительности.
В качестве альтернативы вы можете связать несколько процессов и прослушивать один и тот же сокет.
Если вы запустите два процесса, каждый из которых выполняет указанный выше код, он будет работать, и первый процесс, похоже, получит все соединения. Если первый процесс убит, второй получит соединения. При таком совместном использовании сокетов я не совсем уверен, как Windows решает, какой процесс получает новые подключения, хотя быстрый тест действительно указывает на то, что самый старый процесс получает их первым. Что касается того, будет ли он делиться, если первый процесс занят или что-то в этом роде, я не знаю.
источник
Другой подход (который позволяет избежать многих сложных деталей) в Windows, если вы используете HTTP, - это использовать HTTP.SYS . Это позволяет нескольким процессам прослушивать разные URL-адреса на одном и том же порту. На Server 2003/2008 / Vista / 7 IIS работает так, поэтому вы можете использовать его порты. (В XP SP2 HTTP.SYS поддерживается, но IIS5.1 не использует его.)
Другие API высокого уровня (включая WCF) используют HTTP.SYS.
источник