【问题标题】:C - How to cycle a socketC - 如何循环套接字
【发布时间】:2014-06-19 14:52:13
【问题描述】:

我有一个程序可以打印我发送的文件的内容:

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);

但我希望这个程序能够监听我想要打印的其他内容。所以我需要循环socket,监听socket。我知道我需要 select() 函数,但我不知道如何在我的代码中使用它。

【问题讨论】:

  • 你需要循环调用accept()。每次连接进来时,它都会为它返回一个新的描述符。
  • 当recv返回错误时,值会是-1,而package不会被填写。另外,想想当接收到的数据不是以零结尾的时候会发生什么应该像一个字符串。
  • 另外,是什么让你认为“包”缓冲区总是包含一个漂亮的空终止字符串?
  • “包”缓冲区将始终包含一个很好的以空字符结尾的字符串,因为我发送的所有脚本都是这样终止的。这里我只需要用select循环listen和accept,但是不知道怎么用。
  • 您不是在发送脚本,而是在发送字节流,如果 >0,您的“状态”会告诉您 recv() 调用已接收到多少字节。这可以介于 1 和 PACKAGESIZE 之间,通常不会以 null 结尾。

标签: c sockets printf cycle select-function


【解决方案1】:

您需要在循环中调用select()(或者更好的是epoll()),在其中将readfds 参数传递给包含侦听套接字描述符和所有连接的客户端套接字描述符的fd_set。当select() 退出时,它会告诉你哪些套接字上有待处理的可读数据。

如果侦听套接字有待读取的数据,请在其上调用accept(),如果成功则将返回的套接字描述符添加到某处的跟踪列表中,以便您可以将其包含在对select() 的后续调用中。

如果任何其他套接字有待读取的数据,请在它们上调用recv() 并根据需要对它们的数据进行操作。如果 recv() 在任何给定套接字上返回 -1 或 0,则 close() 该套接字描述符并将其从跟踪列表中删除,以便在后续调用 select() 时将其省略。

话虽如此,您的所有套接字调用都缺少足够的错误处理。它们中的任何一个都可能失败,而您不会知道这对recv() 尤其重要,因此您可以正确检测和清理任何断开/丢失的客户端连接。

另外,您假设recv() 总是返回以空值结尾的字符串,但事实并非如此。客户端可能会发送一个以 null 结尾的字符串,但不能保证 recv() 在一次读取中读取整个以 null 结尾的字符串。你必须在循环中调用recv(),缓冲任何接收到的数据,直到你真正收到空终止符,然后继续处理完成的字符串。

试试这样的(伪代码让你根据需要填写客户跟踪列表的实现细节):

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);

【讨论】:

    猜你喜欢
    • 2014-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多