如何停止收听并仅从一个用户接收数据并在他断开连接时重新开始收听

How to stop listening and recv data from only one user and start listening again when he disconnects

我正在尝试编写一个服务器-客户端程序。这个想法是 server

  1. listens() 在给定的端口上
  2. 当用户连接时,它接受()连接并停止监听
  3. 在用户断开连接时,它会返回到监听状态并一直持续下去。

现在,我已经创建了服务器并且通信正常,但是,我不确定如何在用户连接时停止监听并在断开连接时开始监听。有人可以帮我吗?

此外,我正在关注 beejs 指南

谢谢

停止监听的(唯一)方法是关闭监听套接字。这对已经接受的连接没有影响,因此可以继续使用它们。要再次开始监听,您需要打开一个新的监听套接字并绑定它。如果您想在 TCP 延迟期结束之前重新打开端口,您可能需要套接字上的 SO_REUSEADDR 选项。

您可以改为保留侦听套接字,在完成第一个连接之前不再接受任何连接,但这实际上不会不侦听 - 任何其他尝试连接的客户端都会从内核(因此它会认为它已连接)而不是拒绝。

第三种可能性与您的要求更不一样,但可能更好的设计是在处理第一个连接时保持套接字打开并接受其他连接,然后正确关闭这些新连接远离某种忙碌的消息。这样客户至少可以知道发生了什么事。

这完全取决于您希望客户端在尝试连接到繁忙的服务器时看到的内容。

使服务器在接受连接后拒绝来自客户端的连接的唯一方法是 close(2) 您用于 accept(2) 连接的套接字。

不可能让内核一次拒绝一个连接上的连接。如果没有套接字监听,内核只会拒绝连接。

下面是一个服务器示例,其中接受套接字在连接之间关闭,因此如果您启动第二个连接,第二个连接将出现 ECONNREFUSED 错误。

但是这里有一个竞争条件:如果服务器程序在 accept(2)close(2) 系统调用之间时碰巧连接了第二个连接,则第二个连接将打开(并关闭)由服务器没有错误。如果第二个连接中的客户端尝试 read(2),它将从服务器收到一个 eof,如果它尝试 write(2) 到连接,则会收到一个错误。

#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define F(_fmt) __FILE__":%d:%s: "_fmt, __LINE__, __func__
#define ERR(_ex_cod, _fmt, ...) do { \
        fprintf(stderr,              \
                F("ERROR: "_fmt),    \
                ##__VA_ARGS__);      \
        if (_ex_cod)                 \
            exit(_ex_cod);           \
    } while (0)

char *
sockaddr2str(
        struct sockaddr_in *addr,
        char *buf,
        size_t bufsz)
{
    char *ret_val = buf;
    snprintf(buf, bufsz,
            "%s:%d",
            inet_ntoa(addr->sin_addr),
            ntohs(addr->sin_port));
    return ret_val;
}

int main()
{
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(12345);

    for(;;) {
        int acc_sockfd = socket(PF_INET, SOCK_STREAM, 0);
        if (acc_sockfd < 0)
            ERR(EXIT_FAILURE,
                    "socket: %s\n", strerror(errno));

        /* this is needed in case you need to reopen it in a short time. */
        int reuse_addr = 1;
        int res = setsockopt(
                acc_sockfd,
                SOL_SOCKET, SO_REUSEADDR,
                &reuse_addr, sizeof reuse_addr);
        if (res < 0)
            ERR(EXIT_FAILURE,
                "setsockopt: %s\n", strerror(errno));

        res = bind(
                acc_sockfd,
                (struct sockaddr *) &server_addr,
                sizeof server_addr);
        if (res < 0)
            ERR(EXIT_FAILURE,
                    "bind: %s\n", strerror(errno));

        /* 0 listen(2) will make the queue size 0, so only
         * connections that enter while server is accept()ing
         * them will enter */
        res = listen(acc_sockfd, -1);
        if (res < 0)
            ERR(EXIT_FAILURE,
                    "listen: %s\n", strerror(errno));

        struct sockaddr_in client_addr;
        client_addr.sin_family = AF_INET;
        client_addr.sin_addr.s_addr = INADDR_ANY;
        client_addr.sin_port = 0;
        socklen_t client_addr_sz = sizeof client_addr;

        int conn_sockfd = accept(
                acc_sockfd,
                (struct sockaddr *)&client_addr,
                &client_addr_sz);
        if (res < 0)
            ERR(EXIT_FAILURE,
                "accept: %s\n", strerror(errno));

        close(acc_sockfd);

        char client_name[256];
        sockaddr2str(&client_addr,
                client_name, sizeof client_name);

        char buff[1024];
        printf("Connection from %s\n", client_name);

        FILE *f = fdopen(conn_sockfd, "r");
        if (!f)
            ERR(EXIT_FAILURE,
                "fdopen: %s\n", strerror(errno));
        int c;
        while((c = fgetc(f)) != EOF) {
            size_t n = snprintf(
                    buff, sizeof buff,
                    "[%02x]%s",
                    c,
                    c == '\n'
                        ? "\r\n"
                        : "");
            write(conn_sockfd, buff, n);
            if (c == '3') break;
        }
        printf("Connection from %s ended\n", client_name);
        close(conn_sockfd);
        fclose(f);
    }
}