【问题标题】:TCP Server multiple packet sending issueTCP Server 多包发送问题
【发布时间】:2020-12-05 04:58:11
【问题描述】:

我已经使用 linux tcp 套接字编写了服务器客户端程序。

客户端通过以下方式向服务器请求当前目录文件列表 发送 ls 命令

server replies all the list of files in server dir.
I was testing it for more files in server working dir.

server response format in the buffer
file/dir [tab] file_name [tab] file_change_time
for each 1000 files to client.

服务器发送代码:

#define BUFSIZE 1400

void lsfun(node_t *pclient)
{
    DIR *directory;
    int status;
    int cpylen = 0;
    int msglen = 0;
    unsigned int tt_count = 0;
    unsigned int no_files = 0;
    unsigned int no_sends = 0;
    int clientfd = *(pclient->client_socket);
    char *filectime;
    char *buffer = malloc(BUFSIZE * sizeof(char));
    char *tmp = malloc(BUFSIZE * sizeof(char));
    char ending[] = "#####";
    struct dirent *dir;
    struct stat type;

    pthread_mutex_lock(&lock);
    chdir(pclient->pwd);
    directory = opendir(".");
    pthread_mutex_unlock(&lock);

    if(tmp == NULL || buffer == NULL)
        printf("malloc error for client conn:%d\n", clientfd);

    if(directory)
    {
        while((dir = readdir(directory)) != NULL)
        {
            if(!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, ".."))
                continue;
            status = stat(dir->d_name, &type);

            if(status == 0)
            {
                filectime = ctime(&type.st_ctime);

                if(dir->d_type != DT_REG)
                    cpylen = snprintf(tmp, BUFSIZE, "dir\t%s\t%s", dir->d_name, filectime);
                else
                    cpylen = snprintf(tmp, BUFSIZE, "file\t%s\t%s", dir->d_name, filectime);

                tmp[cpylen] = 0;

                if((cpylen + msglen) < BUFSIZE)
                {
                    strlcpy(buffer + msglen, tmp, cpylen);
                    msglen += cpylen;
                    no_files += 1;
                }
                else
                {
                    tt_count += msglen;
                    printf("%s", buffer);
                    fflush(stdout);
                    send(clientfd, buffer, strlen(buffer), 0);
                    memset(buffer, 0, BUFSIZE + 5);
                    snprintf(buffer, cpylen, "%s", tmp);
                    msglen = cpylen;
                    cpylen = 0;
                    no_files += 1;
                    no_sends += 1;
                }
            }
            else
            {
                cpylen = snprintf(buffer + msglen, BUFSIZE, "%s%s\n", "file stat error:", dir->d_name);
                msglen += cpylen;
            }
            memset(tmp, 0, BUFSIZE);
        }
    }

    cpylen = strlen(buffer);
    if(msglen == cpylen)
        send(clientfd, buffer, strlen(buffer), 0);

    send(clientfd, ending, strlen(ending), 0);      //sending msg ending for client read to close

    printf("\nlssize :%d\tnofile:%d, msglen:%d\tcpylen:%d\tno_sends:%d\n", tt_count + msglen, no_files, msglen, cpylen, no_sends);

    free(tmp);
    free(buffer);
    closedir(directory);
}


客户端接收代码:

#define BUFSIZE 1400
while(true)
{
    msgsize = read(socketfd, buffer, BUFSIZE);
    buffer[msgsize] = 0;
    snprintf(ending, 6, "%s", buffer + (strlen(buffer) - 5));

    if(strcmp(ending, "#####") == 0)
    {
        buffer[strlen(buffer) - 5] = 0;

        if(buffer[strlen(buffer) - 1] == '\n')
            printf("%s", buffer);
        else
            printf("%s\n", buffer);
        fflush(stdout);

        break;
    }
    else
    {
        printf("%s", buffer);
        memset(buffer, 0, BUFSIZE);
    }
}

服务器重放调试打印:

lssize :19931 nofile:501, msglen:437 cpylen:39 no_sends:14

为什么我只收到两个数据包而不是来自
的 14 个数据包 每个大约 1400 字节的服务器数据包?

错在哪里?

也欢迎任何代码改进建议。

【问题讨论】:

  • 如果您在代码中添加 [c] 标签,您还将获得更多的读者。资深读者在 S.O.只读标记有他们感兴趣的领域的问题。祝你好运。
  • 您必须正确、完整地处理 send/recv 等系统调用返回的结果
  • @Sekhar '客户端将始终只从服务器接收 1400 字节',如果您使用 TCP 字节流,则不能这么说。
  • 'tmp[cpylen] = 0;'似乎毫无意义,因为 strlcpy 不会复制它,并且是潜在的(尽管不太可能)OOB 写入。
  • bug 太多了。您需要在修复丢失的返回值处理等后测试和调试此代码。

标签: c linux sockets tcp tcpserver


【解决方案1】:

除了 cmets 中指出的错误之外,您的代码还存在更多基本问题,这些问题过于广泛而无法仅评论。

这段代码暗示多线程使用:

pthread_mutex_lock(&lock);
chdir(pclient->pwd);
directory = opendir(".");
pthread_mutex_unlock(&lock);

但是,此代码假定当前工作目录始终是当前函数的pclient-&gt;pwd

 status = stat(dir->d_name, &type);

如果另一个线程在循环运行时将chdir() 调用到另一个目录,则不会是这样,所以你的结果

stat()总是整个进程的当前工作目录检查相对路径。在您发布的代码中,可以更改。

要遵循的一个好的规则是,如果您正在编写多线程代码,则绝不会更改进程的任何全局属性。

您对snprintf() 的使用也容易出错。例如:

cpylen = snprintf(buffer + msglen, BUFSIZE, "%s%s\n", "file stat error:", dir->d_name);

7.21.6.5 The snprintf function, paragraph 2 of the C11 standard(加粗我的):

snprintf 函数返回如果 n 足够大时可能写入的字符数,不计算终止的空字符,如果发生编码错误,则返回负值。因此,当且仅当返回值为非负且小于 n 时,以 null 结尾的输出已被完全写入

您盲目地假设您对snprintf() 的每一次调用都有效。如果对snprintf() 的任何一次调用失败,则您的消息内容非常不确定,并且您对msglen 的值将无法准确反映缓冲区的内容。

这意味着此代码不会发送任何内容:

cpylen = strlen(buffer);
if(msglen == cpylen)
    send(clientfd, buffer, strlen(buffer), 0);

【讨论】:

  • 非常感谢安德鲁,我同意你关于 chdir 和多线程的观点,pclient 是一个结构队列节点,对于我创建的每个线程池线程都是不同的。
猜你喜欢
  • 2022-01-23
  • 1970-01-01
  • 2020-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-13
  • 2013-08-23
  • 1970-01-01
相关资源
最近更新 更多