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

问题描述

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

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

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

另外,我正在关注 beejs 指南

谢谢

解决方法

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

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

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

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

,

让服务器在接受连接后拒绝来自客户端的连接的唯一方法是 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 == '\033') break;
        }
        printf("Connection from %s ended\n",client_name);
        close(conn_sockfd);
        fclose(f);
    }
}