gpt4 book ai didi

C 服务器客户端 错误的文件描述符

转载 作者:太空宇宙 更新时间:2023-11-04 01:22:21 25 4
gpt4 key购买 nike

我一直在弄清楚为什么下面的代码给出了一整天的错误描述符。下面是服务器代码,大部分引用了 Beej 的指南。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>

#include "CountryData.c"

//This function determine if address is IPv4 or IPv6 IP address
void *getAddr_Type(struct sockaddr *sa) {
if (sa->sa_family == AF_INET) { //If IPv4
return &(((struct sockaddr_in*)sa)->sin_addr);
}
else //if IPv6
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

void sigchld_handler(int s)
{
// waitpid() might overwrite errno, so it is stored in a variable first:
int saved_errno = errno;

while(waitpid(-1, NULL, WNOHANG) > 0);

errno = saved_errno;
}


int main(void){

readData(); //Read from CountryData.c

int status, sockfd, client_sockfd;
int pid; //fork return value

char buffer[1000];
int bytecount;

struct addrinfo hints, *res, *serverInfo; //res points to linked list of "struct addrinfo"; serverInfo also points to linked list of "struct addrinfo" for use in for loop.
struct sockaddr_storage client_addr; //Address information of the client
struct sigaction sa;

socklen_t address_size; //Initialize size of address

char i[INET6_ADDRSTRLEN]; //INET6_ADDRSTRLEN macro is used to store maximum length of IPv6. Since IPv4 is definitely shorter than IPv6, "INET_ADDRSTRLEN" is not used.


memset(&hints, 0, sizeof(hints)); //emptying the structure

//Pass in value into "addrinfo" struct
hints.ai_family = AF_INET; //Using IPv4
hints.ai_socktype = SOCK_STREAM; //Using TCP
hints.ai_flags = AI_PASSIVE; //AI_PASSIVE = Own IP address

status = getaddrinfo(NULL, "8888", &hints, &serverInfo); //Initialising status return value and also passing in values to getaddrinfo().
//IP address is set to null. This will be filled in automatically by AI_PASSIVE.

if (status != 0) {
fprintf(stderr, "Error: %s\n", gai_strerror(status));//gai_strerror to print human readable error
exit(1);
}


//Loop through all results and bind to the first
for (res = serverInfo; res != NULL; res = res->ai_next) {

//(1)Initializing socket
if ((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) { //Show error message if initializing socket file descriptor fails
perror("Socket");
continue;
}

int optValue=1;

if ((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optValue, sizeof(int))) == -1) {
perror("Socket options");
exit(1);
} //(2)setting socket options. "SO_REUSEADDR" to prevent "Address already in use" and to allow reuse of the port

if ((bind(sockfd, res->ai_addr, res->ai_addrlen)) == -1) { //(3) Binding to local address and port
close(sockfd);
perror("Bind");
continue;
}

break;
}
freeaddrinfo(serverInfo); //Freeing "serverInfo" linked list


//However, if linked list is still empty, print error.
if (res == NULL) {
fprintf(stderr, "Error! Server is unable to bind.\n");
exit(1);
}

//If unable to listen, print error.
if ((listen(sockfd, 8)) == -1) { //(4) Listen for client connections, maximum of 8 waiting in queue
perror("Listen");
}

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("sigaction");
exit(1);
}


printf("Running server program 'server' ... \n\n\nCountry Directory Server Started! PID: %d\n", getpid());



for(;;) //infinite loop for server to wait for client requests
{
memset(buffer, 0, 1000);
address_size = sizeof(client_addr);
client_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &address_size);

if (client_sockfd == -1) {
perror("Accept");
close(client_sockfd);
exit(1);
}

inet_ntop(client_addr.ss_family, getAddr_Type((struct sockaddr *)&client_addr), i, sizeof(i));//retrieving IP address. "inet_ntop" is used for IPv6 compatibility.


printf("-------------------------------------------------------\n");
printf("Connection received from: %s\n\n", i);


if ((pid = fork()) == -1){ //Starts forking
perror("Failed to fork");
close(sockfd);
}

else if (pid == 0){ //child process

close(sockfd);//Child doesn't need this socket
memset(buffer, 0, 1000); //clear the buffer

if ((bytecount = recv(client_sockfd, buffer, 1000, 0)) == -1){//Receiving Client's input
perror("Server unable to receive");
close(client_sockfd);
exit(0);
}
else if ((strcasecmp(buffer, "END")) == 0){ //Nested If-statement; If client sends "end"
close(client_sockfd);
exit(0);
break;
}

else if (bytecount == 0) { //If "recv" returns 0, client has closed the connection
printf("Client (%d) has closed the connection.\n", getpid());
close(client_sockfd);
exit(0);
break;
}else {
printf("%s", buffer);
printf("%d", client_sockfd);
}
}

close(client_sockfd);

} //end of infinite while loop

}//End of main function

