Related
I am working on threaded TCP socket server for handling multiple socket client connection. Clients can connect and disconnect asynchronously with server, upon connection, the client should send some data in predefined custom packet protocol format.
The protocol has start of frame(SOP) and end of frame (EOP) defined.
I have written a C code such that for each successful client connection, a thread gets created that keeps on receiving the bytes from client in the predefined packet format, the thread has a thread-local state machine because each client can connect asynchronously so the states for each client may be different.
Below is the thread that receives that data from client and maintains a state based on the type of byte received:
static void *receive_handler(void *args) {
struct thread_args *local_args = args;
struct sockaddr_in6 *client_address = local_args->client_address;
//struct itimerval timer_val;
int32_t conn_fd = local_args->conn_fd;
int32_t val_read = 0;
int32_t resp_code = 0;
uint32_t sendBuffLen = 0;
int8_t buffer[BUFFER_SIZE] = { 0 };
uint8_t RetBuff[1024] = { 0 };
int8_t rx_addr_str[INET6_ADDRSTRLEN];
int8_t byte = 0;
int16_t idx = ePacketType;
int16_t packet_len = 0;
int16_t calculated_crc = 0, recv_crc = 0;
uint16_t num_bytes = 0;
memset(rx_addr_str, 0, INET6_ADDRSTRLEN);
inet_ntop(AF_INET6, &(client_address->sin6_addr), rx_addr_str, INET6_ADDRSTRLEN);
printf("\nRx Thread (%d) Created for %s\n", local_args->connection_no, rx_addr_str);
int eState = eStart_Frame;
memcpy(rx_Packet_Info[local_args->connection_no].inet6, rx_addr_str, INET6_ADDRSTRLEN);
//timerclear(&timer_val.it_interval); /* zero interval means no reset of timer */
//timerclear(&timer_val.it_value);
//timer_val.it_value.tv_sec = 10; /* 10 second timeout */
//(void) signal(SIGALRM, state_reset_handler);
while (1) {
if (eState != eChecksum_Verify) {
val_read = -1;
val_read = recv(conn_fd, &byte, sizeof(byte), 0);
debug_printf(INFO, "Amount Read: %d Byte Rxd: 0x%x => 0x%X\n", val_read, (byte & 0xFF), byte);
if (val_read <= 0) {
if (parse_packet("ERR_DISCONNECT", rx_addr_str, local_args->connection_no) < 0) {
debug_printf(ERR, "Error parsing packet: %s\n", strerror(errno));
}
debug_printf(ERR, "May be closed by client %s: %s\n", rx_addr_str, strerror(errno));
debug_printf(ERR, "Exiting Rx Thread: ConnIdx: %d", num_connections);
close(conn_fd);
pthread_exit(NULL);
}
}
switch (eState) {
case eStart_Frame:
debug_printf(DEBG, "Current State: %d\n", eState);
if ((val_read > 0) && (byte & 0xFF) == SOP) {
memset(buffer, 0, BUFFER_SIZE);
val_read = -1;
buffer[eSOP] = (byte & 0xFF);
eState = eFrame_Len;
}
break;
case eFrame_Len: {
static char MSB_Rxd = 0;
debug_printf(DEBG, "Current State: %d\n", eState);
if (val_read > 0) {
if (MSB_Rxd == 0) {
buffer[ePacket_length] = byte;
MSB_Rxd = 1;
}
else {
buffer[ePacket_length + 1] = byte;
eState = eFrame;
num_bytes = 0;
MSB_Rxd = 0;
packet_len = (buffer[ePacket_length] & 0xFF << 8) | (buffer[ePacket_length + 1]);
debug_printf(INFO, "Packet Length: %d : 0x%x 0x%x\n", packet_len,
buffer[ePacket_length], buffer[ePacket_length + 1]);
}
}
}
break;
case eFrame:
debug_printf(DEBG, "Current State: %d\n", eState);
num_bytes++;
buffer[idx] = byte;
if (num_bytes == packet_len) {
eState = eEnd_Frame;
debug_printf(DEBG, "Num bytes: 0x%x\n", num_bytes);
}
else {
debug_printf(ERR, "Num bytes: 0x%x Pkt Len: 0x%x\n", num_bytes, packet_len);
}
idx++;
break;
case eEnd_Frame:
debug_printf(ERR, "Current State: %d val read %d\n", eState, val_read);
if ((val_read > 0) && (byte & 0xFF) == EOP) {
val_read = -1;
eState = eChecksum_Verify;
}
break;
case eChecksum_Verify: {
calculated_crc = crc_16(&buffer[ePacket_length], (num_bytes));
recv_crc = buffer[num_bytes + 1] << 8 | (buffer[num_bytes + 2] & 0xFF);
if (calculated_crc != recv_crc) {
debug_printf(ERR, "CRC Error! CRC do not match!!\n");
debug_printf(ERR, "Calculated CRC: 0x%X\nCRC Rxd: 0x%X\n", calculated_crc, recv_crc);
resp_code = CRC_ERR;
send(conn_fd, &resp_code, sizeof(resp_code), 0);
}
else {
if (rx_Packet_Info[local_args->connection_no].packetUUID != NULL) {
free(rx_Packet_Info[local_args->connection_no].packetUUID);
rx_Packet_Info[local_args->connection_no].packetUUID = NULL;
}
rx_Packet_Info[local_args->connection_no].packetUUID = calloc(buffer[ePacketUUIDLen],
sizeof(uint8_t));
memcpy(rx_Packet_Info[local_args->connection_no].packetUUID, &buffer[ePacketUUID],
buffer[ePacketUUIDLen]);
rx_Packet_Info[local_args->connection_no].packetUUIDlength = buffer[ePacketUUIDLen];
printf("\nRX-Thread-UUID %d: ConnNo: %d\n", buffer[ePacketUUIDLen],
local_args->connection_no);
for (char i = 0; i < buffer[ePacketUUIDLen]; i++) {
printf("0x%x ", rx_Packet_Info[local_args->connection_no].packetUUID[i]);
}
printf("\n");
if (parse_packet(buffer, rx_addr_str, local_args->connection_no) < 0) {
debug_printf(ERR, "Error parsing packet: %s\n", strerror(errno));
}
}
num_bytes = 0;
eState = eStart_Frame;
idx = ePacketType;
}
break;
default:
debug_printf(DEBG, "Invalid State!! Should not come here.\n");
num_bytes = 0;
eState = eStart_Frame;
idx = ePacketType;
break;
}
}
return NULL;
}
My question is how should I reset this state machine if let's say after receiving start of frame the client gets stuck and is not able to send frame length or complete frame till end of frame?
One way I thought is to implement timer callback but I am not sure how should I keep track of state machine of multiple threads.
Can any one please suggest what should I do in this scenario or if I am doing anything wrong?
If I'm parsing the question correctly, you're asking about how to handle gracefully the situation where the connecting client isn't sending data in a timely manner -- i.e. it has sent the first part of a message, but (due to a network problem or a client-side bug or whatever) never sends the rest, leaving your server-side I/O thread blocked inside a recv() call for a long/indefinite time.
If so, the first question to ask is: is this really a problem? If each connection gets its own thread, then having one particular thread/connection blocked shouldn't cause any issues to the other threads, since they all execute independently of each other. So maybe you can just ignore the problem entirely?
However, the more likely answer is that ignoring the problem isn't quite good enough, because of a couple of subsequent problems that aren't easily ignorable: (a) what if too many client connections "freeze up" at the same time? One or two stalled TCP connections/threads isn't a big deal, but if the same problem keeps happening, eventually you'll run out of resources to spawn more threads or TCP connections, and then your server can no longer function. And (b) what if the server process wants to quit now? (i.e. because the server's user has sent it a SIGINT interrupt or similar) If one or more threads are blocked indefinitely, then it is impossible for the server to exit in a timely-and-controlled manner, because the main thread needs to wait for all the TCP threads to exit first before it can clean up its process-wide resources, and any blocked threads will not exit for a long time, if ever.
So, assuming that the problem does need to be addressed, the most reliable way I've found to address it is to never block in recv() (or send()) in the first place. Instead, make sure to put each socket in non-blocking mode, and have the thread's while-loop block only in a select() call instead. Doing it this way makes your state machine a bit more complex (since it will now have to handle partial-sends as well as partial-receives), but the compensating benefit is that the thread is now in better control of its own blocking behavior. In particular, you can tell select() to always return after a certain amount of time, no matter what, and (better yet) you can tell select() to return whenever any of a number of sockets has bytes ready to be read on it. That means that if your main thread wants to exit, it can use a pipe() or socketpair() to send a dummy-byte to each TCP thread, and the TCP thread (which is presumably blocked inside select(), waiting for either data from its client or from the pipe/socketpair socket) will immediately return from select(), see that the main thread has sent it a byte, and respond by exiting immediately.
That should be sufficient -- in my experience it is better not to impose fixed timeouts if you can avoid it, since it's hard to predict what network performance will be like in all cases, and any rule-of-thumb you might come up with (like "a client that doesn't send the whole message in 5 seconds must be broken") is likely to be wrong, and you'll end up with false-positive problems if you try to enforce that rule. Better to just let each client take as long as it wants/needs to, while also having a mechanism by which the main thread can request that a particular client thread exit immediately if/when that becomes necessary (e.g. during server-process shutdown, or if there are too many TCP threads active and you want to prune some of the old/inactive ones before spawning more)
After installing zmq and czmq with brew, I tried to compile and play the Asynchronous-Majordomo-Pattern but it did not work as it requires czmq v3. As far as I understood, I tried to update it to the v4, using zactor because
zthread is deprecated in favor of zactor http://czmq.zeromq.org/czmq3-0:zthread
So right now the following code looks fine to me as updated async-majordomo pattern, but it does not work as expected, It does not create any thread when I run it via my terminal.
// Round-trip demonstrator
// While this example runs in a single process, that is just to make
// it easier to start and stop the example. The client task signals to
// main when it's ready.
#include "czmq.h"
#include <stdlib.h>
void dbg_write_in_file(char * txt, int nb_request) {
FILE * pFile;
pFile = fopen ("myfile.txt","a");
if (pFile!=NULL)
{
fputs (txt, pFile);
char str_nb_request[12];
sprintf(str_nb_request, "%d", nb_request);
fputs (str_nb_request, pFile);
fputs ("\n", pFile);
fclose (pFile);
}
}
static void
client_task (zsock_t *pipe, void *args)
{
zsock_t *client = zsock_new (ZMQ_DEALER);
zsock_connect (client, "tcp://localhost:5555");
printf ("Setting up test...\n");
zclock_sleep (100);
printf("child 1: parent: %i\n\n", getppid());
printf("child 1: my pid: %i\n\n", getpid());
int requests;
int64_t start;
printf ("Synchronous round-trip test...\n");
start = zclock_time ();
for (requests = 0; requests < 10000; requests++) {
zstr_send (client, "hello");
// stuck here /!\
char *reply = zstr_recv (client);
zstr_free (&reply);
// check if it does something
dbg_write_in_file("sync round-trip requests : ", requests);
// end check
}
printf (" %d calls/second\n",
(1000 * 10000) / (int) (zclock_time () - start));
printf ("Asynchronous round-trip test...\n");
start = zclock_time ();
for (requests = 0; requests < 100000; requests++) {
zstr_send (client, "hello");
// check if it does something
dbg_write_in_file("async round-trip send requests : ", requests);
// end check
}
for (requests = 0; requests < 100000; requests++) {
char *reply = zstr_recv (client);
zstr_free (&reply);
// check if it does something
dbg_write_in_file("async round-trip rec requests : ", requests);
// end check
}
printf (" %d calls/second\n",
(1000 * 100000) / (int) (zclock_time () - start));
zstr_send (pipe, "done");
}
// Here is the worker task. All it does is receive a message, and
// bounce it back the way it came:
static void
worker_task (zsock_t *pipe, void *args)
{
printf("child 2: parent: %i\n\n", getppid());
printf("child 2: my pid: %i\n\n", getpid());
zsock_t *worker = zsock_new (ZMQ_DEALER);
zsock_connect (worker, "tcp://localhost:5556");
while (true) {
zmsg_t *msg = zmsg_recv (worker);
zmsg_send (&msg, worker);
}
zsock_destroy (&worker);
}
// Here is the broker task. It uses the zmq_proxy function to switch
// messages between frontend and backend:
static void
broker_task (zsock_t *pipe, void *args)
{
printf("child 3: parent: %i\n\n", getppid());
printf("child 3: my pid: %i\n\n", getpid());
// Prepare our sockets
zsock_t *frontend = zsock_new (ZMQ_DEALER);
zsock_bind (frontend, "tcp://localhost:5555");
zsock_t *backend = zsock_new (ZMQ_DEALER);
zsock_bind (backend, "tcp://localhost:5556");
zmq_proxy (frontend, backend, NULL);
zsock_destroy (&frontend);
zsock_destroy (&backend);
}
// Finally, here's the main task, which starts the client, worker, and
// broker, and then runs until the client signals it to stop:
int main (void)
{
// Create threads
zactor_t *client = zactor_new (client_task, NULL);
assert (client);
zactor_t *worker = zactor_new (worker_task, NULL);
assert (worker);
zactor_t *broker = zactor_new (broker_task, NULL);
assert (broker);
// Wait for signal on client pipe
char *signal = zstr_recv (client);
zstr_free (&signal);
zactor_destroy (&client);
zactor_destroy (&worker);
zactor_destroy (&broker);
return 0;
}
When I run it, it looks like the program is stuck at the comment
// stuck here /!\
Then when I kill it as it does not finish, or print anything at all, I need to press five time Ctrl+C ( ^C ). Only then, it looks more verbose on the console, like it was indeed running. => Note that I delete all my printf() steps' outputs, as it was really messy to read.
When it runs, it does not write anything to the file, called by the dbg_write_in_file() function, only after sending five Ctrl+C ( ^C ).
Both client worker and broker task return the same getppid number ( my terminal ) and getpid as the program itself.
I use gcc trippingv4.c -o trippingv4 -L/usr/local/lib -lzmq -lczmq to compile.
When I try to kill it :
./trippingv4
Setting up test...
child 1: parent: 60967
child 1: my pid: 76853
Synchronous round-trip test...
^Cchild 2: parent: 60967
child 2: my pid: 76853
^Cchild 3: parent: 60967
child 3: my pid: 76853
^C^C^CE: 18-02-28 00:16:37 [76853]dangling 'PAIR' socket created at src/zsys.c:471
E: 18-02-28 00:16:37 [76853]dangling 'DEALER' socket created at trippingv4.c:29
E: 18-02-28 00:16:37 [76853]dangling 'PAIR' socket created at src/zsys.c:471
E: 18-02-28 00:16:37 [76853]dangling 'DEALER' socket created at trippingv4.c:89
Update
Thanks for the detailed answer #user3666197. In first part, the compiler does not compile the assert call so I just show the value instead and compare visually, they are the same.
int czmqMAJOR,
czmqMINOR,
czmqPATCH;
zsys_version ( &czmqMAJOR, &czmqMINOR, &czmqPATCH );
printf( "INF: detected CZMQ ( %d, %d, %d ) -version\n",
czmqMAJOR,
czmqMINOR,
czmqPATCH
);
printf( "INF: CZMQ_VERSION_MAJOR %d, CZMQ_VERSION_MINOR %d, CZMQ_VERSION_PATCH %d\n",
CZMQ_VERSION_MAJOR,
CZMQ_VERSION_MINOR,
CZMQ_VERSION_PATCH
);
Output :
INF: detected CZMQ ( 4, 1, 0 ) -version
INF: CZMQ_VERSION_MAJOR 4, CZMQ_VERSION_MINOR 1, CZMQ_VERSION_PATCH 0
The zsys_info call does compile but does not show anything on the terminal, even with a fflush(stdout) just in case so I just used printf :
INF: This system's Context() limit is 65535 ZeroMQ socketsINF: current state of the global Context()-instance has:
( 1 )-IO-threads ready
( 1 )-ZMQ_BLOCKY state
Then I changed the global context thread value with zsys_set_io_threads(2) and/or zmq_ctx_set (aGlobalCONTEXT, ZMQ_BLOCKY, false);, still blocked. It looks like zactor does not works with systems threads as zthread was... or does not gives a similar behavior. Given my experience in zeromq (also zero) probably I trying something that can't be achieved.
Update solved but unproper
My main error was to not have properly initiate zactor instance
An actor function MUST call zsock_signal (pipe) when initialized and MUST listen to pipe and exit on $TERM command.
And to not have blocked the zactor's proxy execution before it called zactor_destroy (&proxy);
I let the final code below but you still need to exit at the end with Ctrl+C because I did not figure it out how to manage $TERM signal properly. Also, zactor still appears to not use system theads. It's probably design like this but I don't know how it's work behind the wood.
// Round-trip demonstrator
// While this example runs in a single process, that is just to make
// it easier to start and stop the example. The client task signals to
// main when it's ready.
#include <czmq.h>
static void
client_task (zsock_t *pipe, void *args)
{
assert (streq ((char *) args, "Hello, Client"));
zsock_signal (pipe, 0);
zsock_t *client = zsock_new (ZMQ_DEALER);
zsock_connect (client, "tcp://127.0.0.1:5555");
printf ("Setting up test...\n");
zclock_sleep (100);
int requests;
int64_t start;
printf ("Synchronous round-trip test...\n");
start = zclock_time ();
for (requests = 0; requests < 10000; requests++) {
zstr_send (client, "hello");
zmsg_t *msgh = zmsg_recv (client);
zmsg_destroy (&msgh);
}
printf (" %d calls/second\n",
(1000 * 10000) / (int) (zclock_time () - start));
printf ("Asynchronous round-trip test...\n");
start = zclock_time ();
for (requests = 0; requests < 100000; requests++) {
zstr_send (client, "hello");
}
for (requests = 0; requests < 100000; requests++) {
char *reply = zstr_recv (client);
zstr_free (&reply);
}
printf (" %d calls/second\n",
(1000 * 100000) / (int) (zclock_time () - start));
zstr_send (pipe, "done");
printf("send 'done' to pipe\n");
}
// Here is the worker task. All it does is receive a message, and
// bounce it back the way it came:
static void
worker_task (zsock_t *pipe, void *args)
{
assert (streq ((char *) args, "Hello, Worker"));
zsock_signal (pipe, 0);
zsock_t *worker = zsock_new (ZMQ_DEALER);
zsock_connect (worker, "tcp://127.0.0.1:5556");
bool terminated = false;
while (!terminated) {
zmsg_t *msg = zmsg_recv (worker);
zmsg_send (&msg, worker);
// zstr_send (worker, "hello back"); // Give better perf I don't know why
}
zsock_destroy (&worker);
}
// Here is the broker task. It uses the zmq_proxy function to switch
// messages between frontend and backend:
static void
broker_task (zsock_t *pipe, void *args)
{
assert (streq ((char *) args, "Hello, Task"));
zsock_signal (pipe, 0);
// Prepare our proxy and its sockets
zactor_t *proxy = zactor_new (zproxy, NULL);
zstr_sendx (proxy, "FRONTEND", "DEALER", "tcp://127.0.0.1:5555", NULL);
zsock_wait (proxy);
zstr_sendx (proxy, "BACKEND", "DEALER", "tcp://127.0.0.1:5556", NULL);
zsock_wait (proxy);
bool terminated = false;
while (!terminated) {
zmsg_t *msg = zmsg_recv (pipe);
if (!msg)
break; // Interrupted
char *command = zmsg_popstr (msg);
if (streq (command, "$TERM")) {
terminated = true;
printf("broker received $TERM\n");
}
freen (command);
zmsg_destroy (&msg);
}
zactor_destroy (&proxy);
}
// Finally, here's the main task, which starts the client, worker, and
// broker, and then runs until the client signals it to stop:
int main (void)
{
// Create threads
zactor_t *client = zactor_new (client_task, "Hello, Client");
assert (client);
zactor_t *worker = zactor_new (worker_task, "Hello, Worker");
assert (worker);
zactor_t *broker = zactor_new (broker_task, "Hello, Task");
assert (broker);
char *signal = zstr_recv (client);
printf("signal %s\n", signal);
zstr_free (&signal);
zactor_destroy (&client);
printf("client done\n");
zactor_destroy (&worker);
printf("worker done\n");
zactor_destroy (&broker);
printf("broker done\n");
return 0;
}
Let's diagnose the as-is state, going step by step:
int czmqMAJOR,
czmqMINOR,
czmqPATCH;
zsys_version ( &czmqMAJOR, &czmqMINOR, &czmqPATCH );
printf( "INF: detected CZMQ( %d, %d, %d )-version",
czmqMAJOR,
czmqMINOR,
czmqPATCH
);
assert ( czmqMAJOR == CZMQ_VERSION_MAJOR & "Major: does not match\n" );
assert ( czmqMINOR == CZMQ_VERSION_MINOR & "Minor: does not match\n" );
assert ( czmqPATCH == CZMQ_VERSION_PATCH & "Patch: does not match\n" );
if this matches your expectations, you may hope the DLL-versions are both matching and found in proper locations.
Next:
may test the whole circus run in a non-blocking mode, to prove, there is no other blocker, but as briefly inspected, I have not found such option exposed in CZMQ-API, the native API allows one to flag a NOBLOCK option on { _send() | _recv() }-operations, which prevents them from remaining blocked ( which may be the case for DEALER socket instance in cases on _send()-s, when there are not yet any counterparty with a POSACK-ed .bind()/.connect() state ).
Here I did not find some tools to do this as fast as expected in native API. Maybe you will have more luck on going through this.
Test the presence of a global Context() instance, if it is ready:
add before a first socket instantiation, to be sure we are before any and all socket-generation and their respective _bind()/_connect() operation a following self-reporting row, using:
zsys_info ( "INF: This system's Context() limit is %zu ZeroMQ sockets",
zsys_socket_limit ()
);
One may also enforce the Context() instantiation manually:
so as to be sure the global Context() instance is up and running, before any higher abstracted instances ask if for implementing additional internalities ( sockets, counters, handlers, port-management, etc. )
// Initialize CZMQ zsys layer; this happens automatically when you create
// a socket or an actor; however this call lets you force initialization
// earlier, so e.g. logging is properly set-up before you start working.
// Not threadsafe, so call only from main thread. Safe to call multiple
// times. Returns global CZMQ context.
CZMQ_EXPORT void *
zsys_init (void);
// Optionally shut down the CZMQ zsys layer; this normally happens automatically
// when the process exits; however this call lets you force a shutdown
// earlier, avoiding any potential problems with atexit() ordering, especially
// with Windows dlls.
CZMQ_EXPORT void
zsys_shutdown (void);
and possibly better tune IO-performance, using this right at the initialisation state:
// Configure the number of I/O threads that ZeroMQ will use. A good
// rule of thumb is one thread per gigabit of traffic in or out. The
// default is 1, sufficient for most applications. If the environment
// variable ZSYS_IO_THREADS is defined, that provides the default.
// Note that this method is valid only before any socket is created.
CZMQ_EXPORT void
zsys_set_io_threads (size_t io_threads);
This manual instantiation gives one an additional benefit, from having the instance-handle void pointer, so that one can inspect it's current state and shape by zmq_ctx_get() tools:
void *aGlobalCONTEXT = zsys_init();
printf( "INF: current state of the global Context()-instance has:\n" );
printf( " ( %d )-IO-threads ready\n", zmq_ctx_get( aGlobalCONTEXT,
ZMQ_IO_THREADS
)
);
printf( " ( %d )-ZMQ_BLOCKY state\n", zmq_ctx_get( aGlobalCONTEXT,
ZMQ_BLOCKY
)
); // may generate -1 in case DLL is << 4.2+
...
If unhappy with signal-handling, one may design and use another one:
// Set interrupt handler; this saves the default handlers so that a
// zsys_handler_reset () can restore them. If you call this multiple times
// then the last handler will take affect. If handler_fn is NULL, disables
// default SIGINT/SIGTERM handling in CZMQ.
CZMQ_EXPORT void
zsys_handler_set (zsys_handler_fn *handler_fn);
where
// Callback for interrupt signal handler
typedef void (zsys_handler_fn) (int signal_value);
I'm new to signals, I'm trying to set SIGALRM on UDP echo service, as a socket programming practice.
So here I have a UDP socket, the client sends a string to server and waits for response (any response, here the string is echoed by server).
The goals is to set SIGALRM and let the client resend the string a few times if no responses were made by server or UDP packets get lost.
Here, I used a small sample and simplified long lines with ..., you can get more details on my github repo (line 51)
sigALRM-Client.c
unsigned int tries = 0;
void CatchAlarm()
{
tries += 1;
}
int main(int argc, char **argv)
{
// SKIPPED
// ...
struct sigaction handler;
handler.sa_handler = CatchAlarm;
handler.sa_flags = 0;
if(sigfillset(&handler.sa_mask) < 0)
return 1;
if(sigaction(SIGALRM, &handler, 0) < 0)
return 2;
ssize_t bytes;
bytes = sendto(servSock,...);
while((bytes = recvfrom(servSock,...)) < 0) {
// alarm went off
if(errno == EINTR) {
// try 5 times
if(tries < 5) {
bytes = sendto(servSock,...);
} else {
fprintf(stdout, "no response, waiting...\n");
}
} else {
fprintf(stdout, "failed to get data\n");
return 3;
}
}
// recvfrom() got something, cancel timeout
alarm(0);
fprintf(stdout, "received %d bytes of data\n", bytes);
close(servSock);
}
When I run the client, it won't receive SIGALRM signal and UDP packets get lost in first attempt?!
Client won't retry sending string then exit after 5 attempts, instead, it waits for server response forever!
What prevents client to get SIGALRM?
Did I miss something here?
Your code in the GitHub repo never calls alarm() with a non-zero number. You'll never get an alarm signal delivered automatically unless you actually request one. Relying on some other process to send your process an alarm signal isn't resilient.
Handling & processing data from named pipes.
I am trying to implement a service provider to connect with a hardware device.
request some pointers on my approach to implement a robust system.
Mentioned are the raised requirements
Receive data from other EXE process
To process received Q information and send response information in clients response channel.
Asynchronously send information on some failure to client response channel.
TO implement the mentioned system:
Selected 2 named pipe (ClntcommandRecv & ClntRespSend) .bcz of between process (IPC)
ClntcommandRecv pipe will be used as "Named Pipe Server Using Overlapped" I/O"
ClntRespSend pipe will be used for sending the processed information.
ClntRespSend will also need to send all the async messages from service provider to connected application.
From here my implementation is straight forward.
Using "Named Pipe Server Using Overlapped I/O" by documentation I will be able to address multiple client connection request and its data processing using single thread.
On init system will create a thread to hold connection instance of clients ClntRespSend pipe.
Since, it requires for device to tell its failures to connected clients asynchronously.
Is it advisable for system to have timeout operation on "WaitForMultipleObjects" or
can we have readfile timeout counts after n timeout can we check for health info.WHich is advised
But, stuck in finding the best way to sync my ClntRespSend & ClntcommandRecv (MAPPIN).
Need to get process id of the connected process.Since the system is developed under MINGW - WIN32 - server will not be able to get the process id directly by using (GetNamedPipeClientProcessId).
Need to form a message structure on getting a client connection.
This is the code which i am trying to extend:
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
//#include <strsafe.h>
//#include <glib.h>
#define CONNECTING_STATE 0
#define READING_STATE 1
#define WRITING_STATE 2
#define INSTANCES 4
#define PIPE_TIMEOUT 5000
#define BUFSIZE 4096
typedef struct
{
OVERLAPPED oOverlap;
HANDLE hPipeInst;
TCHAR chRequest[BUFSIZE];
DWORD cbRead;
TCHAR chReply[BUFSIZE];
DWORD cbToWrite;
DWORD dwState;
BOOL fPendingIO;
int processId;
} PIPEINST, *LPPIPEINST;
typedef struct
{
char appName[256];
int processId;
}PIPEHANDSHAKE;
VOID DisconnectAndReconnect(DWORD);
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
VOID GetAnswerToRequest(LPPIPEINST);
PIPEINST Pipe[INSTANCES];
HANDLE hEvents[INSTANCES];
HANDLE responsePipeHandle[INSTANCES];
DWORD WINAPI InstanceThread(LPVOID);
HANDLE hPipeHandles[10];
PULONG s;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
LPTSTR lpszResponsePipe = TEXT("\\\\.\\pipe\\mynamedpipe1");
//GHashTable* hash;
int responsePipeConnectionHandler(VOID)
{
BOOL fConnected = FALSE;
DWORD dwThreadId = 0;
HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
int cbBytesRead;
INT threadCount=0;
//hash = g_hash_table_new(g_str_hash, g_str_equal);
char bufferSize[512];
for (;;)
{
_tprintf( TEXT("\nPipe Server: Main thread awaiting client connection on %s\n"), lpszResponsePipe);
hPipe = CreateNamedPipe(
lpszResponsePipe, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
0, // client time-out
NULL); // default security attribute
if (hPipe == INVALID_HANDLE_VALUE)
{
_tprintf(TEXT("CreateNamedPipe failed, GLE=%d.\n"), GetLastError());
return -1;
}
// Wait for the client to connect; if it succeeds,
// the function returns a nonzero value. If the function
// returns zero, GetLastError returns ERROR_PIPE_CONNECTED.
fConnected = ConnectNamedPipe(hPipe, NULL) ?
TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if(fConnected){
PIPEHANDSHAKE processData;
fConnected = ReadFile(
hPipe, // handle to pipe
bufferSize, // buffer to receive data
sizeof(PIPEHANDSHAKE), // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O
memset(&processData,0,sizeof(PIPEHANDSHAKE));
memcpy(&processData,&bufferSize,sizeof(PIPEHANDSHAKE));
printf("APP Process id: %d , app name: %s",processData.processId,processData.appName);
}
/* if (fConnected)
{
printf("Client connected, creating a processing thread.\n");
// Create a thread for this client.
hThread = CreateThread(
NULL, // no security attribute
0, // default stack size
InstanceThread, // thread proc
(LPVOID) hPipe, // thread parameter
0, // not suspended
&dwThreadId); // returns thread ID
if (hThread == NULL)
{
_tprintf(TEXT("CreateThread failed, GLE=%d.\n"), GetLastError());
return -1;
}
else CloseHandle(hThread);
}
else
// The client could not connect, so close the pipe.
CloseHandle(hPipe);*/
}
return 0;
}
int _tmain(VOID)
{
DWORD i, dwWait, cbRet, dwErr,hThread;
BOOL fSuccess;
int dwThreadId;
// The initial loop creates several instances of a named pipe
// along with an event object for each instance. An
// overlapped ConnectNamedPipe operation is started for
// each instance.
// Create response pipe thread
hThread = CreateThread(
NULL, // no security attribute
0, // default stack size
responsePipeConnectionHandler, // thread proc
NULL, // thread parameter
0, // not suspended
&dwThreadId); // returns thread ID
if (hThread == NULL)
{
printf("Response server creation failed with %d.\n", GetLastError());
return 0;
}
for (i = 0; i < INSTANCES; i++)
{
// Create an event object for this instance.
hEvents[i] = CreateEvent(
NULL, // default security attribute
TRUE, // manual-reset event
TRUE, // initial state = signaled
NULL); // unnamed event object
if (hEvents[i] == NULL)
{
printf("CreateEvent failed with %d.\n", GetLastError());
return 0;
}
Pipe[i].oOverlap.hEvent = hEvents[i];
Pipe[i].hPipeInst = CreateNamedPipe(
lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX | // read/write access
FILE_FLAG_OVERLAPPED, // overlapped mode
PIPE_TYPE_MESSAGE | // message-type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
INSTANCES, // number of instances
BUFSIZE*sizeof(TCHAR), // output buffer size
BUFSIZE*sizeof(TCHAR), // input buffer size
PIPE_TIMEOUT, // client time-out
NULL); // default security attributes
if (Pipe[i].hPipeInst == INVALID_HANDLE_VALUE)
{
printf("CreateNamedPipe failed with %d.\n", GetLastError());
return 0;
}
// Call the subroutine to connect to the new client
Pipe[i].fPendingIO = ConnectToNewClient(
Pipe[i].hPipeInst,
&Pipe[i].oOverlap);
Pipe[i].dwState = Pipe[i].fPendingIO ?
CONNECTING_STATE : // still connecting
READING_STATE; // ready to read
}
while (1)
{
dwWait = WaitForMultipleObjects(
INSTANCES, // number of event objects
hEvents, // array of event objects
FALSE, // does not wait for all
INFINITE); // waits indefinitely
// dwWait shows which pipe completed the operation.
i = dwWait - WAIT_OBJECT_0; // determines which pipe
if (i < 0 || i > (INSTANCES - 1))
{
printf("Index out of range.\n");
return 0;
}
// Get the result if the operation was pending.
if (Pipe[i].fPendingIO)
{
fSuccess = GetOverlappedResult(
Pipe[i].hPipeInst, // handle to pipe
&Pipe[i].oOverlap, // OVERLAPPED structure
&cbRet, // bytes transferred
FALSE); // do not wait
switch (Pipe[i].dwState)
{
// Pending connect operation
case CONNECTING_STATE:
if (! fSuccess)
{
printf("Error %d.\n", GetLastError());
return 0;
}
Pipe[i].dwState = READING_STATE;
break;
// Pending read operation
case READING_STATE:
if (! fSuccess || cbRet == 0)
{
DisconnectAndReconnect(i);
continue;
}
Pipe[i].cbRead = cbRet;
Pipe[i].dwState = WRITING_STATE;
break;
// Pending write operation
case WRITING_STATE:
if (! fSuccess || cbRet != Pipe[i].cbToWrite)
{
DisconnectAndReconnect(i);
continue;
}
Pipe[i].dwState = READING_STATE;
break;
default:
{
printf("Invalid pipe state.\n");
return 0;
}
}
}
// The pipe state determines which operation to do next.
switch (Pipe[i].dwState)
{
case READING_STATE:
fSuccess = ReadFile(
Pipe[i].hPipeInst,
Pipe[i].chRequest,
BUFSIZE*sizeof(TCHAR),
&Pipe[i].cbRead,
&Pipe[i].oOverlap);
if (fSuccess && Pipe[i].cbRead != 0)
{
Pipe[i].fPendingIO = FALSE;
Pipe[i].dwState = WRITING_STATE;
continue;
}
dwErr = GetLastError();
if (! fSuccess && (dwErr == ERROR_IO_PENDING))
{
Pipe[i].fPendingIO = TRUE;
continue;
}
DisconnectAndReconnect(i);
break;
case WRITING_STATE:
GetAnswerToRequest(&Pipe[i]);
fSuccess = WriteFile(
Pipe[i].hPipeInst,
Pipe[i].chReply,
Pipe[i].cbToWrite,
&cbRet,
&Pipe[i].oOverlap);
if (fSuccess && cbRet == Pipe[i].cbToWrite)
{
Pipe[i].fPendingIO = FALSE;
Pipe[i].dwState = READING_STATE;
continue;
}
dwErr = GetLastError();
if (! fSuccess && (dwErr == ERROR_IO_PENDING))
{
Pipe[i].fPendingIO = TRUE;
continue;
}
DisconnectAndReconnect(i);
break;
default:
{
printf("Invalid pipe state.\n");
return 0;
}
}
}
return 0;
}
VOID DisconnectAndReconnect(DWORD i)
{
if (! DisconnectNamedPipe(Pipe[i].hPipeInst) )
{
printf("DisconnectNamedPipe failed with %d.\n", GetLastError());
}
Pipe[i].fPendingIO = ConnectToNewClient(
Pipe[i].hPipeInst,
&Pipe[i].oOverlap);
Pipe[i].dwState = Pipe[i].fPendingIO ?
CONNECTING_STATE : // still connecting
READING_STATE; // ready to read
}
BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo)
{
BOOL fConnected, fPendingIO = FALSE;
fConnected = ConnectNamedPipe(hPipe, lpo);
if (fConnected)
{
printf("ConnectNamedPipe failed with %d.\n", GetLastError());
return 0;
}
switch (GetLastError())
{
// The overlapped connection in progress.
case ERROR_IO_PENDING:
fPendingIO = TRUE;
break;
case ERROR_PIPE_CONNECTED:
if (SetEvent(lpo->hEvent))
break;
default:
{
printf("ConnectNamedPipe failed with %d.\n", GetLastError());
return 0;
}
}
return fPendingIO;
}
int rxProccesIdMsg(HANDLE pipe)
{
PIPEHANDSHAKE pipeInfo;
CHAR bufferSize[512] = {'\0'};
INT cbBytesRead;
BOOL fSuccess;
PIPEHANDSHAKE processData;
fSuccess = ReadFile(
pipe, // handle to pipe
bufferSize, // buffer to receive data
sizeof(PIPEHANDSHAKE), // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O
memset(&processData,0,sizeof(PIPEHANDSHAKE));
memcpy(&processData,&bufferSize,sizeof(PIPEHANDSHAKE));
if ( (!fSuccess))
{
printf("Client: READ Server Pipe Failed(%d)\n",GetLastError());
CloseHandle(pipe);
return -1;
}
else
{
printf("Client: READ Server Pipe Success(%d)\n",GetLastError());
printf("APP Process id: %d , app name: %s",processData.processId,processData.appName);
//Sleep(3*100);
}
return processData.processId;
}
VOID GetAnswerToRequest(LPPIPEINST pipe)
{
_tprintf( TEXT("[%d] %s\n"), pipe->hPipeInst, pipe->chRequest);
// StringCchCopy( pipe->chReply, BUFSIZE, TEXT("Default answer from server") );
strncpy(pipe->chReply, "Default answer from server",BUFSIZE);
pipe->cbToWrite = (lstrlen(pipe->chReply)+1)*sizeof(TCHAR);
}
void main()
{
HANDLE h1,h2,h3;
uint8 data;
double Task2ms_Raster, Task10ms_Raster, Task100ms_Raster ;
CreateSocket();
XCP_FN_TYPE Xcp_Initialize( );
while(1)
{
data = recv(fd, recv_data, 99, 0);
if (data == SOCKET_ERROR) {
printf("recv failed with error %d\n", WSAGetLastError());
}
else
{
pChunkData = &recv_data;
chunkLen = sizeof(pChunkData);
XcpIp_RxCallback ((uint16) chunkLen, (char*) pChunkData, (uint16) port);
}
}
h1=TimerTask(2,TASK1,&Task2ms_Raster);
h2=TimerTask(10,TASK2,&Task10ms_Raster);
h3=TimerTask(100,TASK3,&Task100ms_Raster);
XCP_FN_TYPE XcpIp_OnTcpCxnClosed( port );
}
I have created the server socket and recieving data from the client via the ip address and the port number. I have to run the timer task in parallel with the recieve data from the socket.The function definition of create scoket and TimerTask functionality is not shown. So could anyone please help me how to run the timer task parallel to it ??