我正在尝试使用C中的套接字来学习网络通信的基本知识 . 我的客户端程序接收来自用户的消息,回显服务器端和后端,并打印出收到的消息 . 当我第一次点燃它们时,它们都完全按照预期工作 . 但是,如果我退出客户端,然后在保持服务器程序运行的同时再次启动它,我的回显消息将被一个人关闭 .
我以为这是因为最后一条消息被管道或其他东西 grab 了,在探索之后,我看到有人建议使用 shutdown()
来清除管道,但这似乎也没有帮助 .
server.c
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#define PORT 12403
#define BUFFER_MAX 1024
#define BACKLOG_MAX 1024
int clientSocket;
int serverSocket;
void listening()
{
while (1)
{
struct sockaddr_in clientAddress;
socklen_t addressLength = sizeof(clientAddress);
/*---accept a connection (creating a data pipe)---*/
clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &addressLength);
if (clientSocket > -1)
{
printf("%s:%d connected\n", inet_ntoa(clientAddress.sin_addr), ntohs(clientAddress.sin_port));
break;
}
}
}
int main(int Count, char *Strings[])
{
struct sockaddr_in socketInfo;
char buffer[BUFFER_MAX];
//Create socket
if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Error creating socket");
exit(errno);
}
//Setting the linger option to off and resuse address option to on for testing
int option = 0;
setsockopt(serverSocket, SOL_SOCKET, SO_LINGER, &option, sizeof(option));
option = 1;
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
//Initialize socket information
bzero(&socketInfo, sizeof(socketInfo));
socketInfo.sin_family = AF_INET;
socketInfo.sin_port = htons(PORT);
socketInfo.sin_addr.s_addr = INADDR_ANY;
//Assign a port number to the socket
if (bind(serverSocket, (struct sockaddr*)&socketInfo, sizeof(socketInfo)) != 0)
{
perror("Error binding socket");
exit(errno);
}
//Set socket to listen
if (listen(serverSocket, BACKLOG_MAX) != 0)
{
perror("Error setting socket to listen");
exit(errno);
}
listening();
//Once first socket has been connected, begin echoing process
int i = 0;
while (1)
{
//Clear the buffer
bzero(buffer, BUFFER_MAX);
//Echo back anything sent
//Close connection and begin listening process again if the client disconnects
int sendCheck;
int readCheck;
readCheck = recv(clientSocket, buffer, BUFFER_MAX, 0);
if (readCheck <= 0)
{
shutdown(clientSocket, SHUT_WR);
close(clientSocket);
sleep(1);
listening();
}
sendCheck = send(clientSocket, buffer, BUFFER_MAX, 0);
if (sendCheck <= 0)
{
shutdown(clientSocket, SHUT_WR);
close(clientSocket);
sleep(1);
listening();
}
i++;
}
close(serverSocket);
return 0;
}
client.c
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdlib.h>
#include <resolv.h>
#include <netdb.h>
#define PORT 12403
#define LOCALHOST "127.0.0.1"
#define BUFFER_MAX 1024
int socketStatus = 0;
void sigpipeHandler()
{
perror("Connection to server terminated\n");
socketStatus = 0;
}
int main()
{
int mySocket;
struct sockaddr_in socketInfo;
char buffer[BUFFER_MAX];
int count = 0;
//Create socket
if ((mySocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Error creating socket");
exit(errno);
}
//Get IP address of required host machine
char* hostName = "<host name removed>";
int portNumber = PORT;
char* ipAddr = NULL;
struct hostent* host = NULL;
host = gethostbyname(hostName);
ipAddr = inet_ntoa(*((struct in_addr*) host->h_addr_list[0]));
//Initialize server information
bzero(&socketInfo, sizeof(socketInfo));
socketInfo.sin_family = AF_INET;
socketInfo.sin_port = htons(portNumber);
if (inet_aton(ipAddr, (struct in_addr *)&socketInfo.sin_addr.s_addr) == 0)
{
perror("Error assigning IP address");
exit(errno);
}
//Set up sigpipe handler
signal(SIGPIPE, sigpipeHandler);
//Connect to server
if (connect(mySocket, (struct sockaddr*)&socketInfo, sizeof(socketInfo)) != 0)
{
perror("Error connecting");
exit(errno);
}
//Indicate that socket is OK
socketStatus = 1;
while(1)
{
if(!socketStatus) {shutdown(mySocket, SHUT_WR); break;}
printf("Please enter a command.\n");
char command[BUFFER_MAX];
bzero(command, BUFFER_MAX);
fgets(command, sizeof(command), stdin);
send(mySocket, command, BUFFER_MAX, 0);
//Get echoed message
bzero(buffer, BUFFER_MAX);
recv(mySocket, buffer, sizeof(buffer), 0);
printf("Echo [%d]:%s\n", ++count, buffer);
}
//Close socket
close(mySocket);
return 0;
}
1 回答
我对你的服务器代码进行了一些清理,这似乎有效 .
对于我的测试,客户端代码保持不变 . 但是,正如其他人所建议的那样,您应该检查
send
和recv
中的错误代码 . 另外,请注意,如果你_99927_服务器,客户端将挂在fgets
,所以它赢了't detect the server abort until you hit return after the prompt. Not a big deal, but I thought I' d提到它 .我还添加了
fork
,因此您可以让多个客户端同时与同一服务器实例通信 .我用两个客户端[在两个
xterm
窗口]与单个服务器实例进行交谈测试了这个 .我将你的echo代码移动到一个新函数
docomm
中 . 与您的代码的一个小差别是,recv
或send
中的任何错误都会突破循环并关闭连接 . 来自新客户端的所有连接都保证以recv
调用开始 .在您的代码中,您不会总是突破循环,但关闭连接并再次调用
listening
. 这将发生在send
或recv
. 如果它发生在错误的一个上,这可能是您遇到的问题的根源,因为您最初可以在recv
之前对新客户端进行send
.UPDATE:
我使用了
forkflg
,因此您可以将其设置为零(例如int forkflg = 0;
)以按顺序运行 . 或者,您可以添加一些代码并解析argv
寻找一个选项(例如-f
)来设置/清除它[用于测试/调试目的] . 对于 生产环境 代码,您需要设置forkflg
并且可以删除该标志,并且始终执行fork情况[调整代码以匹配] .是的,这是真的 .
你所描述的是
forkflg
为零时的行为 .如果设置
forkflg
,则调用fork
. 请注意,在这种情况下,在子节点中调用docomm
而不是父节点(因为fork
返回0) . 因此,当孩子进行回声时,父母不会被阻止 .因此,父进程立即返回并可以自由地执行
waitpid
循环以收集任何旧子进程并重新启动主/外循环 .waitpid
循环仅在新连接进入时发生,因此几个孩子可能已经终止并将保持僵尸状态,直到waitpid
循环被执行[这将收获任何/多个未决子项] .收获子项的一种更简洁的方法可能是为
SIGCHLD
设置一个信号处理程序并让它执行waitpid
循环 . 这将立即收获所有用过的孩子,而不必等待新的连接进入 .或者,使用信号处理程序,将
waitpid
循环添加到listening
[在当前循环内],因为如果SIGCHLD
信号进入,accept
将立即返回,errno
设置为EINTR