它成功读取客户端的输入并在第一个 for(;;) 循环的屏幕上打印出来。第二次迭代后,它显示 Bad file descriptor以下是在客户端输入 Hi 后服务器终端的输出。

Johnny$ server
Running server program 'server' ...


Country Directory Server Started! PID: 18386
-------------------------------------------------------
Connection received from: 127.0.0.1

Accept: Bad file descriptor
Hi4

数字 4 打印出子文件描述符的返回值。这意味着循环运行一次,然后返回一个错误。我的预期输出只是继续监听客户端的输入,服务器应该不断吐出客户端输入的内容。我是这个服务器的新手,真的很头疼让它工作。任何帮助将不胜感激。

下面是客户端的代码,如果你感兴趣的话。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>

void welcome()
{
printf("\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
printf(" Welcome to the Country Info Directory Service! \n");
printf(" ---------------------------------------------- \n");
printf("Usage :\n\n");
printf("1) At the '>' prompt, type in the name of the country\n");
printf(" you wish to search\n\n");
printf("2) To end program, type in 'end'\n");
printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n");
}

//This function determine if address is IPv4 or IPv6 IP address
void *getAddr_Type(struct sockaddr *sa) {
if (sa->sa_family == AF_INET) { //If IPv4
return &(((struct sockaddr_in*)sa)->sin_addr);
}
else //if IPv6
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(int argc, char *argv[]) {
int sockfd, numOfBytes;
int retrieveInfo;
char buf[100];
struct addrinfo hints, *res, *serverInfo;

char i[INET6_ADDRSTRLEN]; //INET6_ADDRSTRLEN macro is used to store maximum length of IPv6. Since IPv4 is definitely shorter than IPv6, "INET_ADDRSTRLEN" is not used.

if (argc != 2){
printf("Please enter in this format:\n'client':server-host-name\nFor example, if your hostname is vmwubuntu, please type:\nclient vmwubuntu [Enter]\n\n");
exit(1);
}

welcome();

memset(&hints, 0, sizeof(hints)); //emptying the structure

//Pass in value into "addrinfo" struct
hints.ai_family = AF_INET; //Using IPv4
hints.ai_socktype = SOCK_STREAM; //Using TCP

retrieveInfo = getaddrinfo(argv[1], "8888", &hints, &serverInfo);

if(retrieveInfo != 0) {
printf("Fail to retrieve address!");
}

//Loop through all results and bind to the first
for (res = serverInfo; res != NULL; res = res->ai_next) {

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); //(1)Initializing socket

if (sockfd == -1) { //Show error message if initializing socket file descriptor fails
perror("Socket");
continue;
}

if (connect(sockfd, res->ai_addr, res->ai_addrlen) == -1) { //Retrieving values from "struct addrinfo" through "res" pointer
//close(sockfd);
perror("Connection");
continue;
}
break;
}

if (res == NULL) {
printf("Failed to connect to server\n");
exit(1);
}

char input[1000];
char receive[1000];

for(;;)
{
memset(input, '\0', 1000); //Initialize buffer size to store user input
printf("Enter Country > ");
fgets(input, 1000, stdin); //Take in user input with 1000 as the buffer size
input[strlen(input) - 1] = '\0'; //Stripping the null terminator away

if (strcasecmp(input, "END") == 0){ //If user enters "end"(case is ignored), close the file descriptor and exit
close(sockfd);
exit(0);
}

else {//SEND
if((numOfBytes = send(sockfd, input, strlen(input), 0)) == -1){ //start of nested if statement
perror("Unable to send");
exit(1);
}
else if (numOfBytes != strlen(input)){ //If string is not sent in full
perror("Send");
close(sockfd);
exit(1);
}else{//for testing purposes
printf("%d\n",strlen(input));//for testing purpose
printf("%d\n", numOfBytes); //for testing purpose
}//End of nested if statement

}

}//End of for infinite loop
} //End of main()

最佳答案

您的子进程似乎没有退出,而是继续执行与父进程相同的代码。然后,您尝试使用关闭的文件描述符调用 accept

这就是为什么我总是将子代码放在它自己的函数中,并且总是紧随其后调用 _exit()。请注意,我使用 _exit() 而不是 exit() 来确保没有父 atexit 处理程序被执行。

此外,在您的日志消息中包含 PID 也很有帮助。尝试使用这样的东西:

#define INFO(fmt, ...)    fprintf(stderr, "[%d] %s" fmt, getpid(), __FUNCTION__, __VA_ARGS__)

...
INFO("x=%d\n", x);

关于C 服务器客户端 错误的文件描述符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39075623/

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