¿Socket cerrado?

Iniciado por Dealer098, 20 Diciembre 2011, 23:20 PM

0 Miembros y 1 Visitante están viendo este tema.

Dealer098

¡Buenas de nuevo! Estaba haciendo probaturas con sockets en C, Linux, y se me ha ocurrido una pregunta.
¿Cómo puedo detectar que el cliente/host ha perdido la conexión? Pongo un ejemplo: Tengo un programa que se conecta a cierta IP con cierto puerto. Ahora, al conectarse, recibe datos del servidor. Si corto la conexión de este programa, ¿cómo puedo hacer que el servidor "reaccione"? Hay que tener en cuenta que el programa no envía datos, sólo recibe. ¿Qué se os ocurre?

Un saludo. Dealer.
Stupid silly boy... xD

naderST

La manera mas fácil de hacerlo es enviar un mensaje al cliente cada cierto tiempo y si la función send() retorna SOCKET_ERROR quiere decir que se perdió la conexión. Otra manera es con la función select() investiga un poco sobre ello.

BlackZeroX

#2
cuando creas el socket crea un hilo que manipute recv() con el buffer predeterminado del SO, si retorna SOCKET_ERROR o 0 (Son dos casos particulares), es que el Socket se ha cerrado abruptamente, este metodo es el que uso y SIEMPRE me ha servidor!¡.

por ejemplo yo uso este proceso para detectar dicho evento:

Código (cpp) [Seleccionar]


/***
CallBack RequestData
-> Esta clase es friend de CSockBase, CSockClient y CSockServer.
***/

void*
myCallDataArrival
(
void* p // Puntero a la clase Base CSockBase
)
{
    char* szBuff = NULL;
    char* szNewBuff = NULL;
    int iRetSize = 0;
    LPCSOCKBASE t = (LPCSOCKBASE)p;

    if (!t)
    {
        t->threadDataArrival = {};
        pthread_exit(NULL);
        return NULL;
    }

    szBuff = new char[t->uiBuffRcv];

    do
    {
        iRetSize = recv(t->mySock, szBuff, t->uiBuffRcv, 0);

        if (iRetSize == SOCKET_ERROR || iRetSize == 0 ) // ¿Se ha desconectado el Socket?...
            break;  //  Exit do|while(1)

        szNewBuff = new char[t->udtBuffRcv.size + iRetSize];

        memcpy(szNewBuff, t->udtBuffRcv.szData, t->udtBuffRcv.size);
        memcpy(&szNewBuff[t->udtBuffRcv.size], szBuff, iRetSize);

        delete t->udtBuffRcv.szData;

        t->udtBuffRcv.szData = szNewBuff;
        t->udtBuffRcv.size += iRetSize;

        if (t->pEventDataArrival != NULL)
        { //Activacion del evento de Recepcion de Datos...
            pthread_mutex_lock(&t->mutexDataArrival); // Bloquemos para que no se creen mas de los necesarios.
            t->pEventDataArrival(t); // evento Volatile
            pthread_mutex_unlock(&t->mutexDataArrival); // Habilitamos.
        }
    }
    while(1);

    t->close();// Cerramos...

    if (t->pEventClosed)
        t->pEventClosed(t);

    t->threadDataArrival = {};
    pthread_exit(NULL);

    return NULL;
}




Dulces Lunas!¡.
The Dark Shadow is my passion.

Dealer098

La primera idea ya la he puesto en práctica, pero nada. La segunda es demasiado enrevesada para lo que quiero hacer, pero gracias.

Os dejo el código que tengo y un resumen del problema:

#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

int sockfd, newsock;

int main (int argc, char *argv [])
{
printf ("Code 0.1 - Dealer 2011\n");
if (argc != 2) { printf ("Use: %s <port>\n\n", argv [0]); return 1; }

int cont = 0;
struct sockaddr_in client, host;
char buffer [1024];
int size=sizeof (client);

sockfd=socket (2, 1 ,  0);
host.sin_family=AF_INET;
host.sin_port=htons (atoi (argv [1]));
host.sin_addr.s_addr=0;
bind (sockfd, (struct sockaddr*)&host, sizeof (struct sockaddr));
listen (sockfd, 1);

while (1)
{
newsock=accept (sockfd, (struct sockaddr*)&client, &size);
printf ("Got connection from %s:%d\n", inet_ntoa (client.sin_addr), ntohs (client.sin_port));

while (cont != -1)
{
time_t now=time (0);
struct tm *ahora;
ahora=localtime ((const time_t*)&now);
strftime (buffer, 1024, "%d/%m/%Y %H:%M:%S \n" , ahora);
buffer [strlen (buffer)] = 0;
cont=send (newsock, &buffer, strlen (buffer), 0);
sleep (1);
}

printf ("Finishing connection from %s:%d\n\n", inet_ntoa (client.sin_addr), ntohs (client.sin_port));
close (newsock);
}
close (sockfd);
return 0;
}


