Click here to Skip to main content
15,886,873 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hello. I am trying to implement tcp server and client. My client close connection after every data transfer. When I run my server against my client there is no problem, but when trying to run against netcat it blocks on accept(). Where is my logic broken?

Here is the code:
//
// Compile with:
// $ g++ tcp_server_heartbeat.cpp -o serv_timer -lpthread
//


#include <pthread.h>
#include <thread>
#include <atomic>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>

#define INDEX_ZERO ( 0 )
#define INDEX_ONE  ( 1 )
#define HEARTBEAT_PAYLOAD_SIZE_IN_BYTES ( 2 )
#define ZERO_BUF( buffer ) ( memset(buffer, 0, HEARTBEAT_PAYLOAD_SIZE_IN_BYTES ) )
#define SA struct sockaddr

#define TIMER_TICK_INTERVAL_MS ( 500 )
#define MAX_TIMER_TICKS ( 15 )

#define HEARTBEAT_SERVER_TO_CLIENT ( 's' )
#define HEARTBEAT_CLIENT_TO_SERVER ( 'c' )

#define NETCAT_LINE_FEED ( '\n' ) 
#define BACKLOG 5
//
// Function prototypes
//

void* tcp_server_thread_cb(void* param);
void* timer_thread_cb(void* param);
void tcp_server_send_packet(const char* ip, int port, uint8_t* payload, size_t len, bool await_response);

//
//Global variables accross server thread
//

int serv_sock;

//
// Global variables accross different threads
//
std::atomic<uint16_t> timer_ticks(0);

//
// Thread argument typedefs
//
typedef struct {
    uint16_t limit;
} timer_thread_args;

typedef struct {
    const char* ip;
    int port;
} tcp_server_thread_args;


//
// Timer thread callback
//
void* timer_thread_cb(void *param)
{
    while ( true ) 
    {
        std::this_thread::sleep_for( std::chrono::milliseconds(TIMER_TICK_INTERVAL_MS) );
        
        if ( timer_ticks >= ((timer_thread_args *)param)->limit )
        {    
            printf("Timeout, entering FAIL SAFE STATE!\n"); // In production will may execute the "Enter Fail Safe State" only once, but here we want intrusive output log.
        }
        else
            timer_ticks++;
    }

    return nullptr;
}

//
// TCP Server init
//

bool init_server(const char* ip, int port)
{
    struct sockaddr_in server;
    server.sin_family      = AF_INET;
    server.sin_addr.s_addr = inet_addr(ip);
    server.sin_port        = htons(port);
    socklen_t servaddr_len = sizeof(server);
    
    if ( ( serv_sock = socket(AF_INET, SOCK_STREAM, 0) ) < 0 ) // If socket creation fails, goto finalize
    {
        perror("Socket()");
        close(serv_sock);
        goto finalize;
    }

    if(bind(serv_sock,(SA*)&server, servaddr_len) != 0)
    {
        printf("Binding failed...\n");
        perror("bind(): "); 
        close(serv_sock);
        goto finalize;
    }
    else
    {
        if ((listen(serv_sock, BACKLOG)) != 0) 
        {
            printf("Listen failed...\n");
            close(serv_sock);
            goto finalize;
        }
        else
        {
            printf("Server listening at port: %d..\n", port);
            return true;
        }
    }

    finalize:
    return false;

}

