TCP无法在客户端中检测到关闭的套接字

TCP无法在客户端中检测到关闭的套接字,c,tcp,C,Tcp,我的服务器刚刚连接到一个客户机,然后就断开连接,而客户机试图向一个关闭的套接字发送一个整数(scanf是为了确保服务器先关闭它)。我使用send with MSG_NOSIGNAL并检查EPIPE,但未设置标志。我认为结果的打印值应该是-1或0,但它等于1,因为我正在已经关闭的套接字上写入。有人能解释一下吗 服务器代码: #define QUEUE_LENGTH 5 #define PORT_NUM 10002 #define BUFFER_SIZE 512000 int ma

我的服务器刚刚连接到一个客户机,然后就断开连接,而客户机试图向一个关闭的套接字发送一个整数(scanf是为了确保服务器先关闭它)。我使用send with MSG_NOSIGNAL并检查EPIPE,但未设置标志。我认为结果的打印值应该是-1或0,但它等于1,因为我正在已经关闭的套接字上写入。有人能解释一下吗

服务器代码:

#define QUEUE_LENGTH     5
#define PORT_NUM     10002
#define BUFFER_SIZE 512000

int main(int argc, char *argv[]) {
    int sock, msg_sock;
    struct sockaddr_in server_address;
    struct sockaddr_in client_address;
    socklen_t client_address_len;

    sock = socket(PF_INET, SOCK_STREAM, 0); // creating IPv4 TCP socket
    if (sock < 0)
        syserr("socket");

    server_address.sin_family = AF_INET; // IPv4
    server_address.sin_addr.s_addr = htonl(
            INADDR_ANY); // listening on all interfaces
    server_address.sin_port = htons(PORT_NUM); 

    // bind the socket to a concrete address
    if (bind(sock, (struct sockaddr *) &server_address,
             sizeof(server_address)) < 0)
        syserr("bind");

    // switch to listening (passive open)
    if (listen(sock, QUEUE_LENGTH) < 0)
        syserr("listen");

    printf("accepting client connections on port %hu\n",
           ntohs(server_address.sin_port));


    for (;;) {
        client_address_len = sizeof(client_address);
        msg_sock = accept(sock, (struct sockaddr *) &client_address,
                          &client_address_len);
        if (msg_sock < 0)
            syserr("accept");



        printf("ending connection\n");
        if (close(msg_sock) < 0) {
            printf("ErrorClosingSocket\n");
            break;
        }
        continue;
    }
    return 0;
}
#定义队列长度5
#定义端口号10002
#定义缓冲区大小512000
int main(int argc,char*argv[]){
int sock,msg_sock;
服务器地址中的结构sockaddr\u;
客户端地址中的结构sockaddr\u;
客户地址;
sock=socket(PF_INET,sock_STREAM,0);//创建IPv4 TCP套接字
if(sock<0)
系统错误(“套接字”);
server\u address.sin\u family=AF\u INET;//IPv4
服务器地址sin地址s地址=htonl(
INADDR_ANY);//侦听所有接口
服务器\u地址.sin\u端口=htons(端口号);
//将套接字绑定到具体地址
if(绑定(sock,(结构sockaddr*)和服务器地址,
sizeof(服务器地址))<0)
系统错误(“绑定”);
//切换到收听(被动打开)
if(侦听(sock,队列长度)<0)
syserr(“倾听”);
printf(“在端口%hu上接受客户端连接\n”,
ntohs(服务器地址sin端口);
对于(;;){
客户地址=sizeof(客户地址);
msg_sock=accept(sock,(struct sockaddr*)和客户端地址,
&客户(地址);
如果(msg_sock<0)
系统错误(“接受”);
printf(“结束连接\n”);
如果(关闭(msg_sock)<0){
printf(“ErrorClosingSocket\n”);
打破
}
继续;
}
返回0;
}
客户端代码:

int sendSomething(void *to_send, int socket, uint32_t length) {

    if (send(socket, to_send, length, MSG_NOSIGNAL) !=
        length) {
        if (errno == EPIPE)  // Sending on closed connection
            return 0;
        return -1;
    }

    return 1;
}

int main(int argc, char *argv[]) {
    int sock;
    struct addrinfo addr_hints;
    struct addrinfo *addr_result;

    int err;

    if (argc != 3)
        fatal("Usage: %s host port\n", argv[0]);

    // 'converting' host/port in string to struct addrinfo
    memset(&addr_hints, 0, sizeof(struct addrinfo));
    addr_hints.ai_family = AF_INET; // IPv4
    addr_hints.ai_socktype = SOCK_STREAM;
    addr_hints.ai_protocol = IPPROTO_TCP;
    // argv[1] is localhost and argv[2] is 10002 
    err = getaddrinfo(argv[1], argv[2], &addr_hints, &addr_result);
    if (err == EAI_SYSTEM) // system error
        syserr("getaddrinfo: %s", gai_strerror(err));
    else if (err != 0) // other error (host not found, etc.)
        fatal("getaddrinfo: %s", gai_strerror(err));


    // initialize socket according to getaddrinfo results
    sock = socket(addr_result->ai_family, addr_result->ai_socktype,
                  addr_result->ai_protocol);
    if (sock < 0)
        syserr("socket");

    // connect socket to the server
    if (connect(sock, addr_result->ai_addr, addr_result->ai_addrlen) < 0)
        syserr("connect");

    freeaddrinfo(addr_result);


    int result;
    scanf("%d", &result);
    uint16_t test;

    test = htons(1);
    result = sendSomething(&test, sock, sizeof(test));
    printf("result:%d\n", result);

    if (close(sock) < 0) {
        printf("ErrorClosingSocket\n");
    }

    return 0;
}
int sendSomething(void*to\u send,int socket,uint32\u t长度){
如果(发送(套接字、发送、长度、消息)=
长度){
if(errno==EPIPE)//在关闭的连接上发送
返回0;
返回-1;
}
返回1;
}
int main(int argc,char*argv[]){
int袜子;
结构addrinfo addr_提示;
结构addrinfo*addr\u结果;
INTERR;
如果(argc!=3)
致命(“用法:%s主机端口\n”,argv[0]);
//将字符串中的主机/端口“转换”为结构addrinfo
memset(&addr_hints,0,sizeof(struct addrinfo));
addr\u hits.ai\u family=AF\u INET;//IPv4
addr_hits.ai_socktype=SOCK_STREAM;
addr_hits.ai_protocol=IPPROTO_TCP;
//argv[1]是本地主机,argv[2]是10002
err=getaddrinfo(argv[1],argv[2],&addr\u提示,&addr\u结果);
if(err==EAI\u SYSTEM)//系统错误
syserr(“getaddrinfo:%s”,gai_strerror(err));
else if(err!=0)//其他错误(找不到主机等)
致命(“getaddrinfo:%s”,gai_strerror(err));
//根据getaddrinfo结果初始化套接字
sock=socket(addr\u result->ai\u family,addr\u result->ai\u socktype,
地址结果->人工智能协议);
if(sock<0)
系统错误(“套接字”);
//将套接字连接到服务器
如果(连接(sock,addr\u result->ai\u addr,addr\u result->ai\u addrlen)<0)
系统错误(“连接”);
freeaddrinfo(addr_结果);
int结果;
scanf(“%d”,和结果);
uint16 t检验;
试验=htons(1);
结果=发送某物(&test,sock,sizeof(test));
printf(“结果:%d\n”,结果);
如果(关闭(袜子)<0){
printf(“ErrorClosingSocket\n”);
}
返回0;
}

注意:Fatal和Syserr仅用于报告错误,这是TCP的工作方式。当服务器关闭套接字时,会向客户端发送FIN。这仅表示服务器将不再发送任何数据。这并不一定意味着它不想接收更多的数据

因此,客户端可以在套接字上调用
send()
,而操作系统不会报告错误。如果服务器确实关闭了整个套接字,那么它将发送一个TCP重置数据包,作为对指示该情况的传入数据的响应。现在,套接字上的未来操作(写入/关闭)将指示错误

服务器(或任何对等方)确实可以使用syscall
shutdown()
只在中途(读写端)关闭连接。如果服务器关闭连接进行写入,则网络上会发生相同的情况,就像服务器使用
close()
关闭整个连接一样。更高级别协议的职责是确定每一方何时应关闭连接


如果您想确保您发送的所有数据确实已被对等方确认,您可以使用
SO\u LINGER
socket选项。但更常见的方法是,确保这是通信协议的一部分,即一部分请求在更高级别上关闭连接(例如,smtp
QUIT
命令)对等方通过关闭tcp连接对其作出反应。

您是仅尝试使用本地主机还是使用真正的远程设备?但客户端如何确定发送的数据是否确实被服务器接收,因为在我的情况下,客户端不知道这一点。另一个问题是,我是否正确理解客户机以两种方式读取fom closed socket时会出现错误:从客户端的两次写入(第二次错误)或一次客户机读取?@mikol如果要确保所有数据都被对等方接收,您应该查看
SO_LINGER
选项。