TCP连接断开后send还是能发送成功吗?

15 篇文章 2 订阅

如题,突然有个疑问,假如server 和client通过TCP建立连接后,server突然挂死。那么client调用

send()函数的返回值是什么呢?

测试结果:

如果client连接到server,当server进程终止之后,client的第一次发送是会成功的。

抓包如下图:

但是第二次send()就会失败,返回-1

Errno 32, Broken pipe。 同时也抓不到报文。

测试代码如下

server.c

#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<unistd.h>
#include<arpa/inet.h>
#define portnumber 8080
int main(int argc ,char *argv[])
{
    int sockfd,new_fd;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    int sin_size,iDataNum;
    char buffer[200];

    if (argc!=2) {
         fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);
         return 0;
    }
    if ((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) {
        fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
        return 0;
    }
    bzero(&server_addr,sizeof(struct sockaddr_in));
    server_addr.sin_family=AF_INET;
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    server_addr.sin_port=htons(portnumber);
    if (bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) {
        fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
        return 0;
    }
    if (listen(sockfd,5)==-1) {
        fprintf(stderr,"Listen error:%s\n\a",strerror(errno));
        return 0;
    }
    sin_size=sizeof(struct sockaddr_in);
    if ((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size))==-1) {
        fprintf(stderr,"Accept error:%s\n\a",strerror(errno));
        return 0;
    }
    while (1) {
        fprintf(stdout,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr));
        iDataNum=recv(new_fd,buffer,200,0);
        if (iDataNum<0) {
            perror("Recv\n");
            exit(1);
        }
        printf("Recv data is %s\n",buffer);
        send(new_fd,buffer,sizeof(buffer),0);
    }
    close(sockfd);
    return 0;
}

client.c

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

int main(int argc,char *argv[])
{
    int sockfd;
    char sendbuffer[200];
    char recvbuffer[200];
    struct sockaddr_in server_addr;
    struct hostent *host;
    int portnumber,nbytes;
    if (argc!=3) {
        fprintf(stderr,"Usage :%s hostname portnumber\a\n",argv[0]);
        return -1;
    }
    if ((host=gethostbyname(argv[1]))==NULL) {
        herror("Get host name error\n");
        return -1;
    }
    if ((portnumber=atoi(argv[2]))<0) {
        fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);
        return -1;
    }
    if ((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) {
        fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
        return -1;
    }
    bzero(&server_addr,sizeof(server_addr));
    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(portnumber);
    server_addr.sin_addr=*((struct in_addr *)host->h_addr);
    if (connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) {
        fprintf(stderr,"Connect error:%s\n",strerror(errno));
        return -1;
    }
    while (1) {
        printf("Please input your word:\n");
        scanf("%s",sendbuffer);
        printf("\n");
        if(strcmp(sendbuffer,"quit")==0)
            break;
        int ret = 0;
        ret = send(sockfd,sendbuffer,sizeof(sendbuffer),MSG_NOSIGNAL);
        printf("send ret %d\n", ret);
        if (ret == -1) {
            printf("send failed number %d, err;%s\n", errno, strerror(errno));
        }
        ret = recv(sockfd,recvbuffer,200,0);
        printf("recv data of my world is :%s; ret=%d\n",recvbuffer, ret);
    }
    close(sockfd);
    return 0;
}
执行过程

server:
./server 8080

client:
./client localhost 8080

遇到的问题

       如果send()函数的标志位为0,则当server终止之后,client发送第二次send时,会导致线程退出。原因

在linux下send函数原型为:

#include <sys/types.h>

 #include <sys/socket.h>

 ssize_t send(int sockfd, const void *buf, size_t len, int flags);

其中“flags”被忽略了,一般设置为0,但当flags为0时,如果客户端断开,继续往里边写数据的话,会引发一个信号SIGPIPE,此信号会引发线程的退出

所以,将“flags”设置为MSG_NOSIGNAL,则不会导致线程退出。

MSG_NOSIGNAL的含义,通过man send,说明如下:

 MSG_NOSIGNAL (since Linux 2.2)

              Requests not to send SIGPIPE on errors on stream oriented sockets when  the

              other end breaks the connection.  The EPIPE error is still returned.

意思是,当对方断开连接导致错误时,不发送SIGPIPE信号。但还是会返回EPIPE错误。

于是,只需要判断send是否成功就可以了。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个示例代码,实现了上述功能: ```python import socket import time # 设置连接信息 HOST = 'localhost' PORT = 8888 MSG = b'Hello, world!' # 统计信息 connection_count = 0 connection_success_count = 0 connection_total_time = 0 send_count = 0 send_success_count = 0 recv_count = 0 recv_success_count = 0 match_count = 0 match_success_count = 0 # 循环执行连接发送、接收、匹配、断开连接 for i in range(100): # 连接 start_time = time.time() try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((HOST, PORT)) connection_success_count += 1 except: print('Connection failed') continue end_time = time.time() connection_count += 1 connection_total_time += end_time - start_time # 发送数据 try: sock.sendall(MSG) send_success_count += 1 except: print('Send failed') sock.close() continue send_count += 1 # 接收数据 try: data = sock.recv(1024) recv_success_count += 1 except: print('Receive failed') sock.close() continue recv_count += 1 # 匹配数据 if data == MSG: match_success_count += 1 match_count += 1 # 断开连接 sock.close() # 输出统计信息 print(f'Connection success rate: {connection_success_count / connection_count:.2f}') print(f'Connection average time: {connection_total_time / connection_count:.2f}s') print(f'Send success rate: {send_success_count / send_count:.2f}') print(f'Send count: {send_count}') print(f'Receive success rate: {recv_success_count / recv_count:.2f}') print(f'Receive count: {recv_count}') print(f'Match success rate: {match_success_count / match_count:.2f}') print(f'Match count: {match_count}') ``` 这段代码连接了100次,每次发送1024字节的数据,并且统计了连接成功率、连接平均时长、发送成功率、发送次数、接收成功率、接收次数、匹配成功率、匹配次数。可以根据需要修改HOST、PORT、MSG等参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值