C 服务器客户端 错误的文件描述符
C server client Bad file descriptor
我一直在弄清楚为什么下面的代码给出了一整天的错误描述符。下面是服务端代码,大部分参考了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
正在打印出子文件描述符的return值。这意味着循环 运行 一次,然后 return 出错。我的预期输出只是继续监听客户端的输入,服务器应该不断吐出客户端输入的内容。
我是这个服务器的新手,真的很头疼让它工作。任何帮助将不胜感激。
下面是客户端的代码,如果你感兴趣的话。
#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, '[=12=]', 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] = '[=12=]'; //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);
服务器端的子进程会尝试接受同一个fd。
如何在
之前添加一个无限循环
else if (pid == 0){ //child process
close(sockfd);//Child doesn't need this socket
memset(buffer, 0, 1000); //clear the buffer
for (;;) {
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);
}
}
}
我一直在弄清楚为什么下面的代码给出了一整天的错误描述符。下面是服务端代码,大部分参考了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
正在打印出子文件描述符的return值。这意味着循环 运行 一次,然后 return 出错。我的预期输出只是继续监听客户端的输入,服务器应该不断吐出客户端输入的内容。
我是这个服务器的新手,真的很头疼让它工作。任何帮助将不胜感激。
下面是客户端的代码,如果你感兴趣的话。
#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, '[=12=]', 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] = '[=12=]'; //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);
服务器端的子进程会尝试接受同一个fd。 如何在
之前添加一个无限循环 else if (pid == 0){ //child process
close(sockfd);//Child doesn't need this socket
memset(buffer, 0, 1000); //clear the buffer
for (;;) {
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);
}
}
}