Foros del Web » Programación para mayores de 30 ;) » C/C++ »

Servidor C++

Estas en el tema de Servidor C++ en el foro de C/C++ en Foros del Web. Hola! Me llamo María. Tengo una duda un tanto básica, creo, que me está bloqueando un poco en mi trabajo. Tengo un programa que es ...
  #1 (permalink)  
Antiguo 20/06/2011, 05:45
 
Fecha de Ingreso: junio-2011
Ubicación: Saarbrücken
Mensajes: 9
Antigüedad: 12 años, 10 meses
Puntos: 0
Pregunta Servidor C++

Hola!
Me llamo María.
Tengo una duda un tanto básica, creo, que me está bloqueando un poco en mi trabajo.

Tengo un programa que es un servidor inicialmente, en un momento dado, pasa a comportarse como cliente, y luego quiero que vuelva a comportarse como un servidor.
El primer cambio (servidor->cliente) lo hago sin problemas, pero cuando quiero volver a servidor, obtengo un error de "bind".

Lo que hago para volver a configurarme como servidor es cerrar el socket que tenía abierto en modo cliente, y después vuelvo a hacer un bind pero TIENE QUE SER a la misma IP y puerto.
Imagino que el problema está ahí, que no puedo volver a hacer un bind al mismo puerto. Pero, si yo cerré el socket cuando cambié la primera vez de servidor a cliente, por qué no puedo volver a utilizar la misma configuración?

Espero que alguien pueda ayudarme

Muchas gracias!!!
  #2 (permalink)  
Antiguo 20/06/2011, 10:05
 
Fecha de Ingreso: abril-2010
Ubicación: Rosario
Mensajes: 1.850
Antigüedad: 14 años
Puntos: 228
Respuesta: Servidor C++

Cuando haces el accept haces algo asi:

newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);

Luego cierras la conexion de newsockfd...pero posiblmente el socket "sockfd" sigue escuchando por conexiones.

Esa es una sugerencia podrias mostrar un poco de codigo para ver lo que tienes.
  #3 (permalink)  
Antiguo 21/06/2011, 02:24
 
Fecha de Ingreso: junio-2011
Ubicación: Saarbrücken
Mensajes: 9
Antigüedad: 12 años, 10 meses
Puntos: 0
Respuesta: Servidor C++

Tienes razón, pero no he pegado el código porque es algo complejo. Estoy programando un microcontrolador y aunque lo hago en C++, tengo que ceñirme a unas librerías concretas.
En realidad, las funciones son las mismas, con la salvedad de que tengo estar haciendo un "poll" permantente al socket para observar que eventos tienen lugar, y gestinonarlos.

En el main tengo lo siguiente:
Código C++:
Ver original
  1. [CODE]
  2. int main() {
  3.  
  4.     pc.format(8, Serial::None, 1);
  5.     pc.baud(9600);
  6.     EthernetErr NetErr = NetIf.setup();
  7.     if (NetErr) {
  8.         pc.printf("\r\nERROR SETTING UP EHTERNETNETIF...\r\n");
  9.         return 0;
  10.     }
  11.     socket_event();
  12.  
  13.     while (true) { //Necessary to work!.
  14.         Net::poll();
  15.  
  16.     }
  17. }[/CODE]

Estoy haciendo un "poll" continuo de la red, además de llamar a la función "socket_event" que es la siguiente:

Código:


