gpt4 book ai didi

c - TCP 套接字,服务器无法响应客户端,接受 : Interrupted system call

转载 作者:可可西里 更新时间:2023-11-01 02:56:20 25 4
gpt4 key购买 nike

我正在尝试用 C 语言在 Solaris 上运行 TCP 服务器和客户端。我是套接字的新手,正在使用 Beej's Guide举个例子。

对于初学者,我希望客户端以word1 word2 的形式向服务器发送消息。收到消息后,我希望服务器从消息中提取 word2 并将其发送回客户端。

初始客户端 --> 服务器消息发送工作正常。但是服务器 --> 客户端响应不起作用。有几种失败症状:

  1. 服务器似乎甚至没有尝试向客户端send()任何东西。
  2. 服务器端收到客户端的消息后,打印:accept: Interrupted system call,然后返回到while(1)循环的顶部,一直保持到我Ctrl-C 退出。
  3. 客户端对 recv() 的调用返回 0 个字节。

我找到了一个旧线程 here ,最后一篇文章是这样说的:

accept is being interrupted by the child process sending a signal back to the parent when it terminates (SIGCHLD, if I remember write). You can either ignore SIGCHLD, or you can code accept() to handle the interrupt better (errno is set to EINTR)

但是,我不明白这一点。为什么子进程甚至在尝试 send() 部分之前就终止了? “更好地处理中断”是什么意思?

进一步搜索后,我在 Stack Overflow 上发现了这个问题:How to handle EINTR (interrupted System Call) .

我尝试在接受的答案中添加代码,将 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;
}

客户端代码:

#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;
}

服务器输出:

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

客户端输出:

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 ''

最后,我最终希望该服务器能够与可能的第二个或第三个客户端(目前,不关心并发服务的客户端)处理相同的双向事务。在这种情况下,我是否希望让 listen() 返回的 sockfd 保持打开状态?

最佳答案

带有 EINTR 的 accept() 调用“失败”是正常的。您的子进程结束,当前系统调用中断。即使您在 sigaction 标志中设置了 SA_RESTART,也会在一些系统调用中发生这种情况。你只是继续循环,尝试接受一个新的客户,所以一切正常。

您的子进程因崩溃而结束。

char *word1, *word2;
...

sscanf(tcp_recv_buf, "%s %s", word1, word2);

在这里,您尝试写入 word1word2,它们是未初始化的指针。

关于c - TCP 套接字,服务器无法响应客户端,接受 : Interrupted system call,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20067657/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com