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:
#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
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);
int serv_sock;
std::atomic<uint16_t> timer_ticks(0);
typedef struct {
uint16_t limit;
} timer_thread_args;
typedef struct {
const char* ip;
int port;
} tcp_server_thread_args;
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");
}
else
timer_ticks++;
}
return nullptr;
}
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 )
{
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;
}
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;
}
fcntl(client_sock, F_SETFL, O_NONBLOCK);
send_res = send(client_sock, payload, len, 0);
if(send_res < 0)
{
perror("send(): ");
goto finalize;
}
ZERO_BUF( payload );
recv_ret = recv(client_sock, payload, len, 0);
if(recv_ret < 0)
{
perror("recv()");
goto finalize;
}
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] );
if ( payload[INDEX_ZERO] == HEARTBEAT_CLIENT_TO_SERVER)
{
timer_ticks = 0;
printf("Recv() HEARTBEAT_CLIENT_TO_SERVER and now will reset the timer.\n");
}
else
printf("Recv() could not receive a heartbeat from the client, returned: %ld\n", recv_ret);
goto finalize;
}
finalize:
close(client_sock);
return;
}
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 )
{
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) );
}
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.