2012-06-05 31 views
5

Tengo una biblioteca de terceros que actúa como servidor HTTP. Le paso una dirección y un puerto, que luego usa para escuchar las conexiones entrantes. Esta biblioteca escucha de tal manera que no recibe el uso exclusivo del puerto y la dirección a la que está vinculado. Como resultado, puedo escuchar en el mismo puerto varias veces.¿Cómo puedo verificar si ya se está escuchando un puerto TCP?

Necesito ejecutar varias instancias de este servidor HTTP en el mismo proceso. Cada instancia tiene un puerto predeterminado, pero si ese puerto no está disponible, debe usar el siguiente puerto disponible. Aquí es donde está mi problema; Puedo terminar con dos servidores HTTP escuchando en el mismo puerto.

No puedo cambiar el código del servidor HTTP y el servidor HTTP no me alertará si no puede escuchar en el puerto que le doy, así que tengo que poder verificar si un puerto ya está en uso antes de iniciar cada servidor HTTP . He intentado comprobar si ya se está escuchando un puerto vinculando mi propio socket con SO_REUSEADDR configurado en FALSE y SO_EXCLUSIVEADDRUSE establecido en TRUE, pero las llamadas de vinculación y escucha se realizan correctamente cuando un servidor HTTP existente ya está escuchando en ese puerto.

¿Cómo está logrando este efecto este servidor HTTP, y cómo puedo verificar con precisión si un puerto se está escuchando de esta manera?

+0

¿Hay alguna razón por la que tenga que usar esta biblioteca de servidor HTTP en particular? Dado que está roto, y no tienes suficiente control sobre él o incluso acceso para descubrir qué está haciendo para que funcione, cambiar parece una muy buena idea. – abarnert

+0

Por curiosidad, ¿qué biblioteca es? –

+0

"Esta biblioteca escucha de tal manera que no recibe el uso exclusivo del puerto y la dirección a la que está vinculado". Eso no es posible en TCP. La única forma de compartir puertos que es posible en TCP es vinculante para diferentes direcciones IP locales y el mismo número de puerto. Necesita volver a investigar y luego reformular su pregunta. – EJP

Respuesta

4

El método rápido y sucio sería intentar connect() en el puerto de localhost. Si la llamada connect() tiene éxito, entonces sabrá que el puerto está siendo escuchado actualmente (por quien haya recibido la conexión). Si la llamada de conexión falla (en particular con ECONNREFUSED), entonces puede estar seguro de que nadie está escuchando en ese puerto.

Por supuesto, hay una condición de carrera aquí: nada realmente impide que otro programa se abalande y agarre el puerto inmediatamente después de ejecutar la prueba anterior, pero antes de llegar a encuadernar al puerto usted mismo. Por lo tanto, debe tomar el resultado de la prueba como una sugerencia más que como una regla absoluta, y (con suerte) tener alguna forma de manejarlo si posteriormente descubre que el puerto está en uso después de todo.

+0

Su código de prueba previsto tendrá exactamente la misma condición de carrera (o, más bien, tendría la misma condición de carrera si funcionó ...). Y realmente, no hay forma de evitarlo, dado su diseño. – abarnert

+0

(Lo anterior explica por qué te di +1 a pesar de la condición de carrera, no estoy en desacuerdo con tu respuesta ni nada de eso). – abarnert

2

Use un número de puerto de 0. El sistema operativo elegirá un puerto libre.

+0

No tengo acceso al servidor HTTP socket interno, así que si paso 0 como su puerto, no puedo recuperar el puerto que el sistema operativo le dio. –

+0

A continuación, enlace un socket usando el puerto 0, use 'getsockaddr' para descubrir el número de puerto que seleccionó para usted, inicialice un servidor HTTP usando el mismo puerto y luego cierre el socket temporal. –

1

http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx explica cómo interactúan las diferentes opciones.

No nos ha dado suficiente información para decirnos exactamente qué está pasando en su caso de uso, pero puedo trabajar en un caso de uso arbitrario que se parecería a lo que está viendo.

Digamos que está en Win 2003 o posterior, y su NIC principal es 10.0.0.1, y todo se ejecuta bajo la misma cuenta de usuario.

Aparece la primera instancia de su aplicación, y su código de prueba intenta enlazar 10.0.0.1:12345 con SO_EXCLUSIVEADDREUSE. Por supuesto, esto funciona

Cierre el socket y luego indique al servidor HTTP que escuche el puerto 12345. Vincula 0.0.0.0:12345 con SO_REUSEADDR, lo que por supuesto funciona.

Ahora aparece una segunda instancia de su aplicación, y su código de prueba intenta enlazar 10.0.0.1:12345 con SO_EXCLUSIVEADDREUSE. De acuerdo con el cuadro en el artículo de MSDN, eso funciona.

Cierre el socket, luego diga al servidor HTTP que escuche el puerto 12345. Vincula 0.0.0.0:12345 con SO_REUSEADDR, que funciona.

Si este es el problema, suponiendo que no puede conseguir que el servidor HTTP vincule una dirección específica, puede resolverlo utilizando 0.0.0.0 en su código de prueba.(Por supuesto, si es uno de los otros cientos de posibles problemas, esa solución no funcionará).

Si no sabe qué opciones de socket, dirección, etc. está utilizando el servidor HTTP, y no lo hace tener la fuente, simplemente ejecutarlo en el depurador y romper las llamadas relevantes.

Cuestiones relacionadas