Я пытаюсь создать свой собственный чат-сервер, который будет отправлять и получать сообщения одновременно, используя epoll (на стороне сервера) и выбирать (на стороне клиента).
Программа компилируется успешно, но не работает какЯ ожидал, что клиентский код возвращает сообщение «Ошибка сервера отключена», что не должно происходить в соответствии с тем, что я пытался построить, и в соответствии с возвращаемым значением функции recv.
Я прилагаю свой код клиента здесь.
#define _DEFAULT_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#define NUM_FD 2
#define STDIN 0
#define PORT 50000
#define IP_ADDRESS "127.0.0.1"
#define EXIT "exit"
#define BUFFER_SIZE 256
typedef struct timeval timeval;
typedef struct sockaddr_in sockaddr_in;
typedef struct sockaddr sockaddr;
int main(int argc, char* argv[])
{
int maxFd, retval, fd[NUM_FD];
int indx = 0;
int exitFlag = 0;
char buffer[BUFFER_SIZE] = {0};
char receiveBuffer[BUFFER_SIZE] = {0};
timeval tv;
fd_set readfds;
sockaddr_in address = {0};
tv.tv_sec = 3;
tv.tv_usec = 500000;
fd[0] = STDIN;
fd[1] = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == fd[1])
{
perror("Socket err");
return errno;
}
address.sin_family = AF_INET;
address.sin_port = PORT;
retval = inet_aton(IP_ADDRESS, &address.sin_addr);
if (!retval)
{
perror("Inet aton err");
return errno;
}
retval = connect(fd[1], (sockaddr*)&address, sizeof(address));
if (-1 == retval)
{
perror("Connect err");
return errno;
}
while (!exitFlag)
{
maxFd = -1;
FD_ZERO(&readfds);
for (indx = 0; indx < NUM_FD; ++indx)
{
FD_SET(fd[indx], &readfds);
if (fd[indx] > maxFd)
{
maxFd = fd[indx];
}
}
retval = select(maxFd + 1, &readfds, NULL, NULL, &tv);
if (-1 == retval)
{
perror("Select err");
return errno;
}
for (indx = 0; indx < NUM_FD; ++indx)
{
if (FD_ISSET(fd[indx], &readfds))
{
if (0 == indx)
{
fgets(buffer, BUFFER_SIZE - 1, stdin);
retval = send(fd[1], buffer, strnlen(buffer, BUFFER_SIZE), 0);
if (-1 == retval)
{
perror("Send err");
return errno;
}
if (0 == strcmp(buffer, EXIT))
{
printf("Client %s has disconnected\n", inet_ntoa(address.sin_addr));
exitFlag = 1;
break;
}
}
else
{
retval = recv(fd[1], receiveBuffer, BUFFER_SIZE, 0);
if (!retval)
{
printf("Server disconnected err\n");
exitFlag = 1;
break;
}
/*else if (-1 == retval)
{
perror("Send err");
return errno;
}*/
else
{
printf("%s\n", receiveBuffer);
receiveBuffer[0] = '\0';
}
}
}
}
}
close(fd[1]);
return 0;
}
это код сервера
#define _DEFAULT_SOURCE /* since glibc 2.19 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "Chat.h"
#define BACK_LOG 2
#define ZERO 0
#define MAX_EVENTS 7
#define AVAILABLE -1
typedef struct sockaddr_in sockaddr_in;
typedef struct sockaddr sockaddr;
typedef struct epoll_event epoll_event;
void PrintErr(int _line, int _err, char* _msg);
int Initialization(int* _socketFd, int* _epollFd, int* _dataB, sockaddr_in* _serverAddress);
void InsertIntoDB(int* _dataB, int _fd);
void RemoveFromDB(int* _dataB, int _fd);
int Broadcast(int* _dataB, char* _buffer, sockaddr_in* _clientAddress, int _fd);
int main(int argc, char* argv[])
{
int epollFd, socketFd, nFdReady, connectionFd, indx = 0;
int retval;
int exitFlag = 0;
int nConnected = 0;
int nBytes = 0;
int dataB[MAX_EVENTS];
unsigned int length;
char buffer[BUFFER_SIZE];
epoll_event ev, events[MAX_EVENTS];
sockaddr_in serverAddress = {0};
sockaddr_in clientAddress = {0};
retval = Initialization(&socketFd, &epollFd, dataB, &serverAddress);
if (retval)
{
return retval;
}
for (;!exitFlag;)
{
nFdReady = epoll_wait(epollFd, events, MAX_EVENTS, -1);
if (-1 == nFdReady)
{
PrintErr(__LINE__, errno, "Epoll wait err");
return errno;
}
for (indx = 0; indx < nFdReady; ++indx)
{
if (events[indx].data.fd == socketFd) /* server */
{
length = sizeof(clientAddress);
connectionFd = accept(socketFd, (sockaddr*)&clientAddress, &length);
if (-1 == connectionFd)
{
PrintErr(__LINE__, errno, "Accept err");
return errno;
}
else
{
++nConnected;
if (nConnected > MAX_EVENTS - 1)
{
printf("No place for clients\n");
strncpy(buffer, CANNOT_RECEIVE, sizeof(CANNOT_RECEIVE));
nBytes = sendto(connectionFd, &buffer, 50, MSG_NOSIGNAL, (sockaddr*)&clientAddress, sizeof(clientAddress));
if (-1 == nBytes && EPIPE == errno)
{
printf("Client has finished the connection\n");
}
else if (-1 == nBytes)
{
PrintErr(__LINE__, errno, "Sendto err");
return errno;
}
close(connectionFd);
--nConnected;
buffer[0] = '\0';
}
else
{
printf("Client %s has connected", inet_ntoa(clientAddress.sin_addr));
ev.events = EPOLLIN;
ev.data.fd = connectionFd;
retval = epoll_ctl(epollFd, EPOLL_CTL_ADD, connectionFd, &ev);
if (-1 == retval)
{
PrintErr(__LINE__, errno, "Epoll control err");
return errno;
}
InsertIntoDB(dataB, connectionFd);
}
}
}
else /* client */
{
length = sizeof(clientAddress);
nBytes = recvfrom(events[indx].data.fd, buffer, BUFFER_SIZE, 0, (sockaddr*)&clientAddress, &length);
if (0 < nBytes)
{
if (0 == strcmp(buffer, EXIT))
{
sprintf(buffer, "Client %s has disconnected\n", inet_ntoa(clientAddress.sin_addr));
Broadcast(dataB, buffer, &clientAddress, events[indx].data.fd);
ev.events = 0;
ev.data.fd = events[indx].data.fd;
retval = epoll_ctl(epollFd, EPOLL_CTL_DEL, events[indx].data.fd, &ev);
if (-1 == retval)
{
PrintErr(__LINE__, errno, "Epoll control err");
return errno;
}
RemoveFromDB(dataB, events[indx].data.fd);
--nConnected;
if (0 == nConnected)
{
exitFlag = 1;
break;
}
}
else
{
retval = Broadcast(dataB, buffer, &clientAddress, events[indx].data.fd);
if (retval)
{
return retval;
}
}
}
else if (0 == nBytes)
{
printf("No new messages");
}
else
{
PrintErr(__LINE__, errno, "Receive from err");
return errno;
}
}
}
for (indx = 0; indx < MAX_EVENTS; ++indx)
{
if (dataB[indx] != AVAILABLE)
{
close(dataB[indx]);
}
}
}
return 0;
}
void PrintErr(int _line, int _err, char* _msg)
{
printf("In line: %d error number: %d ", _line, _err);
perror(_msg);
printf("\n");
}
int Initialization(int* _socketFd, int* _epollFd, int* _dataB, sockaddr_in* _serverAddress)
{
int retval, indx = 0;
epoll_event ev;
*_socketFd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == *_socketFd)
{
PrintErr(__LINE__, errno, "Socket create err");
return errno;
}
_serverAddress->sin_family = AF_INET;
_serverAddress->sin_port = SERVER_PORT;
_serverAddress->sin_addr.s_addr = htons(INADDR_ANY);
retval = bind(*_socketFd, (sockaddr*)_serverAddress, sizeof(*_serverAddress));
if (-1 == retval)
{
PrintErr(__LINE__, errno, "Bind err");
return errno;
}
retval = listen(*_socketFd, BACK_LOG);
if (-1 == retval)
{
PrintErr(__LINE__, errno, "Listen err");
return errno;
}
*_epollFd = epoll_create1(0);
if (-1 == *_epollFd)
{
PrintErr(__LINE__, errno, "Epoll create err");
return errno;
}
ev.events = EPOLLIN;
ev.data.fd = *_socketFd;
retval = epoll_ctl(*_epollFd, EPOLL_CTL_ADD, *_socketFd, &ev);
if (-1 == retval)
{
PrintErr(__LINE__, errno, "Epoll control err");
return errno;
}
for (indx = 0; indx < MAX_EVENTS; ++indx)
{
_dataB[indx] = AVAILABLE;
}
return 0;
}
void InsertIntoDB(int* _dataB, int _fd)
{
int indx = 0;
for (; indx < MAX_EVENTS; ++indx)
{
if (AVAILABLE == _dataB[indx])
{
_dataB[indx] = _fd;
return;
}
}
}
void RemoveFromDB(int* _dataB, int _fd)
{
int indx = 0;
for (; indx < MAX_EVENTS; ++indx)
{
if (_fd == _dataB[indx])
{
_dataB[indx] = AVAILABLE;
return;
}
}
}
int Broadcast(int* _dataB, char* _buffer, sockaddr_in* _clientAddress, int _fd)
{
char printBuffer[BUFFER_SIZE];
int nBytes, indx = 0;
nBytes = sprintf(printBuffer, "Client %s says: %s", inet_ntoa(_clientAddress->sin_addr), _buffer);
if (-1 == nBytes)
{
PrintErr(__LINE__, errno, "Sprintf err\n");
return errno;
}
for (; indx < MAX_EVENTS; ++indx)
{
if (AVAILABLE !=_dataB[indx] && _fd != _dataB[indx])
{
nBytes = sendto(_dataB[indx], printBuffer, sizeof(printBuffer), MSG_NOSIGNAL, (sockaddr*)_clientAddress, sizeof(*_clientAddress));
if (-1 == nBytes && errno == EPIPE)
{
printf("Client %s has disconnected\n", inet_ntoa(_clientAddress->sin_addr));
}
else if (-1 == nBytes)
{
PrintErr(__LINE__, errno, "Send to err");
return errno;
}
}
}
memset(_buffer, '\0', BUFFER_SIZE);
return 0;
}
и это заголовочный файл
#ifndef __CHAT_H__
#define __CHAT_H__
#define SERVER_PORT 50000
#define CANNOT_RECEIVE "can't receive anymore clients"
#define EXIT "exit"
#define BUFFER_SIZE 256
typedef int make_it_happy; /* C standard requests the -pedantic to give him a diagnostic if the translation unit is empty so we use this trick to get over it */
#endif