C Блокировка сокетов по неизвестной причине - PullRequest
1 голос
/ 13 апреля 2020

Я пытаюсь написать базовый c эхо-сервер TCP в C на macOS Catalina. Я следую за man-страницами моей среды и этими веб-страницами: Реализация TCP Server-Client в C - GeeksforGeeks и Пример сервера и клиента с C сокетами на Linux - BinaryTides .

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

сервер. c

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

#include <arpa/inet.h>

#include <sys/socket.h>

#define CLOSE(fildes) if (close(fildes) == -1) {            \
    perror("call to `close` failed");               \
    exit(EX_OSERR);                         \
}

#define MAXCONNS 4
#define MSGLEN 1023

int
main(int argc, char **argv)
{
    long port;
    int passive_fildes, active_fildes;
    struct sockaddr_in address;
    ssize_t length;
    char message[MSGLEN];

    if (argc != 2) {
        fprintf(stderr, "usage: %s <port>\n", argv[0]);
        exit(EX_USAGE);
    }
    if ((port = strtol(argv[1], NULL, 10)) > UINT16_MAX || port < 1024) {
        fputs("invalid port argument\n", stderr);
        exit(EX_USAGE);
    }
    if ((passive_fildes = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        perror("failed to create socket");
        exit(EX_OSERR);
    }
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = htonl(INADDR_ANY);
    address.sin_port = htons(port);
    if (bind(passive_fildes, (struct sockaddr *)&address,
        sizeof(struct sockaddr_in)) == -1) {
        perror("failed to bind socket");
        CLOSE(passive_fildes);
        exit(EX_OSERR);
    }
    if (listen(passive_fildes, MAXCONNS) == -1) {
        perror("failed to listen for connections");
        CLOSE(passive_fildes);
        exit(EX_OSERR);
    }
    /* Accept connections indefinitely. */
    for (;;) {
        if ((active_fildes = accept(passive_fildes, NULL, NULL)) ==
            -1) {
            perror("failed to accept connection");
            /* Should we exit here? */
            continue;
        }
        /* Respond to requests until client disconnects. */
        while ((length = recv(active_fildes, message, MSGLEN, 0)) !=
            0) {
            if (length == -1) {
                perror("failed to receive request");
                continue;
            }
            if (message[length-1] != '\n') {
                fprintf(stderr, "request was over %d ", MSGLEN);
                fprintf(stderr, "characters in length\n");
                /* We break the client if we don't respond. */
                break;
            }
            if (send(active_fildes, message, (size_t)length, 0) ==
                -1) {
                perror("failed to send response");
                /* We break the client if we don't respond. */
                break;
            }
        }
        CLOSE(active_fildes);
    }
    CLOSE(passive_fildes);
    return 0;
}

клиент. c

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

#include <arpa/inet.h>

#include <sys/socket.h>

#define CLOSE(fildes) if (close(fildes) == -1) {            \
    perror("call to `close` failed");               \
    exit(EX_OSERR);                         \
}

#define MSGLEN 1023

int
main(int argc, char **argv)
{
    struct sockaddr_in address;
    long port;
    int fildes;
    char message[MSGLEN+1];
    ssize_t length;

    if (argc != 3) {
        fprintf(stderr, "usage: %s <address> <port>\n", argv[0]);
        exit(EX_USAGE);
    }
    if (inet_aton(argv[1], &address.sin_addr) == 0) {
        perror("invalid address argument\n");
        exit(EX_USAGE);
    }
    if ((port = strtol(argv[2], NULL, 10)) > UINT16_MAX || port < 1024) {
        fputs("invalid port argument\n", stderr);
        exit(EX_USAGE);
    }
    if ((fildes = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        perror("failed to create socket");
        exit(EX_OSERR);
    }
    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    if (connect(fildes, (struct sockaddr *)&address,
        sizeof(struct sockaddr_in)) == -1) {
        perror("failed to connect to server");
        CLOSE(fildes);
        exit(EX_NOHOST);
    }
    while (fgets(message, MSGLEN+1, stdin) != NULL) {
        /* Check whether the entire request was read. */
        if (message[strlen(message)-1] != '\n') {
            fprintf(stderr, "request was over %d ", MSGLEN);
            fprintf(stderr, "characters in length\n");
            /* Gobble the rest of the request. */
            while (fgetc(stdin) != '\n');
            continue;
        }
        if (send(fildes, message, strlen(message), 0) == -1) {
            perror("failed to send request");
            continue;
        }
        /* Don't clobber the EOS! */
        if ((length = recv(fildes, message, MSGLEN, 0)) == -1) {
            perror("failed to receive response");
            /* Not reading response is an error condition. */
            CLOSE(fildes);
            exit(EX_OSERR);
        }
        if (length == 0) {
            fputs("server unexpectedly closed connection", stderr);
            CLOSE(fildes);
            exit(EX_PROTOCOL);
        }
        /* Check whether the entire response was read. */
        if (message[length-1] != '\n') {
            fprintf(stderr, "response was over %d ", MSGLEN);
            fprintf(stderr, "characters in length\n");
            /* Gobble the rest of the response. */
            do if (recv(fildes, message, 1, 0) == -1) {
                perror("failed to gobble extra characters");
                /* This is an error condition. */
                CLOSE(fildes);
                exit(EX_OSERR);
            } while (message[0] != '\n');
            /* Can still print partial response. */
        }
        /* Validate the response with an EOS. */
        message[length] = '\0';
        if (fputs(message, stdout) == -1) {
            perror("failed to print response out");
            continue;
        }
    }
    if (!feof(stdin)) {
        perror("failed to read request in");
        CLOSE(fildes);
        exit(EX_OSERR);
    }
    CLOSE(fildes);
    return 0;
}

Серверная консоль

$ ./server 8080
entered main loop
accepted connection
received request: hello
sent response: hello

Клиентская консоль

$ ./client 127.0.0.1 8080
connected to server
hello

После того, как клиент распечатал сообщение о соединении, я ввел «привет»; однако клиент никогда не выводит ответ от сервера. Обратите внимание на оператор отладочной печати в строке 73 в клиенте. c никогда не выполняется. Это показывает, что вызов recv не завершен правильно. Тем не менее, оператор отладочной печати в строке 87 на сервере выполняется. c, что показывает, что сервер завершает свой вызов send.

Я очень тщательно пытался написать правильный код C, поэтому, если у любого из вас есть комментарии о безопасности или стиле, пожалуйста, дайте им!

...