¿Puede el servidor usar el mismo socket para enviar la respuesta al cliente? ¿cómo?

StackOverflow https://stackoverflow.com/questions/467396

  •  19-08-2019
  •  | 
  •  

Pregunta

Estoy usando los sockets Berkeley (ambos: dominio de Internet y dominio Unix) y me preguntaba si el servidor puede usar los mismos sockets para leer la solicitud y escribir una respuesta al cliente. O si el cliente crea otro socket para esperar la reproducción y el servidor se conecta a él después de procesar el mensaje recibido.

Por cierto, estoy hablando de sockets orientados a la conexión (sockets de flujo, TCP, ...).

Este es el código de servidor simplificado (omito la comprobación de errores en las llamadas del sistema aquí solo por simplicidad):

int main() {

    int server_socket, connected_socket;
    struct sockaddr_in server_addr;
    char buf[1024];
    char aux[256];
    int bytes_read;

    server_socket = socket(AF_INET, SOCK_STREAM, 0);    

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(1234);
    bind(server_socket, &server_addr, sizeof(server_addr))

    listen(server_socket, 5)

    connected_sodket = accept(server_socket, 0, 0);
    do {
        bzero(buf, sizeof(buf));
        bytes_read = read(connected_socket, buf, sizeof(buf));        
    } while (bytes_read > 0);          

    /* Here I want to use connected_socket to write the reply, can I? */

    close(connected_socket);       

    close(server_socket);

    return (EXIT_SUCCESS);
}

Y este es el código de cliente simplificado (omito la comprobación de errores en las llamadas del sistema aquí solo por simplicidad):

int main() {

    int client_socket;
    struct sockaddr_in server_addr;

    client_socket = socket(AF_INET, SOCK_STREAM, 0);

    hp = gethostbyname("myhost");
    server_addr.sin_family = AF_INET;
    memcpy(&server_addr.sin_addr, hp->h_addr_list[0], hp->h_length);
    server_addr.sin_port = htons(1234);

    connect(client_socket, &server_addr, sizeof(server_addr));

    write(client_socket, MSG, sizeof(MSG));

    /* Here I want to wait for a response from the server using client_socket, can I? */

    close(client_socket);

    return (EXIT_SUCCESS);
}

¿Puedo usar connected_socket en el servidor y client_socket en el cliente para devolver un mensaje de respuesta? ¿O debería usar la dirección del cliente que obtengo en el servidor cuando estoy en " aceptar " conectarse a un zócalo en el cliente?

Lo intenté usando read / wrint en el cliente / servidor donde se muestra el comentario, pero de esa manera ambos programas se mantienen bloqueados, parece ser un punto muerto.

¡Gracias por adelantado! Saludos.

¿Fue útil?

Solución

Puede usar el mismo socket PERO su programa está configurado para que el servidor lea TODO lo que el cliente envía antes de intentar responder. Por lo tanto, el bucle en el servidor no se completará hasta que el cliente cierre el lado de escritura de su socket para que el servidor obtenga un EOF (lectura de 0 bytes) y, por lo tanto, el servidor nunca enviará su respuesta.

Hay un par de formas en que puede lidiar con esto.

  1. Puede romper el bucle en el servidor después de haber visto toda la solicitud, en lugar de leer hasta EOF. Esto requiere que los datos enviados por el cliente se autodefinan de alguna manera, para que el servidor pueda saber cuándo lo lee todo.
  2. Puede usar una segunda conexión para la respuesta. Probablemente no sea el mejor.
  3. Puede usar el apagado asimétrico del zócalo. Haga que el cliente cierre shutdown (client_socket, SHUT_WR) para cerrar la mitad del socket. El servidor verá el EOF (y el bucle finalizará), pero la otra dirección en el socket seguirá abierta para la respuesta.

Otros consejos

¡Usted debería usar el mismo zócalo!

El protocolo de su aplicación define inequívocamente cuándo el cliente y el servidor deben esperar datos o enviarse mensajes entre ellos; suponiendo un protocolo con solo una solicitud del cliente y una respuesta del servidor, se debe cumplir lo siguiente:


  • El cliente establece una conexión con el servidor;
  • el cliente envía su solicitud (con send ());
  • el cliente sabe , en virtud del protocolo, que el servidor responderá; por lo tanto, espera datos en el mismo socket (recv ());
  • después de validar la respuesta, el cliente puede cerrar el socket.

  • El servidor acepta una conexión del cliente;
  • el servidor sabe que el primer paso depende del cliente, por lo tanto, espera datos (recv ());
  • el servidor valida la solicitud;
  • el servidor ahora sabe , desde el protocolo, que el cliente está esperando datos; por lo tanto, envía su respuesta con send ();
  • el servidor sabe , desde el protocolo, que no hay más pasos; por lo tanto, puede cerrar el zócalo.

Sí, es posible. Mira echa un vistazo a esta página para ver un ejemplo de un servidor simple (y un cliente simple). Tenga en cuenta que el servidor generalmente pasa el descriptor de archivo "aceptar" en un nuevo proceso para que pueda seguir escuchando más conexiones entrantes

No solo debe usar el mismo socket (como dice Federico), sino que también tiene que hacerlo para obtener sus paquetes de devolución a través de firewalls.

Los firewalls conocen las conexiones TCP, y automáticamente permiten que los datos de retorno pasen si una máquina dentro del firewall inició la conexión. Si, en cambio, intentara crear un nuevo socket TCP desde el exterior, el firewall lo bloquearía a menos que ese tráfico se permitiera específicamente.

Sí, los sockets SOCK_STREAM son bidireccionales. Debería poder leer y escribir en / desde el mismo socket en cada lado de la conexión. La página del manual para socket (2) tiene más detalles sobre esto.

Sí, puedes. Los zócalos TCP son bidireccionales. Simplemente use las mismas funciones read () y write (). También asegúrese de verificar las condiciones de error en todas las llamadas a connect (), read (), write (), ... ya que no puede controlar lo que sucede en la red.

Lo siento; No lo dije pero lo intenté así

Este código en el servidor donde está el comentario:

write(connected_socket, "Ok, I got it", sizeof("Ok, I got it"));

y este código en el cliente donde está el comentario:

read(client_socket, buf, sizeof(buf));

Ambos programas se mantienen bloqueados y cuando mato al cliente, el servidor muestra los mensajes que recibió (tengo un printf justo después de que el servidor llama a leer ).

Lo intento con send y recv (ambos con 0 marcas) en lugar de leer y escribir y no cambió.

En su configuración actual, el servidor intenta leer hasta que el cliente cierra el socket, mientras que el cliente no cierra el socket hasta que el servidor responde. Por lo tanto, tiene una especie de "punto muerto". El servidor no se detiene para leer con la esperanza de que lleguen más datos y el cliente no tiene forma de decirle al servidor que está listo.

Necesita alguna forma para que el servidor reconozca que la solicitud está completa, mientras la conexión aún está abierta. Si, por ejemplo, finaliza su solicitud con una nueva línea, el servidor puede verificar si recibió una línea completa y luego dejar de leer y enviar la respuesta.

¿Lo has probado? Parece que debería funcionar cuando realmente pones el código en el lugar indicado

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top