Linux TCP套接字:客户端已发送数据,但服务器仍在read()上阻塞

男人

我有一个在Linux上使用TCP套接字的简单客户端-服务器示例。服务器监听回送地址。客户端连接到服务器,并发送一些整数以及一个“ END”字符串以标记数据结束。服务器读取数字,将所有数字相加并返回总和。但是,read()即使客户端成功发送了所有数据,我的服务器有时还是处于打开状态。

这是代码:

server.c:

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BACKLOG 5

int main(int argc, char *argv[]) {
    struct sockaddr_in addr;
    int down_flag = 0;
    int result = 0;
    int ret = 0;

    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sfd < 0) {
        perror("Create server socket error: %s\n");
        return 0;
    }

    /* Bind socket to loopback address */
    memset((void *) &addr, 0, sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888);
    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    if (bind(sfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) == -1) {
        perror("Bind server socket failed");
        goto _exit;
    }

    if (listen(sfd, BACKLOG) == -1) {
        perror("Listen failed");
        goto _exit;
    }

    ssize_t num_rd = 0;
    char buf[100] = {0};
    for (;;)
    {
        printf("Waiting to accept a connection...\n");
        int cfd = accept(sfd, NULL, NULL);
        printf("Accepted socket fd = %d\n", cfd);
        result = 0;
        while ((num_rd = read(cfd, buf, sizeof(buf))) > 0) {
            /* Ensure the buffer is 0-terminated */
            buf[sizeof(buf) - 1] = 0;
            printf("Read data: %s\n", buf);

            /* Handle commands */
            if (!strncmp(buf, "DOWN", sizeof(buf))) {
                down_flag = 1;
                break;
            }
            if (!strncmp(buf, "END", sizeof(buf))) {
                break;
            }
            /* Add received summand */
            result += atoi(buf);
        }
        if (-1 == num_rd) {
            perror("Read error");
        }

        /* Send result */
        sprintf(buf, "%d", result);
        ret = write(cfd, buf, sizeof(buf));
        if (-1 == ret) {
            perror("Write error\n");
            goto _exit;
        }
        close(cfd);
        /* Quit on DOWN command */
        if (down_flag) {
            break;
        }
    }
_exit:
    close(sfd);
    return 0;
}

client.c:

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

int main(int argc, char *argv[]) {
    struct sockaddr_in addr;
    int ret;
    int data_socket;
    char buf[100] = {0};
    int i = 0;

    data_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == data_socket) {
        perror("Create client socket error");
        exit(EXIT_FAILURE);
    }

    /* Connect to server socket */
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888);
    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    ret = connect(data_socket, (const struct sockaddr *) &addr, sizeof(addr));
    if (-1 == ret) {
        perror("Connect error");
        exit(EXIT_FAILURE);
    }

    /* Send arguments */
    for (i = 1; i < argc; i++) {
        ret = write(data_socket, argv[i], strlen(argv[i]) + 1);
        if (-1 == ret) {
            perror("Write error");
            break;
        }
    }
    strcpy(buf, "END");
    ret = write(data_socket, buf, strlen(buf) + 1);
    printf("write %s to socket, ret = %d\n", buf, ret);
    if (-1 == ret) {
        perror("Write to socket error");
        exit(EXIT_FAILURE);
    }
    /* Read the result */
    memset(buf, 0, sizeof(buf));
    ret = read(data_socket, buf, sizeof(buf));
    if (-1 == ret) {
        perror("Read from client socket error");
        exit(EXIT_FAILURE);
    }
    buf[sizeof(buf) - 1] = 0;
    printf("Result = %s\n", buf);
    close(data_socket);
    exit(EXIT_SUCCESS);
}

运行客户端几次,服务器将read()在某些时候阻止呼叫:

$ for i in {1..100}; do ./client 3 4 5 6; done
write END to socket, ret = 4
Result = 18
write END to socket, ret = 4