//
// TCP Server send packet
//
void tcp_server_send_packet(const char* ip, int port, uint8_t* payload, size_t len, bool await_response)
{
    struct sockaddr_in client;
    int client_sock;
    socklen_t cli_len = sizeof(client);
    size_t recv_ret;
    int send_res;
    
    client_sock = accept(serv_sock, (SA*)&client,&cli_len );

    if(client_sock < 0)
    {
        perror("accept()\n");
        goto finalize;
    }

    //Set client socket to non blocking
    fcntl(client_sock, F_SETFL, O_NONBLOCK);
    
    //Send heartbeat payload to client 
    send_res = send(client_sock, payload, len, 0);
    if(send_res < 0)
    {
        //If sent is unsuccessfull close the connection and retry because function is in while loop 
        perror("send(): ");
        goto finalize;
    }
    
    //Zero the buffer
    ZERO_BUF( payload );

    //Receive client response in this case the heartbeat
    recv_ret = recv(client_sock, payload, len, 0);

    //Check if there is no response 
    if(recv_ret < 0)
    {
        //Go to finalize to close the connection
        perror("recv()");
        goto finalize;
    }

    //Check is this the heartbeat payload size
    if(recv_ret == HEARTBEAT_PAYLOAD_SIZE_IN_BYTES)
    {
        printf("Recv() %lu bytes from client.\n", recv_ret);
        for ( int i = 0; i < recv_ret; i++ )
            printf( "<--- payload[%d] = %c\n", i, payload[i] );

        //Check is this the heartbeat payload
        if ( payload[INDEX_ZERO] == HEARTBEAT_CLIENT_TO_SERVER) // If the client responds with client-to-server heartbeat, reset the timer
        {
            timer_ticks = 0; // Reset the timer
            printf("Recv() HEARTBEAT_CLIENT_TO_SERVER and now will reset the timer.\n");

            //No need to send again the same response only reset the timer 

        }
        else
        printf("Recv() could not receive a heartbeat from the client, returned: %ld\n", recv_ret);
        
        goto finalize; // In all cases, proceed to finalize
    }

    finalize:
    close(client_sock);
	return;
}


//
// TCP Server thread callback
//
void* tcp_server_thread_cb(void* param)
{   
    while(! init_server(((tcp_server_thread_args *)param)->ip, ((tcp_server_thread_args *)param)->port))
    {
        printf("Trying to init the server\n");
    };
    
    uint8_t payload[HEARTBEAT_PAYLOAD_SIZE_IN_BYTES]  = { 0 }; 

    while ( true )
    {
        // Send the server-to-client heartbeat
        payload[INDEX_ZERO] = HEARTBEAT_SERVER_TO_CLIENT;
        payload[INDEX_ONE] = NETCAT_LINE_FEED;

        tcp_server_send_packet( ((tcp_server_thread_args *)param)->ip, ((tcp_server_thread_args *)param)->port, payload, HEARTBEAT_PAYLOAD_SIZE_IN_BYTES, true );
        std::this_thread::sleep_for( std::chrono::seconds(1) ); // One seconds delay for comfortable log observation...
    }

    return nullptr;
}

int main (void)
{
    cpu_set_t cpuset;
    
    timer_thread_args timer_thread_args_obj = { .limit = MAX_TIMER_TICKS };
    tcp_server_thread_args tcp_server_thread_args_obj = { .ip = "127.0.0.1", .port = 5505 };

    pthread_t timer_thread;
    pthread_t tcp_server_thread;

    pthread_create( &timer_thread, 0, timer_thread_cb,  (void *)&timer_thread_args_obj );
    pthread_create( &tcp_server_thread, 0, tcp_server_thread_cb,  (void *)&tcp_server_thread_args_obj );

    int s;
    
    CPU_SET(2, &cpuset);
    s = pthread_setaffinity_np(timer_thread, sizeof(cpuset), &cpuset);
    if (s != 0)
        printf("FATAL: Could not set CPU core affinity for the timer thread...\n");

    CPU_SET(3, &cpuset);
    s = pthread_setaffinity_np(tcp_server_thread, sizeof(cpuset), &cpuset);
    if (s != 0)
        printf("FATAL: Could not set CPU core affinity for the tcp client thread...\n");

    pthread_join( timer_thread, nullptr );
    pthread_join( tcp_server_thread, nullptr );
    
    return 0;
}


What I have tried:

I think I must somehow call accept() if there is no client open client socket, but with getsockopt() I don't achieve success.
Posted
Updated 9-Nov-22 5:14am
Comments
k5054 8-Nov-22 9:04am    
How are you invoking netcat? The issue might be on that side rather than the code presented here.
Деян Цонев 8-Nov-22 9:46am    
With command nc localhost portnumb
k5054 8-Nov-22 10:09am    
If you're trying to connect your client to netcat, then netcat needs to be in listen mode nc -l localhost portnum
Деян Цонев 8-Nov-22 10:20am    
Netcat is the client in my case, not the server. Thanks.

1 solution

Your tcp_server_send_packet() function builds and tears down a connection for every "heartbeat" packet. That might not be what you intend.

netcat does not timeout a closed connection, but you can get it to do so by adding an idle timeout e.g
Shell
nc -l 0.1 localhost 5050
If I do that, I get an s sent by the server, then the connection ends. Only you can tell if that's the correct behaviour or not.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900