C write(2)仍然报告成功,即使我已关闭套接字

C write(2)仍然报告成功,即使我已关闭套接字,c,sockets,unix,C,Sockets,Unix,我遇到了一个问题,即使在服务器端关闭()套接字之后,客户端仍然可以执行一次写入(),只有第二次写入()将返回SIGPIPE,这导致我丢失数据,因为我没有应用程序级握手,并且完全依赖于write()的返回值。在服务器端关闭连接后,我有没有办法直接获得SIGPIPE 测试程序如下: 我期待第二次写入返回SIGPIPE,但它返回了成功,只有第三次写入返回SIGPIPE!为什么会这样 #include <stdio.h> #include <errno.h> #include <sys

我遇到了一个问题,即使在服务器端关闭()套接字之后,客户端仍然可以执行一次写入(),只有第二次写入()将返回SIGPIPE,这导致我丢失数据,因为我没有应用程序级握手,并且完全依赖于write()的返回值。在服务器端关闭连接后,我有没有办法直接获得SIGPIPE

测试程序如下:

我期待第二次写入返回SIGPIPE,但它返回了成功,只有第三次写入返回SIGPIPE!为什么会这样

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdarg.h>
#include <netdb.h>
#include <unistd.h>
#include <signal.h>

void log_error(const char *fmt, ...) 
{
 va_list ap;
 char buf[BUFSIZ] = {0};

 va_start(ap, fmt);
 vsnprintf(buf, BUFSIZ, fmt, ap);
 fprintf(stderr, "ERROR: %s\n", buf);
 va_end(ap);
 return;
}

static int create_inet_socket()
{
    int fd;
    struct sockaddr_in my_addr;
    int yes = 1;

    if ((fd=socket(PF_INET, SOCK_STREAM, 0))==-1) {
 log_error("create_inet_socket:socket:%d:%s", errno, strerror(errno));
 return -1;
    }

    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))==-1) {
 log_error("create_inet_socket:setsockopt:%d:%s", errno, strerror(errno));
 return -1;
    }

    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(9998);
    my_addr.sin_addr.s_addr = INADDR_ANY;
    memset(&(my_addr.sin_zero), '0', 8);

    if (bind(fd, (struct sockaddr*)&my_addr, sizeof(struct sockaddr_in))==-1) {
 log_error("main:bind:%d:%s", errno, strerror(errno));
 return -1;
    }

    if (listen(fd, 5)==-1) {
 log_error("main:listen:%d:%s", errno, strerror(errno));
 return -1;
    }

    return fd;
}

int myconnect()
{
    int fd;
    char ch[1] = {'a'};
    struct sockaddr_in sin;
    fd_set wfds;
    struct timeval tv;
    struct hostent *he;
    ssize_t nwritten;

    if ((fd=socket(PF_INET, SOCK_STREAM, 0))==-1) {
 log_error("rlog:socket failed:%d:%s", errno, strerror(errno));
 return -1;
    }

    bzero(&sin, sizeof(sin));

    if ((he=gethostbyname("localhost"))==NULL) {
 log_error("rlog:gethostbyname failed:%d:%s", errno, strerror(errno));
 return -1;
    }

    sin.sin_addr = *((struct in_addr*)he->h_addr);
    sin.sin_port = htons(9998);
    sin.sin_family = AF_INET;

    if (connect(fd,(struct sockaddr *) &sin,sizeof(sin)) == -1) {
 log_error("connect:%d:%s", errno, strerror(errno));
 return 0;
    }

    nwritten = write(fd, &ch, 1);
    if (nwritten==-1) {
 log_error("write:%d:%s", errno, strerror(errno));
    } else {
 fprintf(stderr, "client : 1. written %ld\n", nwritten);
    }
    sleep(3);
    nwritten = write(fd, &ch, 1);
    if (nwritten==-1) {
 log_error("write:%d:%s", errno, strerror(errno));
    } else {
 fprintf(stderr, "client : 2. written %ld\n", nwritten);
    }
    sleep(3);
    nwritten = write(fd, &ch, 1);
    if (nwritten==-1) {
 log_error("write:%d:%s", errno, strerror(errno));
    } else {
 fprintf(stderr, "client : 3. written %ld\n", nwritten);
    }
    return 0;
}

void run_server()
{
    int fd;
    int newfd;
    char c[1];
    ssize_t nread;
    int status;
    fprintf(stderr, "server : Running\n");
    fd = create_inet_socket();
    if (fd==-1) {
 perror("create_inet_socket");
    }

    newfd = accept(fd, NULL, NULL);
    fprintf(stderr, "server : accepted newfd %d\n", newfd);

    nread = read(newfd, &c, 1);
    if (nread==-1) {
 log_error("read:%d:%s", errno, strerror(errno));
    } else {
 fprintf(stderr, "read returned %ld, closing socket\n", nread);
    }
    shutdown(newfd, SHUT_RDWR);
    close(newfd);
    wait(&status);
    fprintf(stderr, "server : exit\n");
}

void run_client()
{
    fprintf(stderr, "client : running\n");
    myconnect();
    fprintf(stderr, "client : exit\n");
    _exit(1);
}

int main()
{
    signal(SIGPIPE, SIG_IGN);
    int pid = fork();
    switch (pid) {
    case 0:
 run_client();
 break;
    case -1:
 perror("fork");
 break;
    default:
 run_server();
 break;
    }
    return 0;
}