Código C++:
Ver original
  1. //////////////////////////////////////////////
  2. /////Function to create a listener socket/////
  3. //////////////////////////////////////////////
  4.  
  5. int socket_event() { //function to open an event socket
  6.     socket.resetOnEvent();
  7.     socket.setOnEvent(&onTCPSocketEvent_Accept);
  8.     TCPSocketErr err;
  9.     err = socket.bind(Host(IpAddr(134,96,214,36), 10001); //PC IP + PORT ; this socket keeps open listening for another connections
  10.     if (err) {
  11.         pc.printf("\r\nERROR BINDING: %s", err, "/r/n");
  12.         return 0;
  13.     }
  14.     err = socket.listen(); //Starts listening
  15.     if (err) {
  16.         pc.printf("\r\nERROR LISTENING...\r\n");
  17.         return 0;
  18.     }
  19.     pc.printf("\r\nLISTENING...\r\n");
  20.     server_mode=true;
  21.     client_mode=false;
  22.     return 0;
  23. }
Aquí, hago el bind, el listen y llamo a la función que captará el evento "ACCEPT", "onTCPSocketEvent_Accept"

Código C++:
Ver original
  1. [CODE]
  2. ///////////////////////////////////////////
  3. //// Function to handle "ACCEPT" event ////
  4. ///////////////////////////////////////////
  5.  
  6. void onTCPSocketEvent_Accept(TCPSocketEvent e) {
  7.     if ( e == TCPSOCKET_ACCEPT ) {
  8.         pClientSock = new TCPSocket();
  9.         Host Client;
  10.         TCPSocketErr err = socket.accept(&Client, &pClientSock);
  11.         if (err) {
  12.             pc.printf("\r\nERROR ACCEPTING CLIENT...\n\r");//Could not accept client
  13.             return;
  14.         }
  15.  
  16.         pClientSock->setOnEvent(&onTCPSocketEvent); //Setup the new socket events
  17.        
  18.     }
  19. }
  20. [/CODE]

Aquí también abro el socket para el intercambio de información con el cliente (pClientSock) y llamo a la funcion que gestionará los eventos en ese socket , "onTCPSocketEvent":
Código C++:
Ver original
  1. [CODE]////////////////////////////////////////////
  2. //////Function to catch Socket´s Events/////
  3. ////////////////////////////////////////////
  4.  
  5. void onTCPSocketEvent(TCPSocketEvent e) {
  6.     pc.printf("\r\nEvent: %d", e);
  7.     switch (e) {
  8.  
  9.         case TCPSOCKET_CONNECTED: {
  10.             pc.printf("\r\nCONNECTION ESTABLISHED...\r\n");
  11.           int long_sent =socket.send("Tag received", sizeof("Tag received"));
  12.         }
  13.         break;
  14.  
  15.         case TCPSOCKET_WRITEABLE: {
  16.             pc.printf("\n\rCan now write some data\r\n");
  17.            
  18.         }
  19.         break;
  20.  
  21.         case TCPSOCKET_CONRST: {
  22.             pc.printf("\r\nERROR... CONNECTION RESETED...\r\n");
  23.             if (end_NM_config==true) {
  24.                 pc.printf ("voy a cambiar a cliente en CONRST\r\n");
  25.                 socket.close();
  26.                 pClientSock->close();
  27.                 socket_communication();
  28.                 end_NM_config=false;
  29.             } else if (change_mode) {
  30.                 pc.printf(" voy a cambiar a server en el CONRST\r\n");
  31.                 socket.close();
  32.                 socket_event();
  33.                 change_mode=false;
  34.             }
  35.         }
  36.         break;
  37.  
  38.         case TCPSOCKET_CONTIMEOUT:
  39.         case TCPSOCKET_CONABRT: {
  40.             socket.close();
  41.             pClientSock->close();
  42.             socket_communication();
  43.         }
  44.         break;
  45.         case TCPSOCKET_ERROR: {
  46.  
  47.             pc.printf("\r\nERROR...\r\nCLOSING SOCKET\r\n");
  48.             socket.close();
  49.         }
  50.         break;
  51.         case TCPSOCKET_READABLE: {
  52.             pc.printf("\n\rCan now read some data\r\n");
  53.             if (server_mode) {
  54.                 //Receive command
  55.                 char command[255];
  56.                 int long_command= pClientSock->recv(command, 255);
  57.                 pc.printf("Recibo en el READABLE server: ");
  58.                 for (int i=0;i<long_command;i++) {
  59.                     pc.printf ("%c",command[i]);
  60.  
  61.                 }
  62.                 pc.printf ("\r\n");
  63.                 pc.printf ("\r\nBytes recibidos en el READABLE server: %d", long_command, "\r\n");
  64.                 pc.printf ("\r\nPrimer caracter: %c", command[0], "\r\n");
  65.  
  66.                 if (command[0]=='E') {
  67.                     pc.printf ("\r\nend_NM_config a true\r\n");
  68.                     end_NM_config=true;
  69.                    
  70.                     break;
  71.                 }
  72.  
  73.                 //Process command received
  74.                 pc.printf("Long Received: %d\r\n", long_command);
  75.                 for (int i=0; i<long_command;i++) {
  76.                     pc.printf ("Command Received from PC: %X\n\r",command[i]);
  77.                 }
  78.  
  79.  
  80.                 // simulation of the response of the reader
  81.                 int long_sent=pClientSock->send("Command received OK", sizeof("Command received OK"));
  82.                 pc.printf ("Long sent: %d\r\n", long_sent);
  83.             } else if (client_mode) {
  84.                 char command[255];
  85.                 int long_command= socket.recv(command, 255); //receiving the ACK of the tag
  86.  
  87.                 if (command[0]=='T') {
  88.                  
  89.                     pc.printf ("Va a empezar a enviar tags\r\n");
  90.  
  91.                     int long_sent=socket.send("Tag received", sizeof("Tag received"));
  92.  
  93.  
  94.                 } else if (command[0]=='C') {
  95.  
  96.                     int long_sent=socket.send("Change intention received", sizeof("Change intention received"));
  97.                     change_mode=true;
  98.                    
  99.                     pc.printf("Change mode igual a true\r\n");
  100.                 }
  101.             }
  102.         }
  103.         break;
  104.  
  105.         case TCPSOCKET_DISCONNECTED: {
  106.             pc.printf("\r\nDISCONNECTED...\n\rCLOSING SOCKET\r\n");
  107.             if (end_NM_config==true) {
  108.                 pc.printf("voy a cambiar a cliente en DISCONNECTED\r\n");
  109.                 socket.close();
  110.                 pClientSock->close();
  111.              
  112.                 socket_communication();
  113.                 end_NM_config=false;
  114.             } else if (change_mode) {
  115.                 pc.printf ("voy a cambiar a servidor en el DISCONNECT\r\n");
  116.                 socket.close();
  117.                 socket_event();
  118.                 change_mode=false;
  119.             }
  120.         }
  121.         break;
  122.  
  123.         default: {
  124.             pc.printf("\r\nUNKNOWN EVENT...\r\nCLOSING SOCKET\r\n");
  125.             socket.close();
  126.         }
  127.         break;
  128.     }
  129. }[/CODE]


En los eventos "DISCONNECTED" y "CONRST" es donde realizo el cambio de servidor a cliente y de cliente a servidor.
en el primer caso, cierro socket y pClientSock y llamo a la función "socket_communication" que abre un socket y lo conecta al servidor remoto:

Código C++:
Ver original
  1. [CODE]////////////////////////////////////////////////////////
  2. ///////Function to open a communication socket//////////
  3. ////////////////////////////////////////////////////////
  4.  
  5. int socket_communication() {
  6.     socket.resetOnEvent();
  7.     socket.setOnEvent(&onTCPSocketEvent);
  8.     Host server(IpAddr(134, 96, 214, 35),20001); //READER IP + PORT
  9.     TCPSocketErr bindErr = socket.connect(server);
  10.     if (bindErr) {
  11.         pc.printf("\r\nERROR BINDING server...\r\n");
  12.         return 0;
  13.     }
  14.     pc.printf("\r\nCONNECTING TO %d.%d.%d.%d \n\r", server.getIp()[0], server.getIp()[1], server.getIp()[2], server.getIp()[3]);
  15.     server_mode=false;
  16.     client_mode=true;
  17.     return 0;
  18. }[/CODE]


En el segundo caso, cierro el socket que se utiliza en modo cliente y vuelvo a llamar a "socket_server", donde vuelvo a hacer el bind con la misma IP y puerto.

Sé que es un lio todo este código, pero el problema lo tengo cuando hago el close.socket();
socket_event();
Es como si el close.socket() no cerrase del todo la conexión, o como si , cuando cerré por primera vez los sockets en modo server, se hubiese dejado rastro que no me permite volver a hacer el bind con la misma IP y puerto.
Estoy casi segura de que es lo que dices tú, que aunque cierre la conexión, el socket sigue escuchando, pero cómo puedo hacer para que pare?
Espero que se entienda algo de mi problema
Gracias!

Última edición por mariaamo; 21/06/2011 a las 03:21
  #4 (permalink)  
Antiguo 21/07/2011, 04:40
 
Fecha de Ingreso: junio-2011
Ubicación: Saarbrücken
Mensajes: 9
Antigüedad: 12 años, 10 meses
Puntos: 0
Respuesta: Servidor C++

Nadie tiene alguna idea??
Estoy un poco bloqueada...
  #5 (permalink)  
Antiguo 21/07/2011, 08:39
 
Fecha de Ingreso: junio-2008
Ubicación: Seattle, USA
Mensajes: 733
Antigüedad: 15 años, 10 meses
Puntos: 61
Respuesta: Servidor C++

close() podria estar fallando, o podria estarse demorando, haces socket.close() sin chequear y haces socket.bind() inmediatamente sin esperar ni chequear el resultado del close().

Para estar seguro que es un problema de timing, agregar un delay entre el close y el bind(), si los problemas se resuelven esperando, agregar sincronizacion.

Si por otro lado es el close() el que falla, verificar la razon de esta falla, haciendo logica de correccion en caso de ella.
  #6 (permalink)  
Antiguo 21/07/2011, 10:27
 
Fecha de Ingreso: abril-2010
Ubicación: Rosario
Mensajes: 1.850
Antigüedad: 14 años
Puntos: 228
Respuesta: Servidor C++

Mira lo que encontre en esta pagina:
http://beej.us/guide/bgnet/output/ht...gnet.html#bind

Sometimes, you might notice, you try to rerun a server and bind() fails, claiming "Address already in use." What does that mean? Well, a little bit of a socket that was connected is still hanging around in the kernel, and it's hogging the port. You can either wait for it to clear (a minute or so), or add code to your program allowing it to reuse the port, like this:
Código C++:
Ver original
  1. int yes=1;
  2. //char yes='1'; // Solaris people use this
  3.  
  4. // lose the pesky "Address already in use" error message
  5. if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
  6.     perror("setsockopt");
  7.     exit(1);
  8. }
  #7 (permalink)  
