Сервер не будет recv второй раз - PullRequest
0 голосов
/ 03 декабря 2018

У меня есть клиент и сервер, сервер настроен следующим образом:

int listenS = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(PORT);
s.sin_addr.s_addr = htonl(IP_ADDR);
bind(listenS, (struct sockaddr*)&s, sizeof(s));
listen(listenS, QUEUE_LEN);

struct sockaddr_in clientIn;
int clientInSize = sizeof clientIn;
while (1)
{
    int newfd = accept(listenS, (struct sockaddr*)&clientIn, (socklen_t*)&clientInSize);
    //......

( Есть тесты, которые я просто удалил, чтобы сделать код более читабельным)

Клиент просто:

int sock = socket(AF_INET, SOCK_STREAM, 0), nrecv;
struct sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(PORT);
s.sin_addr.s_addr = htonl(IP_ADDR);
if (connect(sock, (struct sockaddr*)&s, sizeof(s)) < 0)
{ //......

У меня установлено соединение, и все работает отлично, сервер recv получает сообщение в первый раз, когда я отправляю его с клиента, но когда я пытаюсьsend другое сообщение серверу, на котором сервер не заблокирует вызов recv и не получит ничего ( возвращает размер буфера, а не 0 )

Вот код клиента:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.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 <fcntl.h>
#include <sys/sendfile.h>
#include <sys/stat.h>

#define PORT 0x0da2
#define IP_ADDR 0x7f000001
#define MAX_BUFFER_SIZE 1024

int send_all(int socket, void* buffer, size_t length)
{
    char *ptr = (char*)buffer;
    while (length > 0)
    {
        int i = send(socket, ptr, length, 0);
        if (i < 1) return -1;
        ptr += i;
        length -= i;
    }
    return 0;
}

int main(int argc, char** argv)
{
    if (argc > 1)
    {
        if ((strcmp(argv[1], "list-files") != 0) &&
            (strcmp(argv[1], "upload-file") != 0) &&
            (strcmp(argv[1], "download-file") != 0) &&
            (strcmp(argv[1], "search") != 0))
            {
                perror("The arguments are incorrect.");
            }

        int sock = socket(AF_INET, SOCK_STREAM, 0), nrecv;
        struct sockaddr_in s = { 0 };
        s.sin_family = AF_INET;
        s.sin_port = htons(PORT);
        s.sin_addr.s_addr = htonl(IP_ADDR);
        if (connect(sock, (struct sockaddr*)&s, sizeof(s)) < 0)
        {
            perror("connect");
            return 1;
        }
        printf("Successfully connected.\n");

        char sendBuffer[MAX_BUFFER_SIZE];
        int lenOfArgv = strlen(argv[1]);
        int sendBufferIndex = 0;
        for (int i = 0;
            i < lenOfArgv && sendBufferIndex < MAX_BUFFER_SIZE;
            i++, sendBufferIndex++)
        {
            sendBuffer[sendBufferIndex] = argv[1][i];
        }

        if (argc == 3)
        {
            sendBuffer[sendBufferIndex++] = ' ';
            int lenOfArgv = strlen(argv[2]);
            for (int i = 0;
                i < lenOfArgv && sendBufferIndex < MAX_BUFFER_SIZE;
                i++, sendBufferIndex++)
            {
                sendBuffer[sendBufferIndex] = argv[2][i];
            }
        }

        sendBuffer[sendBufferIndex] = 0;

        // + 1 for terminating null
        if (send_all(sock, sendBuffer, strlen(sendBuffer) + 1) < 0)
        {
            perror("send buffer to server failed");
            return 1;
        }

        if(strcmp(argv[1], "download-file") == 0)
        {
            char sizeBuffer[256];
            recv(sock, sizeBuffer, 256, 0);
            int fileSize = atoi(sizeBuffer);

            if(fileSize > 0)
            {
                FILE* recievedFile = fopen(argv[2], "w");
                if(recievedFile != NULL)
                {
                    int remainData = fileSize;
                    size_t len;
                    char fileBuffer[MAX_BUFFER_SIZE];
                    while(((len = recv(sock, fileBuffer, MAX_BUFFER_SIZE, 0)) > 0 && (remainData > 0)))
                    {
                        fwrite(fileBuffer, sizeof(char), len, recievedFile);
                        remainData -= len;
                        printf("Received %d bytes, %d is left..\n", len, remainData);
                    }
                    fclose(recievedFile);
                    printf("File downloaded!\n");
                }
                else
                {
                    perror("Failed to download file\n");
                }
            }
        }
        else if(strcmp(argv[1], "upload-file") == 0)
        {
            char filePath[MAX_BUFFER_SIZE];
            sprintf(filePath, "%s", argv[2]);
            int fd = open(filePath, O_RDONLY);
            int downloadFailed = 0;
            if (fd != -1)
            {
                struct stat file_stat;
                if(fstat(fd, &file_stat) >= 0)
                {
                    char fileSize[256];
                    sprintf(fileSize, "%d", (int)file_stat.st_size);
                    int len = send(sock, fileSize, sizeof(fileSize), 0);
                    if(len >= 0)
                    {
                        int remainData = file_stat.st_size;
                        off_t offset = 0;
                        int sent_bytes = 0;
                        while(((sent_bytes = sendfile(sock, fd, &offset, MAX_BUFFER_SIZE)) > 0) && (remainData > 0))
                        {
                            remainData -= sent_bytes;
                            printf("sent %d bytes, %d is left...\n", sent_bytes, remainData);
                        }
                    }else {downloadFailed = 1;}
                }else {downloadFailed = 1;}
            }else {downloadFailed = 1;}

            if(downloadFailed == 1)
            {
                perror("Failed to download file!\n");
            }
        }
        else
        {
            char someBuffer[MAX_BUFFER_SIZE];
            // nrecv is the number of bytes that we recieved
            if ((nrecv = recv(sock, someBuffer, MAX_BUFFER_SIZE, 0)) < 0)
            {
                perror("recv");
                return 1;
            }
            printf("%s\n", someBuffer);
        }

        close(sock);
        return 0;
    }
    else
    {
        perror("The arguments are incorrect.");
    }
}

вот код сервера:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.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 <math.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>

#define PORT 0x0da2 // 3490
#define IP_ADDR 0x7f000001 // 127.0.0.1
#define QUEUE_LEN 20
#define MAX_BUFFER_SIZE 1024

int send_all(int socket, void* buffer, int length)
{
    char *ptr = (char*)buffer;
    while (length > 0)
    {
        int i = send(socket, ptr, length, 0);
        if (i < 1) return -1;
        ptr += i;
        length -= i;
    }
    return 0;
}

void list_dir()
{
    DIR * directory;
    struct dirent* dir;
    directory = opendir(".");
    if (directory)
    {
        while ((dir = readdir(directory)) != NULL)
        {
            printf("%s\n", dir->d_name); // /home/text.txt, text.txt
            // get filesize (in bytes0 with dir->d_name
        }
    }
}

void list_files(char* buffer, int withBytes = 0)
{
    DIR* d;
    struct dirent* dir;
    d = opendir("data");
    int bufferIndex = 0;
    while((dir = readdir(d)) != NULL)
    {
        char tempFilename[256] = "data/";
        int tempIndex = 5;
        char* scan = dir->d_name;
        while(*scan)
        {
            tempFilename[tempIndex++] = *scan;
            buffer[bufferIndex++] = *scan++;
        }
        tempFilename[tempIndex] = 0;
        struct stat st = {0};
        stat(tempFilename, &st);
        int fileSize = st.st_size;

        if(withBytes == 1)
        {
            // Adding file size to the buffer
            bufferIndex += sprintf(&buffer[bufferIndex], " %d bytes", fileSize);
        }

        buffer[bufferIndex++] = '\n';
    }
    buffer[bufferIndex] = 0;
    closedir(d);
}

int main(void)
{
    int listenS = socket(AF_INET, SOCK_STREAM, 0);
    if (listenS < 0)
    {
        perror("socket");
        return 1;
    }
    struct sockaddr_in s = { 0 };
    s.sin_family = AF_INET;
    s.sin_port = htons(PORT);
    s.sin_addr.s_addr = htonl(IP_ADDR);
    if (bind(listenS, (struct sockaddr*)&s, sizeof(s)) < 0)
    {
        perror("bind");
        return 1;
    }
    if (listen(listenS, QUEUE_LEN) < 0)
    {
        perror("listen");
        return 1;
    }
    struct sockaddr_in clientIn;
    int clientInSize = sizeof clientIn;

    struct stat st = {0};
    if(stat("data", &st) == -1)
    {
        mkdir("data", 0700);
    }

    while (1)
    {
        int newfd = accept(listenS, (struct sockaddr*)&clientIn, (socklen_t*)&clientInSize);
        if (newfd < 0)
        {
            perror("accept");
            return 1;
        }
        int pid = fork(); // creating new thread 
        if (pid == 0)
        {
            close(listenS); // duplicate=> thats why we need to close the socket

            char someBuffer[MAX_BUFFER_SIZE];
            int nrecv;
            if ((nrecv = recv(newfd, someBuffer, MAX_BUFFER_SIZE, 0)) < 0)
            {
                perror("recv");
                return 1;
            }
            printf("Message recieved: %s\n", someBuffer);

            // Here we read the command the argument and split them
            // into seperate variables
            char command[256];
            char argument[256];
            int commandHasBeenSet = 0;
            char* token = strtok(someBuffer, " ");
            while(token != NULL)
            {
                if(commandHasBeenSet == 0)
                {
                    strcpy(command, token);
                    commandHasBeenSet = 1;
                }
                else
                {
                    strcpy(argument, token);   
                }
                token = strtok(NULL, " ");
            }

            if (strcmp(command, "list-files") == 0)
            {
                char buffer[MAX_BUFFER_SIZE];
                list_files(buffer, 1);

                if (send_all(newfd, buffer, strlen(buffer) + 1) < 0)
                {
                    perror("send buffer to client failed");
                    return 1;
                }
                printf("Sent a message to a client!\n");
            }
            else if (strcmp(command, "upload-file") == 0)
            {
                printf("Uploading file %s\n", argument);
                char sizeBuffer[256];
                recv(newfd, sizeBuffer, 256, 0);
                int fileSize = atoi(sizeBuffer); 

                if(fileSize > 0)
                {
                    char filePath[MAX_BUFFER_SIZE];
                    sprintf(filePath, "data/%s", argument);
                    printf("Downloading to %s", filePath);
                    FILE* recievedFile = fopen(filePath, "w");
                    if(recievedFile != NULL)
                    {
                        int remainData = fileSize;
                        size_t len;
                        char fileBuffer[MAX_BUFFER_SIZE];
                        while(((len = recv(newfd, fileBuffer, MAX_BUFFER_SIZE, 0)) > 0 && (remainData > 0)))
                        {
                            fwrite(fileBuffer, sizeof(char), len, recievedFile);
                            remainData -= len;
                            printf("Received %d bytes, %d is left..\n", len, remainData);
                        }
                        fclose(recievedFile);
                        printf("File downloaded!\n");
                    }
                    else
                    {
                        perror("Failed to download file\n");
                    }
                }else
                {
                    perror("Failed to get file size for download\n");
                }
            }
            else if (strcmp(command, "download-file") == 0)
            {
                char filePath[MAX_BUFFER_SIZE];
                sprintf(filePath, "data/%s", argument);
                int fd = open(filePath, O_RDONLY);
                int downloadFailed = 0;
                if (fd != -1)
                {
                    struct stat file_stat;
                    if(fstat(fd, &file_stat) >= 0)
                    {
                        char fileSize[256];
                        sprintf(fileSize, "%d", (int)file_stat.st_size);
                        int len = send(newfd, fileSize, sizeof(fileSize), 0);
                        if(len >= 0)
                        {
                            int remainData = file_stat.st_size;
                            off_t offset = 0;
                            int sent_bytes = 0;
                            while(((sent_bytes = sendfile(newfd, fd, &offset, MAX_BUFFER_SIZE)) > 0) && (remainData > 0))
                            {
                                remainData -= sent_bytes;
                                printf("Server sent %d bytes, %d is left...\n", sent_bytes, remainData);
                            }

                        }else {downloadFailed = 1;}
                    }else {downloadFailed = 1;}
                }else {downloadFailed = 1;}

                if(downloadFailed == 1)
                {
                    perror("Failed to download file!\n");
                }
            }
            else if (strcmp(command, "search") == 0)
            {
                char buffer[MAX_BUFFER_SIZE];
                char result[MAX_BUFFER_SIZE];
                int resultIndex = 0;
                list_files(buffer);

                result[0] = 0;

                char tempBuffer[MAX_BUFFER_SIZE];
                strcpy(tempBuffer, buffer);
                token = strtok(tempBuffer, "\n");
                while(token != NULL)
                {
                    char* scanToken = token;
                    char* scanArgument = argument;
                    int found = 1;
                    while(*scanToken && *scanArgument)
                    {
                        if(*scanToken++ != *scanArgument++)
                        {
                            found = 0;
                            break;
                        }
                    }

                    if(found == 1)
                    {
                        if(resultIndex > 0)
                        {
                            result[resultIndex++] = ' ';
                        }
                        strcpy(&result[resultIndex], token);
                        resultIndex += strlen(token);
                        result[resultIndex] = 0;
                    }

                    token = strtok(NULL, "\n");
                }

                if (send_all(newfd, result, strlen(result) + 1) < 0)
                {
                    perror("send buffer to client failed");
                    return 1;
                }
                printf("Sent a message to a client!\n");
            }

            close(newfd);
            exit(0);
        }
        else
            close(newfd);
    }


    close(listenS);
    return 0;
}

Если вы запустите сервер, а затем запустите клиент с такими командами, как:

./client list-files
./client download-file test.txt

, он будет работать нормально,клиент будет получать сообщения с сервера и наоборот.

Проблема возникает, когда я пытаюсь запустить:

./client upload-file test.txt

, которая по сути совпадает с командой download-file, только что скопирована ивставлен на сервер с клиента (та же логика, должна работать так же), за исключением того, что это не так.

В частности, сбой программы в строке 175 сервера (recv(newfd, sizeBuffer, 256, 0);), он получает 0 вместо значения, которое отправляет клиент.

Есть идеи, что мне не хватает?

(Я пытался искать в Интернете, но ничего не нашел)

Ответы [ 2 ]

0 голосов
/ 03 декабря 2018

Вы предполагаете, что потоковый сокет сохранит границы ваших сообщений.Я не буду.Итак, вы отправляете что-то вроде:

upload-file filename\0
NNN\0\0\0...\0   [256 bytes containing filesize]
<content of filename>

Так что, вероятно, первый recv, который вы делаете на стороне сервера (MAX_BUFFER_SIZE байт), получает не только командную строку и имя файла, но итакже блок размера файла и все содержимое файла.То есть вы уже все это получили, и он сидит в someBuffer.Ваша начальная командная строка имеет нулевое окончание, поэтому вы не будете знать об остальном, если вы не проверите nrecv.Следовательно, ваш следующий вызов recv получает конец файла, потому что клиент завершил работу и закрыл свой конец соединения.

Чтобы отправлять определенные записи по TCP, вам необходимо точно знать, сколько байтов ожидается в каждой точке, и получать именно их (или быть готовым к анализу полученных данных).Смотри также https://stackoverflow.com/a/47440054/1076479

0 голосов
/ 03 декабря 2018

TCP - это потоковый протокол.Границы сообщений отсутствуют, а recv s сервера не соответствуют send s.

клиента. Клиент отправляет команду с

    send_all(sock, sendBuffer, strlen(sendBuffer) + 1)

OTOH, сервер пытается получитьдля него

    nrecv = recv(newfd, someBuffer, MAX_BUFFER_SIZE, 0))

recv не имеет значения, содержит ли поток '\0' или нет.Он вслепую ожидает поступления MAX_BUFFER_SIZE байтов. Некоторые (ценные) данные, отправленные клиентом, находятся в someBuffer сразу после команды, но игнорируются сервером.

Сервер должен проанализировать ответ большестарательно.Для этого вам, вероятно, понадобится более сложный протокол (например, префикс каждой строки с ее длиной).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...