Функция recv (внутри кода клиента) возвращает 0 - PullRequest
0 голосов
/ 31 декабря 2018

Я пытаюсь создать свой собственный чат-сервер, который будет отправлять и получать сообщения одновременно, используя 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
...