Antiguo 22/07/2011, 01:18
 
Fecha de Ingreso: junio-2011
Ubicación: Saarbrücken
Mensajes: 9
Antigüedad: 12 años, 10 meses
Puntos: 0
Respuesta: Servidor C++

Muchas gracias.
voy a probar todo esto!
  #8 (permalink)  
Antiguo 25/07/2011, 00:40
 
Fecha de Ingreso: junio-2011
Ubicación: Saarbrücken
Mensajes: 9
Antigüedad: 12 años, 10 meses
Puntos: 0
Respuesta: Servidor C++

Hola,
He estado probando el tema de la sincronización y parece que no es eso.
Cómo puedo saber si el close no funciona bien?
Gracias!!
  #9 (permalink)  
Antiguo 25/07/2011, 19:45
 
Fecha de Ingreso: junio-2008
Ubicación: Seattle, USA
Mensajes: 733
Antigüedad: 15 años, 10 meses
Puntos: 61
Respuesta: Servidor C++

close() devuelve el resultado de la operacion.
  #10 (permalink)  
Antiguo 26/07/2011, 05:07
 
Fecha de Ingreso: junio-2011
Ubicación: Saarbrücken
Mensajes: 9
Antigüedad: 12 años, 10 meses
Puntos: 0
Respuesta: Servidor C++

