En la programación de sockets, creas un socket de escucha y luego, para cada cliente que se conecta, obtienes un socket de flujo normal que puedes usar para manejar la solicitud del cliente. El sistema operativo gestiona la cola de conexiones entrantes detrás de escena.
Dos procesos no se pueden vincular al mismo puerto al mismo tiempo, por defecto, de todos modos.
Me pregunto si hay una manera (en cualquier sistema operativo conocido, especialmente Windows) de iniciar múltiples instancias de un proceso, de modo que todas se unan al socket y, por lo tanto, compartan efectivamente la cola. Cada instancia de proceso podría ser de un solo subproceso; simplemente se bloquearía al aceptar una nueva conexión. Cuando un cliente se conecta, una de las instancias de proceso inactivas acepta a ese cliente.
Esto permitiría que cada proceso tuviera una implementación de un solo subproceso muy simple, sin compartir nada a menos que a través de una memoria compartida explícita, y el usuario podría ajustar el ancho de banda de procesamiento iniciando más instancias.
¿Existe tal característica?
Editar: Para aquellos que preguntan "¿Por qué no usar hilos?" Obviamente, los hilos son una opción. Pero con varios subprocesos en un solo proceso, todos los objetos se pueden compartir y se debe tener mucho cuidado para garantizar que los objetos no se compartan o que solo sean visibles para un subproceso a la vez, o que sean absolutamente inmutables, y la mayoría de los lenguajes y lenguajes populares los tiempos de ejecución carecen de soporte integrado para gestionar esta complejidad.
Al iniciar un puñado de procesos de trabajo idénticos, obtendría un sistema concurrente en el que el valor predeterminado es no compartir, lo que facilita mucho la creación de una implementación correcta y escalable.
fuente
Respuestas:
Puede compartir un socket entre dos (o más) procesos en Linux e incluso Windows.
En Linux (o SO tipo POSIX), el uso
fork()
hará que el hijo bifurcado tenga copias de todos los descriptores de archivo del padre. Cualquiera que no se cierre continuará compartiéndose y (por ejemplo, con un socket de escucha TCP) se puede utilizar paraaccept()
nuevos sockets para clientes. Así es como funcionan muchos servidores, incluido Apache en la mayoría de los casos.En Windows, lo mismo es básicamente cierto, excepto que no hay una
fork()
llamada al sistema, por lo que el proceso principal deberá usarCreateProcess
o algo para crear un proceso secundario (que, por supuesto, puede usar el mismo ejecutable) y debe pasarle un identificador heredable.Convertir un socket de escucha en un identificador heredable no es una actividad completamente trivial, pero tampoco demasiado complicada.
DuplicateHandle()
debe usarse para crear un identificador duplicado (sin embargo, aún en el proceso principal), que tendrá el indicador heredable establecido en él. A continuación, puede dar esa mango en laSTARTUPINFO
estructura para el proceso hijo en CreateProcess comoSTDIN
,OUT
oERR
mango (asumiendo que no quiere usarlo para otra cosa).EDITAR:
Al leer la biblioteca MDSN, parece que
WSADuplicateSocket
es un mecanismo más sólido o correcto para hacer esto; todavía no es trivial porque los procesos padre / hijo necesitan determinar qué identificador debe ser duplicado por algún mecanismo de IPC (aunque esto podría ser tan simple como un archivo en el sistema de archivos)ACLARACIÓN:
En respuesta a la pregunta original del OP, no, múltiples procesos no pueden
bind()
; sólo el proceso padre original se llamebind()
,listen()
etc, los procesos hijo se acaba de procesar las solicitudes deaccept()
,send()
,recv()
etc.fuente
La mayoría de los demás han proporcionado las razones técnicas por las que esto funciona. Aquí hay un código de Python que puede ejecutar para demostrarlo usted mismo:
Tenga en cuenta que, de hecho, hay dos ID de proceso escuchando:
Estos son los resultados de ejecutar telnet y el programa:
fuente
Me gustaría agregar que los sockets se pueden compartir en Unix / Linux a través de sockets AF__UNIX (sockets entre procesos). Lo que parece suceder es que se crea un nuevo descriptor de socket que es algo así como un alias del original. Este nuevo descriptor de socket se envía a través del socket AFUNIX al otro proceso. Esto es especialmente útil en los casos en que un proceso no puede bifurcar () para compartir sus descriptores de archivo. Por ejemplo, cuando se utilizan bibliotecas que evitan esto debido a problemas de subprocesos. Debe crear un socket de dominio Unix y usar libancillary para enviar el descriptor.
Ver:
Para crear sockets AF_UNIX:
Por ejemplo código:
fuente
Parece que MarkR y zackthehack ya han respondido completamente esta pregunta, pero me gustaría agregar que Nginx es un ejemplo del modelo de herencia de socket de escucha.
Aquí hay una buena descripción:
fuente
No estoy seguro de cuán relevante es esto para la pregunta original, pero en el kernel 3.9 de Linux hay un parche que agrega una característica TCP / UDP: compatibilidad con TCP y UDP para la opción de socket SO_REUSEPORT; La nueva opción de socket permite que varios sockets en el mismo host se unan al mismo puerto y está destinada a mejorar el rendimiento de las aplicaciones de servidor de red multiproceso que se ejecutan sobre sistemas multinúcleo. se puede encontrar más información en el enlace LWN LWN SO_REUSEPORT en Linux Kernel 3.9 como se menciona en el enlace de referencia:
la opción SO_REUSEPORT no es estándar, pero está disponible en una forma similar en varios otros sistemas UNIX (en particular, los BSD, donde se originó la idea). Parece ofrecer una alternativa útil para obtener el máximo rendimiento de las aplicaciones de red que se ejecutan en sistemas multinúcleo, sin tener que utilizar el patrón de bifurcación.
fuente
SO_REUSEPORT
crea un grupo de subprocesos, donde cada socket está en un subproceso diferente pero solo un socket en el grupo realiza elaccept
. ¿Puede confirmar que todos los sockets del grupo obtienen una copia de los datos?A partir de Linux 3.9, puede configurar SO_REUSEPORT en un socket y luego hacer que varios procesos no relacionados compartan ese socket. Eso es más simple que el esquema prefork, no más problemas de señal, fd fd a procesos secundarios, etc.
Linux 3.9 introdujo una nueva forma de escribir servidores de socket
La opción de socket SO_REUSEPORT
fuente
Tenga una única tarea cuyo único trabajo sea escuchar las conexiones entrantes. Cuando se recibe una conexión, acepta la conexión; esto crea un descriptor de socket separado. El socket aceptado se pasa a una de sus tareas de trabajador disponibles y la tarea principal vuelve a escuchar.
fuente
En Windows (y Linux) es posible que un proceso abra un socket y luego pase ese socket a otro proceso de modo que ese segundo proceso también pueda usar ese socket (y pasarlo a su vez, si así lo desea) .
La llamada de función crucial es WSADuplicateSocket ().
Esto llena una estructura con información sobre un socket existente. Luego, esta estructura, a través de un mecanismo IPC de su elección, se pasa a otro proceso existente (tenga en cuenta que digo existente: cuando llama a WSADuplicateSocket (), debe indicar el proceso de destino que recibirá la información emitida).
El proceso de recepción puede llamar a WSASocket (), pasar esta estructura de información y recibir un identificador al socket subyacente.
Ambos procesos ahora mantienen un identificador en el mismo socket subyacente.
fuente
Parece que lo que desea es un proceso que escuche a los nuevos clientes y luego entregue la conexión una vez que obtenga una conexión. Hacer eso a través de subprocesos es fácil y en .Net incluso tiene los métodos BeginAccept, etc. para encargarse de gran parte de la plomería por usted. Transferir las conexiones a través de los límites del proceso sería complicado y no tendría ninguna ventaja de rendimiento.
Alternativamente, puede tener varios procesos enlazados y escuchando en el mismo socket.
Si inicia dos procesos, cada uno ejecutando el código anterior, funcionará y el primer proceso parece obtener todas las conexiones. Si se mata el primer proceso, el segundo obtiene las conexiones. Con el uso compartido de sockets como ese, no estoy seguro de cómo Windows decide exactamente qué proceso obtiene nuevas conexiones, aunque la prueba rápida apunta al proceso más antiguo que las obtiene primero. En cuanto a si comparte si el primer proceso está ocupado o algo así, no lo sé.
fuente
Otro enfoque (que evita muchos detalles complejos) en Windows si está usando HTTP, es usar HTTP.SYS . Esto permite que varios procesos escuchen diferentes URL en el mismo puerto. En Server 2003/2008 / Vista / 7, así es como funciona IIS, por lo que puede compartir puertos con él. (En XP SP2, HTTP.SYS es compatible, pero IIS5.1 no lo usa).
Otras API de alto nivel (incluido WCF) utilizan HTTP.SYS.
fuente