$ gcc -o serv serv.c
$ ./serv 31337
Code 0.1 - Dealer 2011
Got connection from 127.0.0.1:48677
$
$ gdb -q serv
Leyendo símbolos desde /home/juanra/Escritorio/serv...(no se encontraron símbolos de depuración)hecho.
(gdb) r 31337
Starting program: /home/juanra/Escritorio/serv 31337
Code 0.1 - Dealer 2011
Got connection from 127.0.0.1:48678

Program received signal SIGPIPE, Broken pipe.
0x0012d422 in __kernel_vsyscall ()
(gdb)


$ nc -vv 127.0.0.1 31337
Connection to 127.0.0.1 31337 port [tcp/*] succeeded!
22/12/2011 13:57:09
22/12/2011 13:57:10
22/12/2011 13:57:11
22/12/2011 13:57:12
22/12/2011 13:57:13
^C
$ nc -vv 127.0.0.1 31337
Connection to 127.0.0.1 31337 port [tcp/*] succeeded!
22/12/2011 13:57:33
22/12/2011 13:57:34
22/12/2011 13:57:35
22/12/2011 13:57:36
^C
$

A ver: El primer code es el código del servidor. El segundo es la terminal desde la que se ejecuta code. El tercero muestra los procesos de netcat, que se conecta al servidor corriendo en la segunda terminal, y que tras pocos segundos recibe una interrupción ^C para interrumpir la conexión. En el momento de la interrupción, la idea es que la segunda terminal (la del servidor) mostrase un mensaje diciendo que la conexión se ha finalizado. Ahora, el problema es que al cortar desde netcat la conexión, hay un error que nos muestra el GDB al correr el servidor desde el depurador. Dice que el error es por "broken pipe". Tras varias pruebas, he visto que el "error" se producía cuando se cortaba la conexión y la ejecución del programa llega a la revisión del while. ¿Por qué? ¿Alguna idea?
PS: Evidentemente, los dos programas (servidor y nc) corren a la vez. Un saludo. Dealer
Stupid silly boy... xD

BlackZeroX

Si tu idea es detectar de manera automatica dicho evento vas a tener que usar HILOS + Semaforos o Mutex quieras o no (Es lo mas viable), la de mandar a cada rato algo con send no es viable...

Un ejemplo mas completo (OJO solo el Cliente)... NO esta terminado ese ejemplo pero la idea de como detectar la desconexion de un Socket esta en  void myCallDataArrival(CSocketBase* t)... esta en C++...

Dulces Lunas!¡.
The Dark Shadow is my passion.

Sagrini

Mmm... creo que el problema está en la línea de "while (cont != -1)", que no debería ser -1. Prueba a ver con otros valores que no sean -1 :P Ni idea!

Arkangel_0x7C5

yo detecto la desconexion usando socket asíncronos, lo detecto al usar recv

Código (cpp) [Seleccionar]

ulong ReadSZ = 0;
wsock->Ioctlsocket(hSocket,FIONREAD,&ReadSZ);
if(ReadSZ){
//Se recibieron datos
//Codigo de recv
return ReadSZ;
}else{
//No hay datos
ReadSZ = wsock->Recv(hSocket,0,0,MSG_PEEK);
if(!(ReadSZ+1)){
//A habido un error
switch(wsock->WsaGetLastError()){
case WSAEWOULDBLOCK:
break;
case WSAECONNRESET:
//Se cerro la conexion
default:
//errror
return -1;
}
}else{
//Se cerro la conexion
return -1;
}
}


La cosa es que si ReadSZ es zero no hay datos para leer, y al usar rcv si la conexio se cierra bien da 0 y si se cierra mal da SOCKET_ERROR y LastError = WSAECONNRESET.

Sagrini

Yo lo dejaría así:


#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

int sockfd, newsock;

int main (int argc, char *argv [])
{
printf ("Code 0.1 - Dealer 2011\n");
if (argc != 2) { printf ("Use: %s <port>\n\n", argv [0]); return 1; }

int cont = 0;
struct sockaddr_in client, host;
char buffer [1024];
int size=sizeof (client);

sockfd=socket (2, 1 ,  0);
host.sin_family=AF_INET;
host.sin_port=htons (atoi (argv [1]));
host.sin_addr.s_addr=0;
bind (sockfd, (struct sockaddr*)&host, sizeof (struct sockaddr));
listen (sockfd, 1);

while (1)
{
newsock=accept (sockfd, (struct sockaddr*)&client, &size);
printf ("Got connection from %s:%d\n", inet_ntoa (client.sin_addr), ntohs (client.sin_port));

do
{
time_t now=time (0);
struct tm *ahora;
ahora=localtime ((const time_t*)&now);
strftime (buffer, 1024, "%d/%m/%Y %H:%M:%S \n" , ahora);
buffer [strlen (buffer)] = 0;
cont=send (newsock, &buffer, strlen (buffer), 0);
sleep (1);
} while (cont == strlen (buffer));

printf ("Finishing connection from %s:%d\n\n", inet_ntoa (client.sin_addr), ntohs (client.sin_port));
close (newsock);
}
close (sockfd);
return 0;
}