#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
无效日志错误(常量字符*fmt,…)
{
va_列表ap;
char buf[BUFSIZ]={0};
va_启动(ap、fmt);
vsnprintf(buf、BUFSIZ、fmt、ap);
fprintf(标准,“错误:%s\n”,buf);
va_端(ap);
返回;
}
静态int创建_inet_套接字()
{
int-fd;
我的地址中的结构sockaddr\u;
int yes=1;
if((fd=socket(PF_INET,SOCK_STREAM,0))=-1){
日志错误(“创建内部套接字:套接字:%d:%s”,errno,strerror(errno));
返回-1;
}
if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int))=-1){
日志错误(“创建内部套接字:设置套接字:%d:%s”,错误号,strerror(错误号));
返回-1;
}
我的地址sin家庭=AFINET;
my_addr.sin_port=htons(9998);
my_addr.sin_addr.s_addr=INADDR\u ANY;
memset(&(my_addr.sin_zero),'0',8);
if(bind(fd,(struct sockaddr*)和my_addr,sizeof(struct sockaddr_in))=-1){
日志_错误(“主:绑定:%d:%s”,errno,strerror(errno));
返回-1;
}
如果(监听(fd,5)=-1){
日志_错误(“主:侦听:%d:%s”,errno,strerror(errno));
返回-1;
}
返回fd;
}
int myconnect()
{
int-fd;
char ch[1]={'a'};
sin中的结构sockaddr_;
fd_集wfds;
结构时间值电视;
结构宿主*he;
没有写的;
if((fd=socket(PF_INET,SOCK_STREAM,0))=-1){
日志错误(“rlog:socket失败:%d:%s”,errno,strerror(errno));
返回-1;
}
bzero(&sin,sizeof(sin));
if((he=gethostbyname(“localhost”))==NULL){
日志_错误(“rlog:gethostbyname失败:%d:%s”,errno,strerror(errno));
返回-1;
}
sin.sin\u addr=*((struct in\u addr*)he->h\u addr);
sin.sin_port=htons(9998);
sin.sin_family=AF_INET;
if(connect(fd,(struct sockaddr*)&sin,sizeof(sin))=-1){
日志错误(“连接:%d:%s”,错误号,strerror(错误号));
返回0;
}
nwrited=write(fd和ch,1);
如果(nwrited==-1){
日志错误(“写入:%d:%s”,错误号,strerror(错误号));
}否则{
fprintf(标准,“客户端:1.写入%ld\n”,未写入);
}
睡眠(3);
nwrited=write(fd和ch,1);
如果(nwrited==-1){
日志错误(“写入:%d:%s”,错误号,strerror(错误号));
}否则{
fprintf(标准,“客户端:2.写入%ld\n”,未写入);
}
睡眠(3);
nwrited=write(fd和ch,1);
如果(nwrited==-1){
日志错误(“写入:%d:%s”,错误号,strerror(错误号));
}否则{
fprintf(标准,“客户端:3.写入%ld\n”,未写入);
}
返回0;
}
void run_server()
{
int-fd;
int-newfd;
charc[1];
ssize_t nread;
智力状态;
fprintf(stderr,“服务器:正在运行\n”);
fd=创建inet_套接字();
如果(fd==-1){
perror(“创建inet套接字”);
}
newfd=accept(fd,NULL,NULL);
fprintf(stderr,“服务器:接受的newfd%d\n”,newfd);
nread=读取(newfd,&c,1);
如果(nread==-1){
日志错误(“读取:%d:%s”,错误号,strerror(错误号));
}否则{
fprintf(stderr,“读取返回%ld,关闭套接字\n”,nread);
}
关闭(新FD、关闭RDWR);
关闭(新FD);
等待(&状态);
fprintf(stderr,“服务器:退出\n”);
}
void run_client()
{
fprintf(stderr,“客户端:正在运行”);
myconnect();
fprintf(stderr,“客户端:退出”);
_出口(1);
}
int main()
{
信号(信号管、信号灯);
int-pid=fork();
开关(pid){
案例0:
运行_客户端();
打破
案例1:
佩罗尔(“福克”);
打破
违约:
运行_server();
打破
}
返回0;
}

您不能仅仅依赖于
write(2)
的返回值-这是连接两端之间的一个大竞争条件(内核缓存您提供给系统调用的数据,数据包需要花时间跨线等),因此在传输级连接中断的情况下,可能会导致数据丢失。如果您需要可靠性,请设计应用程序级协议,以便接收方确认所有数据


以前有一篇关于这类事情的好文章-,但现在似乎已经过时了。用谷歌搜索这个标题,它可能会在某个地方被镜像。

你不能仅仅依靠
write(2)
的返回值——这是连接两端之间的一个大竞争条件(内核缓存你给系统调用的数据,数据包需要时间穿越网络等等)因此,在传输级连接中断的情况下,引入了数据丢失的可能性。如果您需要可靠性,请设计应用程序级协议,以便接收方确认所有数据


以前有一篇关于这类事情的好文章-,但现在似乎已经过时了。用谷歌搜索该标题,它可能会在某个地方被镜像。

搜索页面缓存30分钟后,终于在找到它,这是我得到的最好的,希望与原始版本相同。搜索页面缓存30分钟后,终于在找到它,这是我得到的最好的,希望与原始版本相同。