gpt4 book ai didi

c - Recv 返回比消息长度多的字节

转载 作者:太空宇宙 更新时间:2023-11-04 07:51:48 25 4
gpt4 key购买 nike

我刚刚阅读了 recv 手册,但我在理解手册中的内容以及我的程序中实际发生的事情时遇到了一些问题。

手册说:

RETURN VALUE
... return the number of bytes received, or -1 if an error occurred.

所以我有以下可以编译和测试的程序:

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

#define PORT 8888
#define MAX_LEN_MSG 50
#define BACKLOG 5

int socket_sckt ( struct sockaddr_in *server );
int bind_sckt ( struct sockaddr_in *server, const size_t server_addr_len );
int listen_sckt ( void );
int accept_sckt ( struct sockaddr_in *client, const size_t *client_addr_len );
int getsockname_sckt ( struct sockaddr_in *server, const size_t *server_addr_len );
int getpeername_sckt ( struct sockaddr_in *client, const size_t *client_addr_len );
ssize_t recv_sckt ( char *const msg );

int serverID;
int clientID;

int main( void )
{
char msg[ MAX_LEN_MSG] = { 0 };
struct sockaddr_in server;
struct sockaddr_in client;
size_t server_addr_len = sizeof( server );
size_t client_addr_len = sizeof( client );

memset( &server, 0, server_addr_len);
memset( &client, 0, client_addr_len);

/// Socket, Bind and Listen
serverID = socket_sckt ( &server );
bind_sckt ( &server, server_addr_len );
listen_sckt( );

getsockname_sckt( &server, &server_addr_len );
printf("Start Server on...\n" );

clientID = accept_sckt ( &client, &client_addr_len );
getpeername_sckt( &client, &client_addr_len );

printf("Connected IP(%s) on Port(%d)\n", inet_ntoa( client.sin_addr), ntohs( client.sin_port));

ssize_t recv_ret = recv_sckt ( msg );
if ( recv_ret > 0 )
{
printf("The MSG is %s | Len = %zu\n", msg, strlen( msg ) );
printf("The recived length is %zd\n", recv_ret );
}

}

int socket_sckt ( struct sockaddr_in *server )
{
int socket_ret = socket( AF_INET , SOCK_STREAM , 0);
if ( socket_ret == -1 )
{
printf( "Error, socket()\n" );
fprintf( stderr, "socket: %s (%d)\n", strerror( errno ), errno );
exit( EXIT_FAILURE );
}
server->sin_family = AF_INET;
server->sin_addr.s_addr = INADDR_ANY;
server->sin_port = htons( PORT );

return socket_ret;
}

int bind_sckt ( struct sockaddr_in *server, const size_t server_addr_len )
{
int bind_ret = bind( serverID, ( struct sockaddr * )server, ( socklen_t )server_addr_len );
if ( bind_ret == -1 )
{
printf( "Error, bind()\n" );
fprintf( stderr, "bind: %s (%d)\n", strerror( errno ), errno);
exit ( EXIT_FAILURE );
}

return bind_ret;
}

int listen_sckt ( void )
{
int listen_ret = listen( serverID, BACKLOG );
if ( listen_ret == -1 )
{
printf( "Error, listen()\n" );
fprintf( stderr, "listen: %s (%d)\n", strerror( errno ), errno );
exit( EXIT_FAILURE );
}

return listen_ret;
}

int accept_sckt ( struct sockaddr_in *client, const size_t *client_addr_len )
{
int accept_ret = accept( serverID, (struct sockaddr*) client, (socklen_t*) client_addr_len);
if ( accept_ret == -1 )
{
printf( "Error, accept()\n" );
fprintf( stderr, "accept: %s (%d)\n", strerror( errno ), errno );
exit( EXIT_FAILURE );
}

return accept_ret;
}

int getsockname_sckt ( struct sockaddr_in *server, const size_t *server_addr_len )
{
int getsockname_sckt = getsockname( serverID, ( struct sockaddr* ) server, ( socklen_t* ) server_addr_len );
if ( getsockname_sckt == -1 )
{
printf( "Error, getsockname()\n" );
fprintf( stderr, "getsockname: %s (%d)\n", strerror( errno ), errno );
exit ( EXIT_FAILURE );
}
return getsockname_sckt;
}

