【问题标题】:TCP concurrent echo server unable to read string from first clientTCP 并发回显服务器无法从第一个客户端读取字符串
【发布时间】:2014-11-24 14:59:42
【问题描述】:

我正在实现一个简单的 TCP process-per-client 并发回显服务器。服务器无法从第一个客户端读取字符串。但是,对于其他客户端,服务器会正​​确回显字符串。我觉得阅读字符串可能有问题;服务器端无法检测到 EOF。我尝试使用 send()/recv() 而不是 read()/write() 没有成功。我还尝试了其他从标准输入读取字符串的函数——即 fgets()、scanf() 等。

这个问题的可能原因是什么;即服务器无法仅从第一个客户端读取?

服务端和客户端的代码如下 -

// server.c
#include <signal.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#define BUF_SIZE 4096
#define MAXPENDING 256

static void zombieReaper(int sig)
{
    while (waitpid(-1, NULL, WNOHANG) > 0)
        continue;
}

static void handleRequest(int cfd)
{
    char buf[BUF_SIZE];
    int numread;
    int size = BUF_SIZE;
    int i;

    printf("\n inside handling request fd : %d ", cfd);
    fflush(stdout);

    while (1) {
        numread = read(cfd, buf, size);
        if (numread > 0) {
            int l = strlen(buf);
            printf("Echo server numread : %d len : %d buf : %s\n", numread, l, buf);
            fflush(stdout);
            buf[l] = '\0';
            write(cfd, buf, size);
        }
    }
}

int main(int argc, char *argv[])
{
    int lfd, cfd;
    struct sigaction sa;

    sigemptyset(&sa.sa_mask);
    sa.sa_handler = zombieReaper;
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        printf("SIGCHILD signal");
        exit(0);
    }

    struct sockaddr_in serv_addr, clnt_addr;
    lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == -1) {
        printf("Socket Failed");
        exit(0);
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(atoi(argv[1]));
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(lfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
        printf("bind() failed");
        exit(0);
    }

    if (listen(lfd, MAXPENDING) < 0) {
        printf("listen() failed");
        exit(0);
    }

    socklen_t clen = sizeof(clnt_addr);
    int status = 0;
    for (;;) {
        if (cfd = accept(lfd, (struct sockaddr *) &clnt_addr, &clen) < 0) {
            printf("accept() failed");
        } else {
            printf("Accepted client cfd : %d\n", cfd);
            switch (fork()) {
            case -1:
                close(cfd);
                break;
            case 0:            // Child
                close(lfd);
                handleRequest(cfd);
                exit(1);
            default:           // Parent
                close(cfd);
                break;
            }
        }
    }
}

// client.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define BUF_SIZE 4096

int main(int argc, char *argv[])
{
    int clnt_fd;
    struct sockaddr_in serv_addr;
    //memset(&serv_addr, 0, sizeof(serv_addr));
    char cmd[BUF_SIZE];
    char buf[BUF_SIZE];

    clnt_fd = socket(PF_INET, SOCK_STREAM, 0);
    if (clnt_fd == -1) {
        printf("socket() failed");
        exit(0);
    }

    // prepare for connect
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(atoi(argv[2]));
    serv_addr.sin_addr.s_addr = inet_addr(argv[1]);

    // connect to server
    if (connect(clnt_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
        printf("connect() failed");
        exit(0);
    }

    int size = BUF_SIZE;
    while (1) {
        printf("\n Enter a string (q to Exit) : \n ");
        gets(cmd);
        if (!strcmp(cmd, "q"))
            break;

        int n = write(clnt_fd, cmd, strlen(cmd));
        n = read(clnt_fd, buf, BUF_SIZE);
        printf("\n Received Results : \n %s\n", buf);
    }
    close(clnt_fd);

    return 0;
}

【问题讨论】:

    标签: c linux sockets tcp


    【解决方案1】:

    您的代码中的问题是,

    if (cfd = accept(lfd, (struct sockaddr *) &clnt_addr, &clen) < 0)
    

    在这里,当您接受时,您正在使用关系运算符

    验证后返回值 0 存储在 cfd 中。

    假设原始的接受套接字 id 是 4 但返回值是 0

    根据返回值你正在程序中做某事。

    尝试对条件使用优先级,

    if ((cfd = accept(lfd, (struct sockaddr *) &clnt_addr, &clen)) < 0)
    

    现在它会打印出确切接受的套接字 ID。

    【讨论】:

    • 从这个 bug 的存在可以得出结论,OP 没有向我们展示“真实”代码,因为这个 bug 没有客户会得到答案。
    • 我听不懂,你在问什么?
    • 我什么都没问。我只是注意到,您指向的错误(显然确实存在于显示的代码中)实际上不允许任何客户端接收任何数据。
    【解决方案2】:
    if (numread > 0) {
            int l = strlen(buf);
    

    这里你假设缓冲区中有一个空值。相反,您应该只使用下面的numread

            printf("Echo server numread : %d len : %d buf : %s\n", numread, l, buf);
    

    这里再次假设缓冲区中有一个空值。应该是

            printf("Echo server numread : %d buf : %.*s\n", numread, numread, buf);
    

    现在: buf[l] = '\0';

    在这里,如果您找到它,则将 null 放入您已经找到它的地方。删除。

            write(cfd, buf, size);
    

    在这里,您将错误数量的数据写回发送者。应该是write(cfd, buf, numread). }

    如果numread &lt;= 0,您将无法跳出循环。

    printf("socket() failed");
    printf("connect() failed");
    printf("bind() failed");
    printf("listen() failed");
    printf("accept() failed");
    

    在这些地方,您都在打印您自己设计的相当无用的错误消息,而忽略了实际错误。不要这样做。在每个地方将printf更改为perror,或者将strerror()的结果添加到消息中。

    【讨论】:

    • 感谢以上建议。我在我的代码中实现了它们。但之前的问题仍然存在。服务器为除第一个客户端之外的所有客户端正确回显字符串。这个问题有什么原因吗?
    • 哦 - 在网络代码中滥用 strlen()。这是新的:(
    猜你喜欢
    • 2014-03-10
    • 2021-09-05
    • 2012-09-21
    • 2012-05-26
    • 2017-10-09
    • 2017-10-11
    • 2015-12-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多