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

Posted

技术标签:

【中文标题】客户端 read() 获取消息的随机尾随字符(使用套接字的 TCP 客户端-服务器)【英文标题】:Clinet-side read() gets random trailing characters to the message (TCP client-server using sockets) 【发布时间】:2019-01-06 07:40:46 【问题描述】:

练习 C 网络编程:我正在编写一个简单的 TCP 客户端-服务器应用程序,它应该将消息(在每个客户端的单独线程中)作为字符串从服务器发送到客户端并在客户端(稍后将成为控制台商店应用程序)。我首先发送消息的大小,然后使用套接字write() 函数发送消息本身。服务器端:

void answer(void *arg) 
    struct thData tdL; 
    tdL= *((struct thData*)arg);

    // Welcome message
    char *initMsgToClient;
    getInitialMessage(&initMsgToClient);
    int len_initMsgToClient = strlen(initMsgToClient);

    // Returning message to client
    if ( write (tdL.cl, &len_initMsgToClient, sizeof(int)) <= 0 ) 
         printf("[Thread %d] ",tdL.idThread);
         perror ("[Thread] Error at write(): len_initMsgToClient to client.\n");
    
    if (write (tdL.cl, initMsgToClient, len_initMsgToClient) <= 0) 
         printf("[Thread %d] ",tdL.idThread);
         perror ("[Thread]Error at write(): initMsgToClient to client.\n");
    
    else
        printf ("[Thread %d] Message was sent with success.\n",tdL.idThread);



void getInitialMessage(char **paramMessage) 
    char *resultMessage = "Welcome to Console Shopper!";
    *paramMessage = resultMessage; 

在客户端,我首先读取大小和消息本身并打印它,但每次运行./client 时,我都会在消息中得到不同的随机尾随字符。客户端:

char welcomeMessageFromServer[512];
int lenWelcomeMsg;
// Reading size of first mesage: lenWelcomeMsg
if (read(sd, &lenWelcomeMsg, sizeof(int)) < 0) 
    perror ("[client] Error reading len welcome message from server.\n");

// Reading initial message from server: welcomeMessageFromServer
if (read(sd, welcomeMessageFromServer, lenWelcomeMsg) < 0) 
    perror ("[client] Error reading welcome message from server.\n");
    return errno;

printf("%s",welcomeMessageFromServer);
printf("\n");

为简单起见,我省略了添加套接字和线程处理的代码,大部分来自教程,因此应该是正确的。我很确定我对正在发送/接收的消息的大小做错了什么。 我对./client 多次运行的输出:

Welcome to Console Shopper!
Welcome to Console Shopper!�
Welcome to Console Shopper!������i0  
Welcome to Console Shopper!������UF+:  

有时它会做对。为了始终获得正确的信息,我应该更正什么?

【问题讨论】:

【参考方案1】:
// Reading initial message from server: welcomeMessageFromServer
if (read(sd, welcomeMessageFromServer, lenWelcomeMsg) < 0) 
    perror ("[client] Error reading welcome message from server.\n");
    return errno;

printf("%s",welcomeMessageFromServer);

这里有两个问题:

    %s 格式说明符仅适用于 C 样式字符串。为了使您收到的消息成为 C 风格的字符串,您需要在其末尾添加一个终止零字节。

    read 函数不能确保它接收到所有请求的字节。如果没有收到完整的消息,则需要检查返回值并再次调用它。

试试这样的函数:

ssize_t readAll (int fd, void *vbuf, size_t count)

     char *buf = (char *) vbuf;
     ssize_t read_so_far = 0;
     while (count > 0)
     
         ssize_t ret = read(fd, buf, count);
         if (ret < 0)
             return ret;
         if (ret == 0)
             return read_so_far;
         count -= ret;
         read_so_far += ret;
         buf += ret;
     
     return read_so_far;

那么你可以这样做:

// Reading initial message from server: welcomeMessageFromServer
if (readAll(sd, welcomeMessageFromServer, lenWelcomeMsg) != lenWelcomeMsg) 
    perror ("[client] Error reading welcome message from server.\n");
    return errno;

welcoemMessageFromServer[lenWelcomeMsg] = 0;
printf("%s",welcomeMessageFromServer);

【讨论】:

我应该做同样的事情来读取任何 int 或 read() 在整数的情况下应该是安全的吗? 您忽略了如果对等方正常断开连接,read() 返回 0 的可能性。 if (ret &lt; 0) 应该是 if (ret &lt;= 0) 否则你会陷入死循环。调用者必须决定它想如何处理readAll() &lt; count(因为某些协议可以在读取过程中断开连接,例如 FTP,而其他协议则不行)。 @Elliad 你应该做同样的事情来阅读int,很好。谁知道呢,你可能只读取一个字节。并阅读上面关于正常断开连接导致read 返回零的可能性的评论。 (我对答案中的代码进行了一些可能的更新,这样至少它不会无休止地循环。) 你是否忽略了初始化ssize_t ret_so_far = 0

以上是关于客户端 read() 获取消息的随机尾随字符(使用套接字的 TCP 客户端-服务器)的主要内容,如果未能解决你的问题,请参考以下文章

尾随分隔符使熊猫 read_csv 感到困惑

在 C# 中获取等效的正则表达式以切断前导和尾随子字符串

js-生成随机字符串

UNIX API调用:使用read()函数打开文件并将其打印到屏幕会添加额外的随机字符

代码在输出末尾留下随机字符串[重复]

需要使用 selenium Java 验证带有多个随机字符串的错误消息