【问题标题】:TCP/IP client sendTCP/IP 客户端发送
【发布时间】:2018-09-19 12:22:06
【问题描述】:

以下是我的 C/S 代码和输出仅供测试。
我遇到了麻烦。
很明显,我只是通过客户端发送了两次。但是,服务器刚刚收到 3 条消息。第二个似乎是 NULL。

#define MP_MAXLINE 4096    
/* client */
int main(int ac, char *av[])
{
    char buf[MP_MAXLINE];
    int clifd;
    struct sockaddr_in cliaddr;

    if((clifd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        perror("socket error");

    memset(&cliaddr, 0, sizeof(struct sockaddr_in));
    cliaddr.sin_family = AF_INET;
    cliaddr.sin_port = htons(atoi(av[2]));
    inet_pton(AF_INET, av[1], &cliaddr.sin_addr);

    if(connect(clifd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)))
        perror("can not connect to server");

    printf("enter data to send: ");
    while(fgets(buf, MP_MAXLINE, stdin) != NULL){
        write(clifd, buf, sizeof(buf));
        printf("enter data to send: ");
    }
}

/* server */
static void cli_service(int clifd){
    int lclifd, rcv_cnt = 0;
    char data[MP_MAXLINE];

    for(;;){
        if(read(clifd, data, sizeof(data)) == 0)
            exit(1);
        rcv_cnt++;
        printf("cnt: %d, data: %s", rcv_cnt, data);
    }
}

int main(int ac, char *av[])
{
    ...

    for(;;){
        clifd = accept(svrfd, (struct sockaddr *)&cliaddr, &clisocklen);

        if((clipid = fork()) < 0){
            printf("fork error\n");
            continue;
        }
        else if(clipid == 0){
            close(svrfd);
            cli_service(clifd);
        }
        close(clifd);
        continue;
    }
}

客户端的输出在这里:
输入要发送的数据:1111
输入要发送的数据:2222
输入要发送的数据:

服务器的输出在这里:
cnt:1,数据:1111
cnt:2,数据:cnt:3,数据:2222

【问题讨论】:

  • 大家好,我想知道为什么会这样,我该如何解决。
  • 函数:perror() 不退出程序,也不修复错误。所以在调用perror() 之后应该调用exit( EXIT_FAILURE );

标签: c linux sockets unix tcp-ip


【解决方案1】:

发生的事情是你即将发现 TCP 是一个面向流 协议,这意味着它保证你发送的字节将被正确和按顺序传递,但是映射单个write()/read() 调用的字节数是任意的——也就是说,每次调用read() 写入接收器数组的字节数绝对保证是与发送程序之前传递给write() 调用的字节数相同。

因此,在您的客户端中,对write() 的每次调用都发送了 4096 个字节,其中前几个字节将包含用户输入的以 0 结尾的文本字符串,其余部分将是未初始化的数据(即它可以是任何东西,因为你从来没有写过数组的那部分)。

然后在您的服务器中,您正在调用read(),它读取了一些字节,但您不知道它读取了多少字节,因为您所做的只是检查read() 是否返回0。因此,您不知道 read() 是否将 4096 字节的数据放入您的 data 数组或一些较小的字节数;如果它是一些较小的数字,那么您的接收代码现在与您的发送代码不同步,因为随后对 read() 的调用可能会将未初始化的数据写入您的 data 数组的开头,并且用户的下一个字符串位于数组中间的某个位置。

当然,这不是有用的行为。为了修复它,您需要做的是密切注意read() 返回的值;如果它大于零但小于sizeof(data),您将需要以某种方式处理它。处理它的一种方法是计算data 数组中当前有多少有效字节,并且始终保持read(clifd, data+numValidBytes, sizeof(data)-numValidBytes) 直到(numValidBytes == sizeof(data))...,然后才解析数据并将numValidBytes 设置回零。另一个更简单的选择是调用recv() 而不是read(),并在最后一个参数中设置MSG_WAITALL——在这种情况下,recv() 将保证在读取所有sizeof(data) 字节之前不会返回,然后您不必自己处理部分读取。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-17
    • 2019-12-06
    • 2017-01-26
    • 1970-01-01
    • 2011-08-15
    • 1970-01-01
    • 2018-02-26
    • 1970-01-01
    相关资源
    最近更新 更多