Сокет C: получить и отправить все данные

Я хотел бы получить поведение, подобное этому:

  1. Запуск сервера
  2. Запуск клиента
  3. Клиент набирает команду типа "help" или другую
  4. Сервер отвечает адекватно
  5. go to 3

Проблема в том, что когда моя функция excCommand("help") запускается, принимается и печатается только небольшой текст. Мой текстовый файл таков:

COMMAND HELP:

help - Display help
quit - Shutdown client

печатается только COMMAND HELP. Другая проблема заключается в том, что когда я набираю команду, ничего не печатается, и после 2 команд клиент выходит. Это произведение, в частности:

while (quit)
    {
        getLine("client> ", command, 10);
        if (strcmp(command, "quit") == 0)
            quit = 0;
        else
            excCommand(command);
    }

Это сервер:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "common.h"

int main(int argc, char *argv[])
{
    if (argc != 2)
        ErrorWithUserMessage("Parameter(s)", "<Server Port>");

    char *service = argv[1];

    int servSock = SetupTCPServerSocket(service);
    if (servSock < 0)
        ErrorWithUserMessage("SetupTCPServerSocket() failed: ", "unable to establish");

    unsigned int childProcessCount = 0;
    while (1)
    {
        int clntSock = AcceptTCPConnection(servSock);

        pid_t processID = fork();
        if (processID < 0)
            ErrorWithSystemMessage("fork() failed");
        else if (processID == 0)
        {
            close(servSock);
            HandleTCPClient(clntSock);
            exit(EXIT_SUCCESS);
        }

        printf("with child process: %d\n", processID);
        close(clntSock);
        childProcessCount++;

        //clean up zombies
        while (childProcessCount)
        {
            processID = waitpid((pid_t) - 1, NULL, WNOHANG);
            if (processID < 0)
                ErrorWithSystemMessage("waitpid() failed");
            else if (processID == 0)
                break;
            else
                childProcessCount--;
        }

    }

}

Обработчик:

void HandleTCPClient(int clntSock)
{
    char buffer[BUFSIZE];
    ssize_t numBytesRcvd = recv(clntSock, buffer, BUFSIZE, 0);
    buffer[numBytesRcvd] = '\0';
    if (numBytesRcvd < 0)
        ErrorWithSystemMessage("recv() failed");
    if (strcmp(buffer, "help") == 0)
    {
        FILE *fp = fopen("help.txt", "r");
        if (fp)
        {
            char line[128];
            while (fgets(line, sizeof(line), fp) != NULL)
            {
                if (send(clntSock, line, sizeof(line), 0) < 0)
                    ErrorWithSystemMessage("send() failed");
            }
            fclose(fp);
        }
    }

    close(clntSock);
}

а это мой клиент:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/socket.h>

#include "common.h"

int sock;

void getLine(char *message, char *buf, int maxLen)
{
    printf("%s", message);
    fgets(buf, maxLen, stdin);
    buf[strlen(buf) - 1] = 0;
}

void excCommand(char *command)
{
    if ( send(sock, command, strlen(command), 0) < 0)
        ErrorWithSystemMessage("send() failed");

    char replyMessage[BUFSIZE];
    ssize_t numBytesRecv = 0;
    do
    {
        numBytesRecv = recv(sock, replyMessage, BUFSIZE, 0);
        if ( numBytesRecv < 0)
            ErrorWithSystemMessage("recv() failed");
        printf("%s\n", replyMessage);
        memset(&replyMessage, 0, sizeof(replyMessage));

    }
    while (numBytesRecv > 0);
}

void PrintFile(const char *filename)
{
    FILE *fp;
    fp = fopen(filename, "r");
    if (fp)
    {
        char line[128];
        while (fgets(line, sizeof(line), fp) != NULL)
            fputs(line, stdout);
        fputs("\n", stdout);
        fclose(fp);
    }
}

int main(int argc, char *argv[])
{
    int quit = 1;
    char command[10];

    if (argc < 2 || argc > 3)
    {
        ErrorWithUserMessage("Parameter(s)", "<Server Address> <Server Port>");
    }

    char *server = argv[1];
    char *service = argv[2];

    sock = SetupTCPClientSocket(server, service);
    if (sock < 0)
        ErrorWithUserMessage("SetupTCPClientSocket() failed: ", "unable to connect");

    printf("Connection established!\n\n");

    PrintFile("menu.txt");
    excCommand("help");

    while (quit)
    {
        getLine("client> ", command, 10);
        if (strcmp(command, "quit") == 0)
            quit = 0;
        else
            excCommand(command);
    }

    fputs("\n", stdout);
    close(sock);
    exit(EXIT_SUCCESS);
}

извините, что так многословно


