Recv возвращает больше байтов, чем длина сообщения - PullRequest
0 голосов
/ 15 ноября 2018

Я только что прочитал руководство 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 голосов
/ 15 ноября 2018

Функция 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 терминатора.Вы найдете множество вещей о завершении строки в c здесь на SO и с поиском Google.Вы можете сделать, как я сказал в моем первоначальном ответе, и завершить нулем весь буфер:

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

Чтобы данные заканчивались нулем, я бы попробовал - удалите строку msg[strcspn(msg, '\n')] = 0; и напечатайтеполный буфер, включая этот \n и все, что находится за его пределами, может быть, мусор, может быть, просто пробел ... кто знает?!

Надеюсь, это имеет смысл.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...