Я выполнил некоторую очистку кода вашего сервера, и, похоже, это работает.
Для моего тестирования код клиента не изменился.Но, как другие предложили, вы должны проверить коды ошибок от send
и recv
.Также обратите внимание, что если вы ctrl-c
сервер, клиент будет зависать в fgets
, поэтому он не обнаружит прерывание работы сервера, пока вы не нажмете return после запроса.Ничего страшного, но я подумал, что упомяну это.
Я также добавил fork
, чтобы вы могли иметь несколько клиентов, говорящих с одним экземпляром сервера одновременно.
Я проверил это с двумя клиентами [в двух xterm
окнах], разговаривающими с одним экземпляром сервера.
Я переместил ваш эхо-код в новую функцию docomm
.Небольшое отличие от вашего кода в том, что любая ошибка из recv
или send
прерывается и закрывает соединение.Все подключения от новых клиентов гарантированы для начала с recv
звонком.
В вашем коде вы бы не всегда разрывалицикла, но закройте соединение и снова наберите listening
.Это произойдет для или send
или recv
.Если это произошло на неверном , это может быть источником проблемы, с которой вы столкнулись, потому что вы могли сделать send
до a recv
новый клиент изначально.
#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>
#include <sys/wait.h>
#define PORT 12403
#define BUFFER_MAX 1024
#define BACKLOG_MAX 1024
int clientSocket;
int serverSocket;
int forkflg = 1;
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;
}
}
}
void
docomm(void)
{
char buffer[BUFFER_MAX];
//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)
break;
sendCheck = send(clientSocket, buffer, BUFFER_MAX, 0);
if (sendCheck <= 0)
break;
i++;
}
printf("close\n");
shutdown(clientSocket, SHUT_WR);
close(clientSocket);
}
int main(int Count, char *Strings[])
{
struct sockaddr_in socketInfo;
//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);
}
while (1) {
listening();
if (! forkflg) {
docomm();
continue;
}
pid_t pid = fork();
if (pid == 0) {
docomm();
exit(0);
}
while (waitpid(0,NULL,WNOHANG) > 0);
}
close(serverSocket);
return 0;
}
ОБНОВЛЕНИЕ:
Всего с одного взгляда: 1) Могу я спросить, почему вы создали флаг форка, если вы никогда не меняете его значение?Должно ли оно быть где-то изменено?
Я использовал forkflg
, чтобы вы могли установить его на ноль (например, int forkflg = 0;
) для последовательной работы.Или вы можете добавить некоторый код и разобрать argv
в поисках опции (например, -f
), чтобы установить / очистить его [для целей тестирования / отладки].Для производственного кода вы бы хотели, чтобы был установлен forkflg
и могли бы убрать флаг, и всегда делайте случай с вилкой [корректируя код для соответствия].
Просто мысленно отслеживая программу,похоже, что раздел разветвления никогда не будет выполнен.Поправьте меня, где я ошибаюсь: после первоначальной установки сокета на прослушивание, включится цикл while и будет вызвана listen ().Выполнение будет приостановлено в listen () до тех пор, пока соединение не будет принято.
Да, это правда.
Элемент управления вернется в main, где вызывается docomm ().Элемент управления остается в docomm () до тех пор, пока не будет разорвано соединение, после чего он вернется к основному и будет вызван метод continue, пропуская содержимое форка и снова начав процесс.Так выполняется ли разветвление?
Что вы описываете, это поведение, если forkflg
равно нулю.
fork
называется , если forkflg
установлено.Обратите внимание, что в этом случае docomm
вызывается в child и не родительском (потому что fork
вернул 0).Таким образом, родитель будет не заблокирован, пока ребенок выполняет эхо.
Таким образом, родитель немедленно возвращается и может свободно делатьцикл waitpid
, чтобы пожинать всех старых детей и перезапускать основной / внешний цикл.
Цикл waitpid
происходит только при установлении нового соединения, поэтому несколько детей уже могут прерваться и останутся в зомбисостояние до тех пор, пока не будет выполнен цикл waitpid
[который будет пожинать все / несколько ожидающих потомков].
Более чистый способ пожинать потомки может быть установить обработчик сигнала для SIGCHLD
и пусть это сделает цикл waitpid
.Это сразу же пожнет все потраченные дети, без необходимости ждать, пока не установится новое соединение.
Или, с помощью обработчика сигнала, добавить цикл waitpid
к listening
[внутри токовой петли], потому что, если поступит сигнал SIGCHLD
, accept
немедленно вернется с errno
, установленным в EINTR