C - How to cycle a socket

188 Views Asked by At

I have a program which prints the content of a file which I send:

struct addrinfo hints;
struct addrinfo *serverInfo;

memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

getaddrinfo(NULL, PUERTO, &hints, &serverInfo);
//socket para escuchar
int listenningSocket;
listenningSocket = socket(serverInfo->ai_family, serverInfo->ai_socktype, serverInfo->ai_protocol);
bind(listenningSocket,serverInfo->ai_addr, serverInfo->ai_addrlen);
freeaddrinfo(serverInfo);

listen(listenningSocket, BACKLOG);
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
int socketCliente = accept(listenningSocket, (struct sockaddr *) &addr, &addrlen);
char package[PACKAGESIZE];
int status = 1;

printf("\nCliente conectado. Esperando mensajes:\n");
printf("\nEl contenido del script es: \n");

while (status != 0) {
    status = recv(socketCliente, (void*) package, PACKAGESIZE, 0);
    if (status != 0) printf("%s", package);
}
close(socketCliente);
close(listenningSocket);

But I want this program listen for others things I want to print. So I need to cycle the socket, the listening of the socket. I understand I need the select() function, but I don't know how to use it for my code.

1

There are 1 best solutions below

0
Remy Lebeau On

You need to call select() (or better, epoll()) in a loop, where you pass its readfds parameter an fd_set containing the listening socket descriptor and all connected client socket descriptors. When select() exits, it will tell you which sockets have pending readable data on them.

If the listening socket has pending data to read, call accept() on it, and if successful then add the returned socket descriptor to a tracking list somewhere so that you can include it in subsequent calls to select().

If any other sockets have pending data to read, call recv() on them and act on their data as needed. If recv() returns -1 or 0 on any given socket, close() that socket descriptor and remove it from your tracking list so you omit it in subsequent calls to select().

That being said, all of your socket calls are missing adequate error handling. Any of them can fail, and you wouldn't know This is especially important for recv() so you can properly detect and cleanup any disconnected/lost client connections.

Also, you are assuming that recv() always returns null-terminated strings, but that is simply not true. A client may send a null-terminated string, but recv() is not guaranteed to read an entire null-terminated string in one read. You have to call recv() in a loop, buffering any received data in between, until you actually receive the null terminator, THEN proceed with processing the completed string.

Try something more like this (pseudo-code for you to fill in the implementation details of the client tracking list as desired):

struct addrinfo hints;
struct addrinfo *serverInfo;

memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;

if (getaddrinfo(NULL, PUERTO, &hints, &serverInfo) < 0) {
    // TODO: error handling...
    return -1;
}

//socket para escuchar
int listenningSocket = socket(serverInfo->ai_family, serverInfo->ai_socktype, serverInfo->ai_protocol);
if (listenningSocket < 0) {
    // TODO: error handling...
    freeaddrinfo(serverInfo);
    return -1;
}

if (bind(listenningSocket, serverInfo->ai_addr, serverInfo->ai_addrlen) < 0) {
    // TODO: error handling...
    freeaddrinfo(serverInfo);
    close(listenningSocket);
    return -1;
}

freeaddrinfo(serverInfo);

if (listen(listenningSocket, BACKLOG) < 0) {
    // TODO: error handling...
    close(listenningSocket);
    return -1;
}

struct sockaddr_in addr;
socklen_t addrlen;
int socketCliente, maxfd, status;
char package[PACKAGESIZE];
char *str;
struct timeval timeout;

someList clients; // TODO: for you to decide the implementation of...

while (!someStopCondition) { // TODO: for you to decide the implementation of...

    fd_set rfd;
    FD_ZERO(&rfd);

    FD_SET(listenningSocket, &rfd);
    maxfd = listenningSocket;

    for (each fd in clients) { // TODO: for you to decide the implementation of...
        FD_SET(fd, &rfd);
        if (fd > maxfd) {
            maxfd = fd;
        }
    }

    // TODO: for you to decide on...
    timeout.tv_sec = ...;
    timeout.tv_usec = ...;

    status = select(maxfd + 1, &rds, NULL, NULL, &timeout);
    if (status < 0) {
        // TODO: error handling...
        break;
    }

    if (status == 0) {
        // TODO: do other things as needed...
        continue;
    }

    if (FD_ISSET(listenningSocket, &rds)) {
        addrlen = sizeof(addr);
        socketCliente = accept(listenningSocket, (struct sockaddr *) &addr, &addrlen);
        if (socketCliente < 0) {
            // TODO: error handling...
        }
        else if (!addToList(&clients, socketCliente)) { // TODO: for you to decide the implementation of...
            // TODO: error handling...
            close(socketCliente);
        } else {
            printf("\nCliente conectado. Esperando mensajes:\n");
        }
    }

    for (each fd in clients) { // TODO: for you to decide the implementation of...
        if (FD_ISSET(fd, &rfd)) {
            status = recv(fd, package, PACKAGESIZE, 0);
            if (status <= 0) {
                close(socketCliente);
                removeFromList(&clients, fd); // TODO: for you to decide the implementation of...
                continue;
            }

            // TODO: for you to decide the implementation of...
            addToEndOfClientBuffer(fd, package, status);
            while ((str = getNullTerminatedStringFromClientBuffer(fd)) != NULL) {
                printf("\nEl contenido del script es: %s\n", str);
                removeFromFrontOfClientBuffer(fd, strlen(str)+1);
            }
        }
    }
}

// TODO: for you to decide the implementation of...
for (each fd in clients) {
    close(fd);
}
clearList(&clients);

close(listenningSocket);