4

我正在了解 Winsock,并且在发送和接收简单字符串时遇到了一个奇怪的问题。这是我的代码(纯 C):

客户:



//...
//Declarations and stuff

//----------- SEND SOME DATA -------------------------------------------------

    char string1[] = "string-1";
    int bytes_sent = 0;

    bytes_sent = send(client_socket, string1, strlen(string1), 0);  

    printf("BYTES SENT: %i\n", bytes_sent);
    printf("\n-----------------------------------------------\n\n");

    system("pause");

//...

服务器:



//...
//Declarations and stuff

//----------- START LISTENING FOR REQUESTS ------------------------------------

    SOCKET ClientSocket;

    #define BUFFER_SIZE 256

    int size;
    struct sockaddr_in client_info;
    char client_ip[16];
    char data_received[BUFFER_SIZE];    
    int bytes_received = 0; 

    listen(ListenSocket, SOMAXCONN);

    while(1){           

        ClientSocket = accept(ListenSocket, (struct sockaddr *)&client_info, &size);        
        strcpy(client_ip, inet_ntoa(client_info.sin_addr));     

        do{

            bytes_received = recv(ClientSocket, data_received, BUFFER_SIZE, 0);

            if(bytes_received > 0){
                printf("DATA RECEIVED FROM %s: %s (%i bytes)\n", client_ip, data_received, bytes_received);
            }


        }while(bytes_received > 0);

        printf("\n-----------------------------------------------\n\n");


    }

//...

问题是服务器打印了我的字符串+一些奇怪的符号(见图)。

奇怪的符号

我使用流套接字。这个例子很简单,所以我不知道有什么问题。如果我随机修改字符串或服务器的缓冲区大小,或两者兼而有之,问题就会消失(服务器打印 OK 字符串)。如果在 send() 调用中我使用 sizeof() 而不是 strlen(),问题就解决了。我有点迷失在这里。如果我遗漏了什么,请多多关照,这是我在这里的第一篇文章。我可以提供整个代码(它基本上是 winsock 启动和套接字定义)。

4

3 回答 3

9

您发送的数据不包含终止空字符:

bytes_sent = send(client_socket, string1, strlen(string1), 0);

...因为strlen不计算终止的空值。这本身并不完全是问题,而是与接收方的事实相结合:

char data_received[BUFFER_SIZE];
// ...
bytes_received = recv(ClientSocket, data_received, BUFFER_SIZE, 0);

data_received未初始化,您最多可以接收BUFFER_SIZE字节。这意味着由于您发送的数据不是以空值结尾的:

  • 如果bytes_received < BUFFER_SIZE,其余部分data_received 可能未初始化,因此访问/打印将是未定义的行为。正如文档所说,实际上并不是 100% 清楚:

    [...] 调用 recv 将返回当前可用的尽可能多的数据——最大为指定的缓冲区大小 [...]

    ...所以这可能意味着缓冲区的其余部分保持不变。

  • 如果bytes_received == BUFFER_SIZE,则没有空终止符,因此printf将通过尝试打印来调用未定义的行为,因为它不知道字符串在哪里停止并且会溢出数组。

解决这些问题的最简单方法是发送空终止符:

bytes_sent = send(client_socket, string1, strlen(string1)+1, 0); // +1 here
bytes_sent = send(client_socket, string1, sizeof(string1), 0);   // same as above

...或少接收一个字节并将空终止符放在接收大小上:

bytes_received = recv(ClientSocket, data_received, BUFFER_SIZE-1, 0); // -1 here
data_received[bytes_received] = 0;

我个人会选择第一个。

于 2013-01-18T18:32:48.393 回答
3

所以问题是您没有发送终止NUL字节,但您似乎将接收到的字符串视为 C 字符串(即您假设它是 NUL 终止的)。修复它,而不是

bytes_sent = send(client_socket, string1, strlen(string1), 0);

bytes_sent = send(client_socket, string1, strlen(string1) + 1, 0);

此外,您提到“没有人使用strlen(s) + 1” - 可能是因为他们关注接收方接收到的字节数。

于 2013-01-18T18:33:10.987 回答
-1

尝试设置所有字符串数据的长度,然后像这样终止服务器中的字符串:

bzero(data_received, sizeof(data_received));
bytes_received = recv(ClientSocket, data_received, BUFFER_SIZE, 0);
data_received[bytes_received] = '\0';

如果这不能解决,也许@H2CO3 可以帮助您更好地阅读您的要求:]

于 2013-01-18T18:21:19.543 回答