问题描述
我用 C 编写了一个简单的 TCP 套接字服务器,它在接受来自客户端的新连接请求时创建一个新的子工作线程,并简单地计算和发送数字。如果客户端终止,相应的子工作线程也应该终止,而其他线程不应该终止。
如果所有客户端都是用 Python 编写的,当一个客户端终止时,服务器上会打印“Connection is reset by peer”,但其他一切都很好,即其他线程和客户端仍在工作。
但是如果客户端是用 C 编写的,当任何客户端和它们对应的子工作线程终止时,其他线程也会终止,这是意料之中的。为什么会发生?我用 Python 重写了服务器,但是无论客户端是用什么语言编写的,都没有发生这种情况。
然后我注释掉了 close(*client_fd);
,问题就解决了。我没有任何线索,因为它在使用 fork()
的服务器中运行良好。
服务器使用pthread
的C代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#define PORT 9000
#define CONNECTIONS 2
#define MAX_BUFFER_SIZE 1024
struct sockaddr_in socket_address;
socklen_t socket_address_size = sizeof(socket_address);
int server_fd;
void *handle_request(void *fd) {
int *client_fd = (int *) fd;
char buffer[MAX_BUFFER_SIZE] = {0};
for (int i = INT_MAX; send(*client_fd,buffer,strlen(buffer),0) >= 0 && i >= 0; i--) {
printf("%d\r\n",i);
sprintf(buffer,"%d",i);
}
if (close(*client_fd) < 0) {
perror("close client_fd");
}
return NULL;
}
int main(int argc,char *argv[]) {
int option = 1;
char buffer[MAX_BUFFER_SIZE] = {0};
if ((server_fd = socket(AF_INET,SOCK_STREAM,0)) == 0) {
perror("socket");
exit(EXIT_FAILURE);
}
if (setsockopt(server_fd,SOL_SOCKET,SO_REUSEADDR | SO_REUSEPORT,&option,sizeof(option))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
socket_address.sin_family = AF_INET;
socket_address.sin_addr.s_addr = htonl(INADDR_ANY);
socket_address.sin_port = htons(PORT);
if (bind(server_fd,(struct sockaddr *) &socket_address,socket_address_size) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
if (listen(server_fd,CONNECTIONS) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
pthread_t threads[CONNECTIONS];
int client_fds[CONNECTIONS];
for (int i = 0; i < CONNECTIONS; i++) {
client_fds[i] = accept(server_fd,&socket_address_size);
if (client_fds[i] < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
if (pthread_create(&threads[i],NULL,handle_request,&client_fds[i]) < 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
}
for (int i = 0; i < CONNECTIONS; i++) {
if (pthread_join(threads[i],NULL) < 0) {
perror("pthread_join");
}
}
close(server_fd);
return EXIT_SUCCESS;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#define PORT 9000
#define CONNECTIONS 2
#define MAX_BUFFER_SIZE 1024
int main(int argc,char *argv[]) {
struct sockaddr_in socket_address;
socklen_t socket_address_size = sizeof(socket_address);
int server_fd,client_fd,option = 1;
char buffer[MAX_BUFFER_SIZE] = {0};
if ((server_fd = socket(AF_INET,CONNECTIONS) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
pid_t pids[CONNECTIONS];
for (int i = 0; i < CONNECTIONS; i++) {
pids[i] = fork();
if (pids[i] < 0) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pids[i] == 0) {
if ((client_fd = accept(server_fd,&socket_address_size)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
for (int i = INT_MAX; send(client_fd,0) >= 0 && i >= 0; i--) {
printf("%d\r\n",i);
sprintf(buffer,i);
}
close(client_fd);
return EXIT_SUCCESS;
}
}
for (int i = 0; i < CONNECTIONS; i++) {
int wstatus;
if (waitpid(0,&wstatus,WUNTRACED) < 0) {
perror("waitpid");
}
}
close(server_fd);
return EXIT_SUCCESS;
}
客户端的C代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define IP_ADDRESS "127.0.0.1"
#define PORT 9000
#define MAX_BUFFER_SIZE 1024
int main(int argc,char *argv[]) {
int socket_fd;
struct sockaddr_in socket_address;
socklen_t socket_address_size = sizeof(socket_address);
ssize_t message_len;
char buffer[MAX_BUFFER_SIZE] = {0};
if ((socket_fd = socket(AF_INET,0)) == 0) {
perror("socket");
exit(EXIT_FAILURE);
}
socket_address.sin_family = AF_INET;
socket_address.sin_addr.s_addr = inet_addr(IP_ADDRESS);
socket_address.sin_port = htons(PORT);
if (connect(socket_fd,socket_address_size) < 0) {
perror("connect");
exit(EXIT_FAILURE);
}
while ((message_len = recv(socket_fd,MAX_BUFFER_SIZE,0)) > 0) {
buffer[message_len] = '\0';
puts(buffer);
}
close(socket_fd);
return EXIT_SUCCESS;
}
服务端的Python代码如下:
import socket
import threading
HOST = ''
PORT = 9000
CONNECTIONS = 2
TRUNK_SIZE = 1024
def handle_request(connection):
with connection:
count = 0
while True:
state = connection.send(f'{count}\r\n'.encode('utf-8'))
if not state:
print(f"Connection closed from {address}.")
break
print(count)
count += 1
with socket.socket(socket.AF_INET,socket.soCK_STREAM) as s:
s.setsockopt(socket.soL_SOCKET,socket.so_REUSEADDR | socket.so_REUSEPORT,1)
s.bind((HOST,PORT))
s.listen(CONNECTIONS)
threads = []
for c in range(CONNECTIONS):
connection,address = s.accept()
print(f'Connected by {address}.')
thread = threading.Thread(target=handle_request,args=(connection,),daemon=True)
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
客户端的Python代码如下:
import socket
HOST = '127.0.0.1'
PORT = 9000
TRUNK_SIZE = 1024
with socket.socket(socket.AF_INET,socket.soCK_STREAM) as s:
s.connect((HOST,PORT))
while True:
data = s.recv(TRUNK_SIZE).decode('utf-8')
if not data:
print("Connection closed.")
break
print(data)
解决方法
如果您的整个进程在写入套接字时突然停止,则是由于您收到了 SIGPIPE
信号(由错误 EPIPE 指示)。 SIGPIPE
的标准操作是终止进程。实现一个信号处理程序,它将捕获 SIGPIPE
信号(并且可能会忽略或处理它)。
“Connection is reset by peer”消息表示捕获到 SIGPIPE 信号。