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