Gracias!
El close funciona bien y por mucho retardo que le ponga entre el close y el bind sigue dándome error de que el socket está en uso.
Alguna idea de por qué después de cerrar los sockets en modo servidor no puedo volver a abrir un socket servidor con la misma IP y puerto??
Muchísimas gracias...
  #11 (permalink)  
Antiguo 26/07/2011, 08:11
 
Fecha de Ingreso: junio-2008
Ubicación: Seattle, USA
Mensajes: 733
Antigüedad: 15 años, 10 meses
Puntos: 61
Respuesta: Servidor C++

Una duda, si tienes tu servidor atendiendo y un cliente se conecta y luego se desconecta, te llegaria el evento TCP_DISCONNECT, pero dado que estas en modo servidor, tu logica trataria de cerrar el socket del servidor sin cerrar el socket del cliente, verdad?

Yo creo que es por eso que no puedes hacer bind() de nuevo, pero tal vez seria mas útil saber que secuencia de pasos realizas para probar, y cual es la salida que obtienes de tu programa, o bien, confirmar que lo que haces es lo que describí previamente y en tal caso, te falta cerrar el socket del cliente.
  #12 (permalink)  
Antiguo 26/07/2011, 09:53
 
Fecha de Ingreso: abril-2010
Ubicación: Rosario
Mensajes: 1.850
Antigüedad: 14 años
Puntos: 228
Respuesta: Servidor C++

