Recv返回更多字节,则消息长度为

时间:2018-11-15 19:13:49

标签: c sockets

我刚刚阅读了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

1 个答案:

答案 0 :(得分:2)

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的完整缓冲区以及其中的任何内容,也许是垃圾,也许只是空白...谁知道?!

希望这很有道理。