int getpeername_sckt ( struct sockaddr_in *client, const size_t *client_addr_len )
{
int getpeername_ret = getpeername( clientID, ( struct sockaddr* ) client, ( socklen_t* ) client_addr_len );
if ( getpeername_ret == -1 )
{
printf( "Error, getpeername()\n" );
fprintf( stderr, "getpeername: %s (%d)\n", strerror( errno ), errno );
exit ( EXIT_FAILURE );
}

return getpeername_ret;
}

ssize_t recv_sckt ( char *const msg )
{
ssize_t recv_ret = recv( clientID, msg, MAX_LEN_MSG, 0 );
if ( recv_ret == -1 )
{
printf( "Error, recv()\n" );
fprintf( stderr, "recv: %s (%d)\n", strerror( errno ), errno );
exit( EXIT_FAILURE );
}
msg[ strcspn( msg, "\n" ) ] = 0;
msg[ recv_ret ] = 0;
return recv_ret;
}

现在,如果我使用客户端连接到此服务器:

Please enter your name: George
Connect to Server: 192.168.0.103:8888
You are: 192.168.0.103:60878

在服务器端我得到:

    Start Server on...
Connected IP(192.168.0.103) on Port(60878)
The MSG is George | Len = 6
The recived length is 31

为什么接收到的长度是31而不是6

最佳答案

recv() 函数不会\0 终止缓冲区,在 recv_sckt() 函数中将缓冲区清零。

ssize_t recv_sckt ( char *const msg )
{
// Zero out the buffer
memset(msg, '\0', MAX_LEN_MEG);

ssize_t recv_ret = recv( clientID, msg, MAX_LEN_MSG, 0 );
if ( recv_ret == -1 )
{
printf( "Error, recv()\n" );
fprintf( stderr, "recv: %s (%d)\n", strerror( errno ), errno );
exit( EXIT_FAILURE );
}
msg[ strcspn( msg, "\n" ) ] = 0;
msg[ recv_ret ] = 0;
return recv_ret;
}

更新:

啊啊啊,我刚刚意识到 - msg[strcspn(msg, '\n')] = 0; 行将第一个 \n 替换为 0 (或 \0)。

所以实际上它使缓冲区的一部分不可读。

也许不是

return recv_ret;

尝试:

return strlen(msg);

希望这能解决问题,我不确定服务器实际发送给您的是什么。

另一种方法可能是删除 msg[strcspn(msg, '\n')] = 0; 行,尽管我不确定 \n 所以可能会产生不希望的结果。

此外,您仍然需要确保正确终止缓冲区。

更新#2

我不确定服务器及其发送的内容,我指出的那一行:

msg[strcspn(msg, '\n')] = 0;`

基本上是在返回缓冲区中找到第一个 \n 字符并将其转换为 0 或 \0 字符。 \0 告诉像 printf() 这样的函数缓冲区在哪里结束。

但是,recv() 函数会返回一个数字,告诉您接收到的字节数。为了参数的缘故,让我们假设服务器正在返回这个字符串:

George\nabcdef

msg[strcspn(msg, '\n')] = 0; 行发现 \n 并将其替换为 \0(或文字 0 值)。

所以 recv() 告诉你,嘿,看 - 我实际上读取了 13 字节,但是你让其中一些字节不可访问,因为它们超出了 >\0 终止符。

您会在 SO 和 Google 搜索中找到大量关于 c 中字符串终止的内容。您可以按照我在最初的回复中所说的那样做,并以零终止整个缓冲区:

memset(msg, '\0', buffer_size);

为了使数据以零结尾,我会尝试 - 删除 msg[strcspn(msg, '\n')] = 0; 行并打印出完整的缓冲区,包括\n 以及它之外的任何东西,也许是垃圾,也许只是空白……谁知道呢?!

希望这是有道理的。

关于c - Recv 返回比消息长度多的字节,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53326459/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com