我遇到了一个问题,即使在我的服务器端关闭() - 编辑套接字后,客户端仍然可以执行单个write(),只有第二个write()将返回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;
}
答案 0 :(得分:4)
你不能只依赖write(2)
的返回值 - 这是连接两端之间的一个大竞争条件(内核缓存你给系统调用的数据,数据包需要时间来通过网络,等等。)因此在传输级连接拆除的情况下引入了数据丢失的可能性。如果您需要可靠性,请设计应用程序级协议,以便接收方确认所有数据。
过去有一篇关于类似这样的文章的好文章 - The ultimate SO_LINGER page or why is my TCP not reliable,但现在似乎已经失败了。谷歌这个称号,可能会在某个地方反映出来。