C套接字因未知原因而阻塞

问题描述 投票:0回答:1

我正在尝试在macOS Catalina上的C语言中编写基本的TCP回显服务器。我正在关注环境的手册页和以下网页:TCP Server-Client implementation in C - GeeksforGeeksServer and client example with C sockets on Linux - BinaryTides

出于某种原因,我的客户(我相信)在第一次致电recv时被阻止。我是套接字编程的新手,所以我不知道为什么会这样。为什么我的客户端不打印出应该从服务器得到的响应?

server.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 ERR_BIGREQ "request was over %d characters in length\n"

#define MAXCONN 4
#define BUFLEN 1024

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

    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\n", stderr);
        exit(EX_USAGE);
    }
    if ((passive_fildes = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        perror("call to `socket` failed");
        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("call to `bind` failed");
        CLOSE(passive_fildes);
        exit(EX_OSERR);
    }
    if (listen(passive_fildes, MAXCONN) == -1) {
        perror("call to `listen` failed");
        CLOSE(passive_fildes);
        exit(EX_OSERR);
    }
    /* Accept connections indefinitely. */
    for (;;) {
        /* DEBUG */ fputs("entered main loop\n", stderr);
        if ((active_fildes = accept(passive_fildes, NULL, NULL)) ==
            -1) {
            perror("call to `accept` failed");
            /* Should we exit here? */
            continue;
        }
        /* DEBUG */ fputs("accepted connection\n", stderr);
        /* Respond to requests until client disconnects. */
        while ((length = recv(active_fildes, message, BUFLEN, 0)) !=
            0) {
            if (length == -1) {
                perror("call to `recv` failed");
                continue;
            }
            if (message[length-1] != '\n') {
                fprintf(stderr, ERR_BIGREQ, BUFLEN);
                /* We break the client if we don't respond. */
                CLOSE(active_fildes);
                break;
            }
            /* DEBUG */ fprintf(stderr, "received request: %s", message);
            if (send(active_fildes, message, (size_t)length, 0) ==
                -1) {
                perror("call to `send` failed");
                /* We break the client if we don't respond. */
                CLOSE(active_fildes);
                break;
            }
            /* DEBUG */ fprintf(stderr, "sent response: %s", message);
        }
        CLOSE(active_fildes);
    }
    CLOSE(passive_fildes);
    return 0;
}

client.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 ERR_BIGREQ "request is over %d characters in length\n"
#define ERR_BIGRES "response is over %d characters in length\n"

#define BUFFER 1024

int
main(int argc, char **argv)
{
    struct sockaddr_in address;
    long port;
    int fildes;
    char message[BUFFER];
    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");
        exit(EX_USAGE);
    }
    if ((port = strtol(argv[2], NULL, 10)) > UINT16_MAX || port < 1024) {
        fputs("invalid port\n", stderr);
        exit(EX_USAGE);
    }
    if ((fildes = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        perror("call to `socket` failed");
        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("call to `connect` failed");
        CLOSE(fildes);
        exit(EX_NOHOST);
    }
    /* DEBUG */ fputs("connected to server\n", stderr);
    while (fgets(message, BUFFER, stdin) != NULL) {
        /* Check whether the entire request was read. */
        if (message[strlen(message)-1] != '\n') {
            fprintf(stderr, ERR_BIGREQ, BUFFER-2);
            continue;
        }
        if (send(fildes, message, strlen(message), 0) == -1) {
            perror("call to `send` failed");
            continue;
        }
        /* Don't clobber the EOS! */
        if ((length = recv(fildes, message, BUFFER-1, 0)) == -1) {
            perror("call to `recv` failed");
            /* Not reading response is an error condition. */
            CLOSE(fildes);
            exit(EX_OSERR);
        }
        /* DEBUG */ fputs("got a response\n", stderr);
        /* Validate the response with an EOS. */
        message[length] = '\0';
        /* Check whether the entire response was read. */
        if (message[length-1] != '\n') {
            fprintf(stderr, ERR_BIGRES, BUFFER-1);
            /* Gobble the rest of the response. */
            do if (recv(fildes, message, 1, 0) == -1) {
                perror("call to `recv` failed");
                /* This is an error condition. */
                CLOSE(fildes);
                exit(EX_OSERR);
            } while (message[0] != '\n');
            /* Can still print partial response. */
        }
        fputs(message, stdout);
    }
    if (!feof(stdin)) {
        perror("call to `fgets` failed");
        CLOSE(fildes);
        exit(EX_OSERR);
    }
    CLOSE(fildes);
    return 0;
}

服务器输出

entered main loop
accepted connection
received request: hello
sent response: hello

客户端输出

connected to server

客户端打印出连接消息后,我输入“ hello”;但是,客户端从不输出服务器的响应。请注意,永远不会执行client.c中第73行的调试打印语句。这表明recv调用未正确完成。但是,将执行server.c中第87行的调试打印语句,该语句显示服务器已完成其send调用。

我非常努力地编写了正确的C代码,因此,如果您对安全性或样式有任何意见,请给他们!

c macos sockets tcp blocking
1个回答
-2
投票

我无法复制您的问题。

这里是我的Catalina系统的笔录:

server$ ./server 11111
entered main loop
accepted connection
received request: hello from client
sent response: hello from client
entered main loop
^C
server$ 
client$ ./client 127.0.0.1 11111
connected to server
hello from client
got a response
hello from client
^C
client$ 
© www.soinside.com 2019 - 2024. All rights reserved.