person Lorenzo Cinque    schedule 20.11.2012    source источник
comment
В следующий раз, когда у вас возникнет вопрос, попробуйте создать как можно более маленькую программу, имитирующую подобное поведение, людям будет намного проще понять, в чем проблема, и помочь вам.   -  person Jah    schedule 20.11.2012
comment
ты прав, извини за это :(   -  person Lorenzo Cinque    schedule 20.11.2012


Ответы (3)


Функции recv() и send() не гарантируют отправку/получение всех данных (см. man recv, отправить вручную)

Вам нужно реализовать свои собственные send_all() и recv_all(), что-то вроде

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

Следующее руководство может помочь вам Руководство Beej по сетевому программированию

person Jah    schedule 20.11.2012
comment
хорошо, но с другой стороны, как мне узнать, сколько раз мне нужно вызвать recv()? - person Lorenzo Cinque; 20.11.2012
comment
Вы можете добавить длину сообщения перед каждым сообщением. И recv() фиксирует количество байтов, представляющих длину размера сообщения, а затем вызывает recv(message_size) - person Jah; 20.11.2012
comment
Эта функция send_all() не работает. Он должен объявить «буфер» как «char *»; он должен увеличивать 'buffer' на 'i' каждый раз, когда 'i' положителен; и, конечно, он должен полностью прекратиться, если «i» отрицательно. - person user207421; 27.03.2014
comment
И все это ненужно. Метод send() блокируется до тех пор, пока не будут переданы все данные или не произойдет ошибка. Конечно, в режиме блокировки, но это то, что предполагает этот код. Неблокирующая реализация должна была бы использовать select(), чтобы знать, когда писать дальше. - person user207421; 06.07.2015

Обычные проблемы.

void excCommand(char *command)
{
    if ( send(sock, command, strlen(command), 0) < 0)
        ErrorWithSystemMessage("send() failed");

    char replyMessage[BUFSIZE];
    ssize_t numBytesRecv = 0;
    do
    {
        numBytesRecv = recv(sock, replyMessage, BUFSIZE, 0);
        if ( numBytesRecv < 0)
            ErrorWithSystemMessage("recv() failed");
        printf("%s\n", replyMessage);

Неверный. numBytesRecv могло быть равно нулю, и в этом случае сообщения вообще нет, иначе в этот момент должно быть положительное значение, как вы уже проверили на отрицательное, и оно указывает фактическую длину сообщения, которая не обязательно равна нулю. прекращено. Изменить на:

    if (numBytesRecv == 0)
        break;
    printf("%.*s\n", numBytesRecv, replyMessage);

а потом:

        memset(&replyMessage, 0, sizeof(replyMessage));

Бессмысленно. Удалять.

    }
    while (numBytesRecv > 0);

В этот момент вы должны проверить наличие numBytesRecv < 0 и позвонить perror() или одному из его друзей.

person user207421    schedule 17.12.2015

Я выбираю отправить перед каждым send(), если мне нужно продолжить или нет.

поэтому у меня сначала есть 3 определения

#define BUFFSIZE 1024
#define CONT "CONT"
#define DONE "DONE"

Затем отправить мои данные

int     send_to_socket(int sock, char *msg)
{
    size_t  len;
    int     ret[2];

    len = strlen(msg);
    ret[0] = send(sock, (len <= BUFFSIZE) ? DONE : CONT, 4, 0);
    ret[1] = send(sock, msg, BUFFSIZE, 0);
    if (ret[0] <= 0 || ret[1] <= 0)
    {
        perror("send_to_socket");
        return (-1);
    }
    if (len > BUFFSIZE)
        return (send_to_socket(sock, msg + BUFFSIZE));
    return (1);
}

И чтобы получить:

char    *recv_from_socket(int cs)
{
    char    state[5];
    char    buff[BUFFSIZE+1];
    char    *msg;
    int     ret[2];

    msg = NULL;
    while (42)
    {
        bzero(state, 5);
        bzero(buff, BUFFSIZE+1);
        ret[0] = recv(cs, state, 4, 0);
        ret[1] = recv(cs, buff, BUFFSIZE, 0);
        if (ret[0] <= 0 || ret[1] <= 0)
        {
            perror("recv_from_socket");
            return (NULL);
        }
        // strfljoin() is selfmade
        // join the string and free the left argument to prevent memory leaks.
        // return fresh new string
        msg = (msg) ? ft_strfljoin(msg, buff) : strdup(buff);
        if (strncmp(state, DONE, 4) == 0)
            break ;
        i++;
    }
    return (msg);
}
person albttx    schedule 14.12.2016
comment
ссылки на strfljoin() : github.com/ale- bat/libft/blob/master/sources/string/ - person albttx; 14.12.2016