Una pregunta miraste la sugerencia que te mande??
  #13 (permalink)  
Antiguo 27/07/2011, 02:00
 
Fecha de Ingreso: junio-2011
Ubicación: Saarbrücken
Mensajes: 9
Antigüedad: 12 años, 10 meses
Puntos: 0
Respuesta: Servidor C++

@Sam90: No entiendo muy bien qué hace exactamente el fragmento de código que me sugieres.
La función setsockopt libera por completo al socket listener?
  #14 (permalink)  
Antiguo 27/07/2011, 02:12
 
Fecha de Ingreso: junio-2011
Ubicación: Saarbrücken
Mensajes: 9
Antigüedad: 12 años, 10 meses
Puntos: 0
Respuesta: Servidor C++

@CaligaryCorpus : Cuando me llega el evento TCP_DISCONNECT en el servidor cierro tanto el socket listener (socket) como el que utiliyo para la comunicación (pClientSock). Ambos en el lado del servidor, que es el código que he puesto.
En el lado del cliente, se cierra el socket cliente que, por otro lado, es lo que hace que al servidor le llegue el evento TCP_DISCONNECT..
Además, no sé de qué manera podría el servidor cerrar el socket del cliente...
Gracias!!!
  #15 (permalink)  
Antiguo 27/07/2011, 11:23
 
Fecha de Ingreso: abril-2010
Ubicación: Rosario
Mensajes: 1.850
Antigüedad: 14 años
Puntos: 228
Respuesta: Servidor C++

Fijate que setsockopt tenea varias configuracion en el socket. Una de essas es poder reutilizar la direccion ip y puerto.
Mira esta pagina, sobre todo la parte de option_name la opcion SO_REUSEADDR:
http://pubs.opengroup.org/onlinepubs...etsockopt.html
  #16 (permalink)  
Antiguo 27/07/2011, 14:08
 
Fecha de Ingreso: junio-2008
Ubicación: Seattle, USA
Mensajes: 733
Antigüedad: 15 años, 10 meses
Puntos: 61
Respuesta: Servidor C++

Has intentado hacer un programa mas corto, que solo tenga la logica de un servidor, luego cerrar la conexion, luego abrir de nuevo como servidor? Si eso funciona, el problema esta en la logica que tienes. Sugiero que ademas de probar lo anterior, copies aqui la salida de una sesion de prueba en donde se ve la falla. Tambien deberias imprimir el valor de la variable end_NM_config, cada vez que la cambies, pues ella controla el comportamiento de los close().
  #17 (permalink)  
Antiguo 28/07/2011, 02:39
 
Fecha de Ingreso: junio-2011
Ubicación: Saarbrücken
Mensajes: 9
Antigüedad: 12 años, 10 meses
Puntos: 0
Respuesta: Servidor C++

Muchas gracias a ambos.
De momento, he resuelto el problema pasando a modo cliente y luego volviendo a servidor.
Así no hay ningún error de BIND...
Es un poco chapuza, pero llevo bastante tiempo atascada con esto y debo continuar mi trabajo.
Cuando finalice, intentaré retomar esto para arreglarlo con vuestras sugerencias.
Muchisimas gracias!

Etiquetas: servidor
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 01:49.