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

C 如何停止侦听并仅从一个用户接收数据,并在他断开连接时重新开始侦听,c,sockets,network-programming,network-protocols,C,Sockets,Network Programming,Network Protocols,我正在尝试编写一个服务器客户端程序。这个想法是服务器 在给定端口上侦听() 当用户连接时,它接受()连接并停止侦听 当用户断开连接时,它将返回到侦听状态,并且这种情况将永远持续下去 现在,我已经创建了服务器,通信进展顺利,但是,我不确定当用户连接时如何停止监听,并在断开连接时开始监听。有人能帮我吗 另外,我正在遵循beejs指南 谢谢停止收听的唯一方法是关闭收听插座。这对已接受的连接没有影响,因此它们可以继续使用。要重新开始侦听,您需要打开一个新的侦听套接字并将其绑定。如果要在TCP延迟时间过去

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

  • 在给定端口上侦听()
  • 当用户连接时,它接受()连接并停止侦听
  • 当用户断开连接时,它将返回到侦听状态,并且这种情况将永远持续下去
  • 现在,我已经创建了服务器,通信进展顺利,但是,我不确定当用户连接时如何停止监听,并在断开连接时开始监听。有人能帮我吗

    另外,我正在遵循beejs指南

    谢谢

    停止收听的唯一方法是关闭收听插座。这对已接受的连接没有影响,因此它们可以继续使用。要重新开始侦听,您需要打开一个新的侦听套接字并将其绑定。如果要在TCP延迟时间过去之前重新打开端口,可能需要套接字上的SO_REUSEADDR选项

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

    第三种可能更不符合您的要求,但更好的设计可能是在处理第一个连接时保持套接字打开并接受其他连接,然后立即用某种繁忙消息关闭这些新连接。这样,客户至少可以了解一些正在发生的事情

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

    停止侦听的唯一方法是关闭侦听套接字。这对已接受的连接没有影响,因此它们可以继续使用。要重新开始侦听,您需要打开一个新的侦听套接字并将其绑定。如果要在TCP延迟时间过去之前重新打开端口,可能需要套接字上的SO_REUSEADDR选项

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

    第三种可能更不符合您的要求,但更好的设计可能是在处理第一个连接时保持套接字打开并接受其他连接,然后立即用某种繁忙消息关闭这些新连接。这样,客户至少可以了解一些正在发生的事情


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

    使服务器在接受连接时拒绝客户端连接的唯一方法是关闭(2)用于接受(2)连接的套接字

    不可能让内核一次拒绝一个连接上的连接。内核仅在没有套接字侦听的情况下拒绝连接

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

    但这有一个竞争条件:如果服务器程序在
    accept(2)
    close(2)
    系统调用之间连接第二个连接,则服务器将打开(和关闭)第二个连接,并且不会出错。如果第二个连接中的客户端尝试
    读取(2)
    ,则会收到来自服务器的eof,如果客户端尝试
    写入(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 == '\033') break;
            }
            printf("Connection from %s ended\n", client_name);
            close(conn_sockfd);
            fclose(f);
        }
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #定义F(_fmt)uu文件::%d:%s:“_fmt,uu行,u函数”__
    #定义错误(_ex_cod,_fmt,…)do{\
    fprintf(标准\
    F(“错误:”_fmt)\
    ##__VA_ARGS_uu)\
    如果(_ex_cod)\
    出口(货到付款)\
    }而(0)
    煤焦*
    sockaddr2str(
    *地址中的结构sockaddr_,
    char*buf,
    尺寸(bufsz)
    {
    char*ret_val=buf;
    snprintf(buf,bufsz,
    “%s:%d”,
    inet\u ntoa(地址->sin\u地址),
    ntohs(地址->sin_港);
    返回返回值;
    }
    int main()
    {
    服务器地址中的结构sockaddr\u;
    服务器地址sin家庭=AF网络;
    server\u addr.sin\u addr.s\u addr=INADDR\u ANY;
    服务器地址sin端口=htons(12345);
    对于(;;){
    int acc_sockfd=套接字(PF_INET,SOCK_STREAM,0);
    如果(acc_sockfd<0)
    ERR(退出故障),
    “套接字:%s\n”,strerror(errno));
    /*这是必要的,以防你需要重新打开它在短时间内*/
    int reuse_addr=1;
    int res=setsockopt(
    acc_sockfd,
    SOL_插座,所以,
    &重复使用地址、重复使用地址的大小);
    如果(res<0)
    ERR(退出故障),
    “设置锁选项:%s\n”,strerror(errno));
    res=绑定(
    acc_sockfd,
    (结构sockaddr*)和服务器地址,
    服务器的大小(地址);
    如果(res<0)
    ERR(退出故障),
    “绑定:%s\n”,strerror(errno));
    /*0侦听(2)将使队列大小为0,因此仅
    *在服务器接受()时输入的连接
    *他们会进来的*/
    res=监听(acc_sockfd,-1);
    如果(res<0)
    ERR(退出故障),
    “侦听:%s\n”,strerror(errno));
    客户端地址中的结构sockaddr\u;
    氯