TCP Server多个数据包发送问题

问题描述

我已经使用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,"file\t%s\t%s",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,BUFSIZE + 5);
                    snprintf(buffer,cpylen,"%s",tmp);
                    msglen = cpylen;
                    cpylen = 0;
                    no_files += 1;
                    no_sends += 1;
                }
            }
            else
            {
                cpylen = snprintf(buffer + msglen,"%s%s\n","file stat error:",dir->d_name);
                msglen += cpylen;
            }
            memset(tmp,BUFSIZE);
        }
    }

    cpylen = strlen(buffer);
    if(msglen == cpylen)
        send(clientfd,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,no_sends);

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


客户收到代码

#define BUFSIZE 1400
while(true)
{
    msgsize = read(socketfd,BUFSIZE);
    buffer[msgsize] = 0;
    snprintf(ending,6,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,BUFSIZE);
    }
}

服务器重播调试打印:

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

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

错误在哪里?

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

解决方法

除了注释中指出的错误外,您的代码还有一些更基本的问题,这些问题过于广泛,无法仅对其进行注释。

此代码暗示使用多线程:

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

,此代码假设当前工作目录始终是当前函数的pclient->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时,以零结尾的输出已被完全写入。

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

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

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