服务器输出:

$ ./server
Waiting to accept a connection...
Accepted socket fd = 4
Read data: 3
Read data: 4
Read data: 5
Read data: 6
Read data: END
Waiting to accept a connection...
Accepted socket fd = 4
Read data: 3

服务器阻塞while ((num_rd = read(cfd, buf, sizeof(buf))) > 0)在线。

编辑:我的问题是为什么read()块。AFAIKread()将阻塞,直到从套接字读取至少1个字节的数据为止。在这种情况下,客户端发送的数据多于服务器读取的数据,因此我认为有可用的数据要从套接字读取。那么为什么read()仍然会阻塞?

神秘

问题的核心是代码测试缓冲区中的第一条消息,而忽略了同一缓冲区可能包含多条消息,部分消息或任何其他组合的可能性(请参阅edit)。因此,END有时会忽略该消息,并且read循环永远不会终止。

该代码假定一个单一的 read将获得哪些好处一个单一的 write通话送了。

当客户端和服务器都在同一台计算机上时,这是非常不准确的,很少是真的,并且可能仅在某些情况下有效。

一个人read可能write同时读取2个电话,或者可能读取一半write电话,然后再读取1.5个write电话...

TCP / IP(与UDP不同)是一种流协议,并且不了解消息边界。


编辑

为了弄清楚(按注释中的要求),假设有一个调用来read收集以下数据"1234\0EN"(下一个read将收集"D\0")...该程序做什么?

另一种可能的情况是writes一次性读取所有内容。即,buf包含字符串"3\04\05\06\0END\0"

此时在循环内会发生什么?

在此示例场景中,if语句(strncmp(buf, "END", sizeof(buf))始终false(且不安全),导致服务器永不中断while(read)循环。

由于while循环继续进行,read因此在没有可用数据时,服务器将尝试另一个,导致服务器阻塞,直到客户端发送其他数据为止。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

TCP 套接字客户端和服务器在一个程序中使用 c Linux

Linux客户端与linux / windows服务器与android之间的套接字编程

C ++套接字-服务器不接受多个客户端(Linux)

无法在Matlab中的UDP客户端和Linux中的服务器之间发送数据

有关C语言(Linux)中套接字服务器客户端编程中accept()的问题

非阻塞 Linux 服务器套接字

Linux:TCP 套接字侦听:如何检测无法关闭套接字的客户端?

在linux网络套接字服务器计算机上,当为客户端分配所有网络端口时会发生什么

Python 2.7.8:套接字-TCP套接字中的客户端服务器数据丢失

Linux上的Boost和ssl客户端服务器构建问题

在Linux服务器上处理多个客户端连接的正确方法是什么

Linux上的DHCPv6服务器+客户端

如何在运行 Tomcat 的 linux 服务器上委派 kerberos 客户端凭据?

Java TCP 客户端/服务器套接字

TCP服务器的异步读取使用boost :: asio打印客户端套接字发送的数据

将数据从Python TCP套接字(作为服务器)发送到JS客户端

Linux中的套接字TCP - 客户端无法插入用户名

带套接字的TCP客户端/服务器,服务器向客户端发送文件,客户端挂起,Python

Spring Integration和TCP服务器套接字-如何向客户端发送消息?

通过python中的TCP套接字在客户端-服务器之间发送文件?

客户端js上的套接字服务器?

TCP套接字,发送文件,客户端服务器,服务器不保存整个文件,C语言

C posix套接字,无法从客户端向服务器发送数据

Java套接字-将数据从客户端发送到服务器

Java套接字-将数据从服务器发送到客户端

在Linux客户端计算机上挂载Samba共享(Linux服务器)

如何使用 Python 将数据从客户端套接字发送到服务器套接字?

使用套接字通过TCP从服务器端向客户端发送ArrayList <String>?

Clinet端的read()获取消息的随机尾随字符(使用套接字的TCP客户端-服务器)