user10533142
user10533142

Reputation:

Recv return more bytes then the message lenght is

I just read the recv manual and I have some problem in understanding what the Manual says and what the actually happens in my Program.

The manual say:

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

So I have the following program which can be compiled and tested:

#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;
}

Now if I connect to this server with a client:

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

On the server side I get:

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

Why is the recived length 31 and not 6 ?

Upvotes: 0

Views: 435

Answers (1)

Nunchy
Nunchy

Reputation: 948

The recv() function does not \0 terminate the buffer, zero out the buffer in the recv_sckt() function.

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;
}

Update:

Aaaah, I just realised - the msg[strcspn(msg, '\n')] = 0; line is replacing the first \n with a 0 (or \0).

So in effect it's rendering part of the buffer unreadable.

Maybe instead of

return recv_ret;

Try:

return strlen(msg);

Hope that solves it, I'm not sure what the server is actually sending to you.

An alternative method might be to remove that msg[strcspn(msg, '\n')] = 0; line, although I'm unsure what junk may lie beyond that \n so may have undesired results.

Plus you'd still need to make sure you terminate the buffer properly.

Update #2

I'm not sure about the server and what it's sending, that line I pointed out:

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

Is basically finding the first \n character in the return buffer and turning it into a 0 or \0 character. The \0 tells functions like printf() where the buffer ends.

However, the recv() function returns a number telling you how many bytes are received. Let's imagine for arguments' sake that the server is returning this string:

George\nabcdef

The msg[strcspn(msg, '\n')] = 0; line finds that \n and replaces it with a \0 (or a literal 0 value).

So recv() is telling you, hey look - I actually read in 13 bytes, but you made some of those bytes inaccessible because they lie beyond the \0 terminator.

You'll find loads of stuff about string termination in c here on SO and with a Google search. You could do as I said in my initial response and zero-terminate the entire buffer:

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

So that the data is zero-terminated, I'd try that - remove the msg[strcspn(msg, '\n')] = 0; line and print out the full buffer including that \n and whatever lies beyond it, maybe junk, maybe just whitespace...who knows?!

Hope this makes sense.

Upvotes: 2

Related Questions