我有一个服务器仅连接到客户端,然后断开连接,而客户端则尝试将一个整数发送到关闭的套接字(scanf是为了确保服务器首先将其关闭)。我将send与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;
}
客户代码:
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;
}
注意:致命和Syserr仅用于报告错误
答案 0 :(得分:3)
这就是TCP的工作方式。当服务器关闭套接字时,FIN将发送到客户端。仅发出 信号,表示服务器将不再发送任何数据。这不一定意味着它不想接收更多数据。
因此,客户端可以在套接字上调用send()
,而操作系统不会报告错误。如果服务器确实关闭了整个套接字,则它将发送 TCP重置数据包作为对表示该情况的传入数据的响应。现在,将来在套接字上执行的操作(写/关闭)将指示错误。
对于服务器(或任何对等方)而言,确实有可能仅通过syscall shutdown()
中途关闭连接(读取或写入侧)。如果服务器关闭用于写入的连接,则网络上发生的事情与服务器使用close()
关闭整个连接一样。确定何时应断开每一端的连接是高层协议的职责。
如果要确保对等方确实确认了您发送的所有数据,则可以使用SO_LINGER
套接字选项。但是一种更常见的方法是,确保将此作为通信协议的一部分,即,一部分要求在更高级别上关闭连接(例如,smtp QUIT
命令),而对等方对此做出反应通过关闭tcp连接。