【问题标题】:C TCP Socket using select() to get multiple inputs from same clientC TCP Socket 使用 select() 从同一个客户端获取多个输入
【发布时间】:2021-11-30 07:02:39
【问题描述】:

我是socket新手,我正在尝试向服务器发送消息,如果服务器在5秒内没有收到来自客户端的另一条消息,则向客户端发送警告,否则合并两条消息并发送回给客户。

我正在使用 select,一旦调用 select(),服务器就无法接收第二条消息,它总是超时。

我做错了什么??

服务器

#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <unistd.h>
#define BUF_SIZE 256

char *concat(const char *s1, const char *s2);

int main(int argc, char const *argv[]) {
    struct sockaddr_in server, client;
    struct timeval tv;
    int sock, readSize, fd = 0;
    char buf[BUF_SIZE], stringA[BUF_SIZE], stringB[BUF_SIZE];
    socklen_t addressSize;
    fd_set readfds;

    bzero(&server, sizeof(server));
    server.sin_family = PF_INET;
    server.sin_addr.s_addr = inet_addr("127.0.0.1");
    server.sin_port = htons(6000);

    // TCP check
    sock = socket(PF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
        printf("socket: %s\n", strerror(errno));
        return 1;
    }

    // Handle binding error
    if (bind(sock, (struct sockaddr *)&server, sizeof(server)) == -1) {
        printf("bind: %s\n", strerror(errno));
        return 1;
    }

    // Handle connection error
    if (listen(sock, 5) == -1) {
        printf("listen: %s\n", strerror(errno));
        return 1;
    }

    // Handle client acceptance error
    addressSize = sizeof(client);
    sock = accept(sock, (struct sockaddr *)&client, &addressSize);

    while ((readSize = recv(sock, stringA, sizeof(stringA), 0))) {
        stringA[readSize] = '\0';
        printf("Read Message A: %s", stringA);

        FD_ZERO(&readfds);
        FD_SET(sock, &readfds);
        FD_SET(0, &readfds);

        tv.tv_sec = 5;
        tv.tv_usec = 0;

        if (select(sock + 1, &readfds, NULL, NULL, &tv) < 0) {
            printf("ERROR in select");
        }

        char *result;

        // If more messgae receve within 5 seconds, but the program never reached this part
        if (FD_ISSET(sock, &readfds)) {
            // Get string B
            readSize = recv(sock, stringB, sizeof(stringB), 0);
            stringB[readSize] = '\0';
            result = concat(stringA, stringB);
            printf("Some more input received\n");
        } else {
            printf("Time out\n");
        }

        send(sock, &result, sizeof(result), 0);
    }

    printf("Client has closed the connection.\n");
    close(sock);

    return 0;
}

char *concat(const char *s1, const char *s2) {
    char *result = malloc(strlen(s1) + strlen(s2) + 1); 
    strcpy(result, s1);
    strcat(result, s2);
    return result;
}

客户

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#define BUF_SIZE 256

int main(int argc, char const *argv[]) {
    struct sockaddr_in server;
    struct timeval tv;
    int sock, readSize, addressSize;
    char buf[BUF_SIZE];

    bzero(&server, sizeof(server));
    server.sin_family = PF_INET;
    server.sin_addr.s_addr = inet_addr("127.0.0.1");
    server.sin_port = htons(6000);
    sock = socket(PF_INET, SOCK_STREAM, 0);
    addressSize = sizeof(server);

    // TCP check
    sock = socket(PF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
        printf("socket: %s\n", strerror(errno));
        return 1;
    }

    // Handle connection error
    if (connect(sock, (struct sockaddr *)&server, sizeof(server)) == -1) {
        printf("connect: %s\n", strerror(errno));
        return 1;
    }

    while (1) {
        fgets(buf, 256, stdin);
        if (feof(stdin)) break;
        send(sock, &buf, sizeof(buf), 0);

        readSize = recv(sock, buf, sizeof(buf), 0);
        buf[readSize] = '\0';
        printf("%s\n", buf);
    }

    close(sock);
    return 0;
}

【问题讨论】:

  • FD_ISSET(sock + 1, &amp;readfds) 为什么会有+1?这永远不会是真的。

标签: c sockets tcp


【解决方案1】:

您要问的问题似乎是客户端尝试在每个send() 之后接收来自服务器的响应。这将阻塞,直到它可以读取至少一个字节或服务器关闭连接。同时,服务器期望客户端一个接一个地发送消息,对奇数消息没有任何响应。

这是设计问题的症状。这……

我正在尝试向服务器发送消息,如果服务器发送了 在 5 秒内没有收到来自客户端的另一条消息,然后发送一个 警告客户端,否则合并两条消息并发送回 客户。

... 听起来简单,似乎有道理,但实际上它既不是很简单,也不是很明智。假设您有一个好心的服务客户。它知道服务需要两个消息,一个接一个,它会响应这两个消息的串联。为什么这样的客户端无法发送预期的第二条消息?最可能的解释是

  • 它不能,因为它被挂起,因为网络链接断开,因为它的主机发生故障,或类似情况。
  • 它没有,因为它还没来得及被杀死。
  • 没有,因为它有问题。

这些都没有被服务器发送警告消息所救。 (但如果客户端在传递第二条消息之前被杀死,那么服务器可能会在套接字上看到 EOF 并发出读取就绪的信号。)

此外,假设客户端被挂起,服务器发送警告,然后客户端恢复。当它尝试从服务器读取预期响应时,它会改为收到警告消息,或者很可能是与预期响应连接的警告。客户端应该如何区分警告消息和正常响应?

我建议完全放弃警告。服务器可以改为断开无响应的客户端。

如果您想保留警告并继续只使用一个套接字,那么您需要扩充您的协议以使警告消息与正常响应区分开来。例如,服务器的响应可能分为两部分——标识消息类型的响应代码,后跟响应正文。

或者,您可以为两个不同的消息流使用单独的套接字,但我认为这比您现在想要处理的要复杂。


不过,您的代码还有其他问题。主要是

  • 您似乎认为send/writerecv/read 呼叫将配对,因此一个呼叫从一侧发送的数据正是另一侧的一个呼叫接收到的数据.这根本不是一个安全的假设。您正在使用面向流的套接字,而这种套接字的特点之一是数据流没有任何内置的消息边界。如果您想将数据划分为逻辑上独立的消息,则需要在流的顶部分层。

  • 您没有考虑到 send/writerecv/read 可能(成功地)传输的字节数少于您请求的字节数。一般来说,您需要注意这些函数的返回值,并准备使用多次调用来传输给定传输的所有字节。

【讨论】:

    猜你喜欢
    • 2011-04-25
    • 1970-01-01
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    • 2014-05-23
    • 2019-03-15
    • 2016-03-02
    • 1970-01-01
    相关资源
    最近更新 更多