ton*_*ger 1 c sockets signals tcp interrupt
我正在尝试在Solaris上运行的C语言中实现TCP服务器和客户端。我是套接字的新手,并以Beej指南为例。
对于初学者来说,我希望客户端以的形式向服务器发送消息word1 word2
。收到后,我希望服务器word2
从消息中提取并将其发送回客户端。
初始客户端->服务器消息发送工作正常。但是服务器->客户端响应不起作用。有几种故障症状:
send()
客户端执行任何操作。accept: Interrupted system call
,然后返回到while(1)
循环的顶部并保持在那里,直到我按Ctrl-C为止。recv()
返回0个字节。我在这里找到一个旧线程,最后一个帖子说:
子进程终止时,accept被子进程中断,它将信号发送回父进程(SIGCHLD,如果我还记得写的话)。您可以忽略SIGCHLD,也可以编写accept()以便更好地处理中断(errno设置为EINTR)
但是,我不明白这一点。为什么子进程在尝试该send()
部分之前就终止了?“更好地处理中断”是什么意思?
搜索更多内容后,我在堆栈溢出上发现了这个问题:如何处理EINTR(中断的系统调用)。
我尝试将代码添加到接受的答案中,替换write()
为send()
,但是我仍然看到相同的行为。
服务器代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/wait.h>
#include <signal.h>
const char* nodename = "localhost";
const char* FILESERV1_LISTEN_PORT = "41063";
const unsigned int MAXDATASIZE = 100;
void sigchld_handler(int s)
{
// from http://beej.us/guide/bgnet/examples/server.c
while(waitpid(-1, NULL, WNOHANG) > 0);
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
// from http://beej.us/guide/bgnet/examples/server.c
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
// from http://beej.us/guide/bgnet/examples/server.c
int rv;
const unsigned int BACKLOG = 3; // how many pending connections queue will hold
int tcp_sockfd, new_tcp_sockfd; // listen on tcp_sockfd, new connection on new_tcp_sockfd
struct addrinfo fs_hints, *fileservinfo, *p_fsinfo;
struct sockaddr_storage client_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
char yes='1'; // char for Solaris
char s[INET6_ADDRSTRLEN];
int tcp_numbytes, tcp_numbytes_written, size;
char tcp_recv_buf[MAXDATASIZE];
char *word1, *word2;
memset(&fs_hints, 0, sizeof fs_hints);
fs_hints.ai_family = AF_INET; // force IPv4
fs_hints.ai_socktype = SOCK_STREAM;
//fs_hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(nodename, FILESERV1_LISTEN_PORT, &fs_hints, &fileservinfo)) != 0) {
fprintf(stderr, "file_server1: getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p_fsinfo = fileservinfo; p_fsinfo != NULL; p_fsinfo = p_fsinfo->ai_next) {
if ((tcp_sockfd = socket(p_fsinfo->ai_family, p_fsinfo->ai_socktype,
p_fsinfo->ai_protocol)) == -1) {
perror("file_server1: socket");
continue;
}
if (setsockopt(tcp_sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -1) {
perror("file_server1: setsockopt");
exit(1);
}
if (bind(tcp_sockfd, p_fsinfo->ai_addr, p_fsinfo->ai_addrlen) == -1) {
close(tcp_sockfd);
perror("file_server1: bind");
continue;
}
break;
}
if (p_fsinfo == NULL) {
fprintf(stderr, "file_server1: failed to bind\n");
return 2;
}
freeaddrinfo(fileservinfo); // all done with this structure
if (listen(tcp_sockfd, BACKLOG) == -1) {
perror("file_server1: listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("file_server1: sigaction");
exit(1);
}
printf("DEBUG: file_server1: waiting for connections...\n");
//signal(SIGCHLD, SIG_IGN); /* now I don't have to wait()! */
while(1) { // main accept() loop
printf("DEBUG: Top of while loop\n");
sin_size = sizeof client_addr;
new_tcp_sockfd = accept(tcp_sockfd, (struct sockaddr *)&client_addr, &sin_size);
if (new_tcp_sockfd == -1) {
perror("file_server1: accept");
continue;
}
inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), s, sizeof s);
printf("DEBUG: file_server1: got connection from %s\n", s);
if (!fork()) { // this is the child process
printf("DEBUG: inside if\n");
close(tcp_sockfd); // child doesn't need the listener
if ((tcp_numbytes = recv(new_tcp_sockfd, tcp_recv_buf, MAXDATASIZE-1, 0)) == -1) {
perror("file_server1: recv");
exit(1);
}
tcp_recv_buf[tcp_numbytes] = '\0';
printf("DEBUG: file_server1: received: %s\n", tcp_recv_buf);
sscanf(tcp_recv_buf, "%s %s", word1, word2);
printf("DEBUG: file_server received word1: %s and word2: %s.\n", word1, word2);
size = strlen(word2);
while (size > 0) {
printf("DEBUG: top of inner while size\n");
tcp_numbytes_written = send(new_tcp_sockfd, word2, strlen(word2), 0);
if (tcp_numbytes_written == -1) {
if (errno == EINTR) {
printf("DEBUG: continuing after EINTR\n");
continue;
}
else {
printf("DEBUG: -1 on send(), not EINTR\n");
perror("DEBUG: file_server1: send");
return -1;
}
}
word2 += tcp_numbytes_written;
size -= tcp_numbytes_written;
printf("DEBUG: bottom of inner while size\n");
}
printf("DEBUG: file_server has sent %s to client.\n", word2);
close(new_tcp_sockfd);
//exit(0);
}
printf("DEBUG: outside of if\n");
close(new_tcp_sockfd); // parent doesn't need this
printf("DEBUG: Bottom of while loop\n");
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
客户代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
const char* nodename = "localhost";
const char* FILESERV1_LISTEN_PORT = "41063";
const unsigned int MAXDATASIZE = 100; // max number of bytes we can get at once
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
// http://beej.us/guide/bgnet/examples/client.c
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
// from http://beej.us/guide/bgnet/examples/client.c
int tcp_sockfd, tcp_numbytes;
char buf[MAXDATASIZE];
struct addrinfo fs_hints, *fileservinfo, *p_fsinfo;
struct sockaddr_storage my_addr; // for storing local dynamic port number
unsigned short int client_tcp_port;
char client_ipaddr_str[INET_ADDRSTRLEN];
socklen_t addrlen;
int rv;
int getsock_check;
char fs_ipaddr_str[INET6_ADDRSTRLEN];
char *msg_to_send = "abcdef hijklm"; // word1 and word2
memset(&fs_hints, 0, sizeof fs_hints);
fs_hints.ai_family = AF_INET; // IPv4
fs_hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(nodename, FILESERV1_LISTEN_PORT, &fs_hints, &fileservinfo)) != 0) {
fprintf(stderr, "client1: getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and connect to the first we can
for(p_fsinfo = fileservinfo; p_fsinfo != NULL; p_fsinfo = p_fsinfo->ai_next) {
if ((tcp_sockfd = socket(p_fsinfo->ai_family, p_fsinfo->ai_socktype, p_fsinfo->ai_protocol)) == -1) {
perror("client1: socket");
continue;
}
if (connect(tcp_sockfd, p_fsinfo->ai_addr, p_fsinfo->ai_addrlen) == -1) {
close(tcp_sockfd);
perror("client1: connect");
continue;
}
break;
}
printf("DEBUG: client1: socket and connect successful\n");
if (p_fsinfo == NULL) {
fprintf(stderr, "client1: failed to connect\n");
return 2;
}
addrlen = sizeof my_addr;
if ((getsock_check=getsockname(tcp_sockfd, (struct sockaddr *)&my_addr, (socklen_t *)&addrlen)) == -1) {
perror("client1: getsockname");
exit(1);
}
printf("DEBUG: client1: getsockname successful\n");
client_tcp_port = ntohs(((struct sockaddr_in *)&my_addr)->sin_port);
inet_ntop(AF_INET, &(((struct sockaddr_in *)&my_addr)->sin_addr), client_ipaddr_str, INET_ADDRSTRLEN);
printf("DEBUG: client1 has dynamic TCP port number %hu and IP address %s.\n", client_tcp_port, client_ipaddr_str);
inet_ntop(p_fsinfo->ai_family, get_in_addr((struct sockaddr *)p_fsinfo->ai_addr), fs_ipaddr_str, sizeof fs_ipaddr_str);
printf("DEBUG: client1: connecting to %s\n", fs_ipaddr_str);
freeaddrinfo(fileservinfo); // all done with this structure
if (send(tcp_sockfd, msg_to_send, strlen(msg_to_send), 0) == -1) {
perror("client1: send");
exit(1);
}
printf("DEBUG: The request from client1 has been sent to the file_server1\n");
if ((tcp_numbytes = recv(tcp_sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
perror("client1: recv");
exit(1);
}
buf[tcp_numbytes] = '\0';
printf("DEBUG: client1: received %d bytes, content '%s'\n", tcp_numbytes, buf);
close(tcp_sockfd);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
服务器的输出:
DEBUG: file_server1: waiting for connections...
DEBUG: Top of while loop
DEBUG: file_server1: got connection from 127.0.0.1
DEBUG: outside of if
DEBUG: Bottom of while loop
DEBUG: Top of while loop
DEBUG: inside if
DEBUG: file_server1: received: abcdef hijklm
file_server1: accept: Interrupted system call
DEBUG: Top of while loop
Run Code Online (Sandbox Code Playgroud)
客户的输出:
DEBUG: client1: socket and connect successful
DEBUG: client1: getsockname successful
DEBUG: client1 has dynamic TCP port number 51196 and IP address 127.0.0.1.
DEBUG: client1: connecting to 127.0.0.1
DEBUG: The request from client1 has been sent to the file_server1
DEBUG: client1: received 0 bytes, content ''
Run Code Online (Sandbox Code Playgroud)
最后,我最终希望该服务器与可能的第二个或第三个客户端(目前,不考虑同时服务的客户端)处理相同的双向交易。listen()
在这种情况下,我是否要保持打开状态返回的sockfd ?
使用EINTR的accept()调用“失败”是正常的。您的子进程已结束,当前系统调用被中断。即使您SA_RESTART
在sigaction标志中进行设置,这种情况也会在少数系统调用中发生。您只要继续循环,尝试接受新客户,就可以了。
您的子进程由于崩溃而结束。
char *word1, *word2;
...
sscanf(tcp_recv_buf, "%s %s", word1, word2);
Run Code Online (Sandbox Code Playgroud)
在这里,您尝试写入word1
和word2
,它们是未初始化的指针。