patterncMinor
Handling serial port with select and local socket
Viewed 0 times
handlinglocalwithserialportandselectsocket
Problem
I have another issue: I'm trying to handle connections made by local socket along with data from serial port. Here is my concept:
It appears that it is working so far.
My questions are:
main.c
```
struct client_struct {
int socket;
char cmd_type;
};
void main {
int loop_num=250;
int local_socket;
int serial_fd;
int max_fd;
int tmp_socket;
fd_set input;
fd_set tmp_input;
char *serial_output_buffer;
struct timeval timeout;
struct client_struct *clients;
int clients_num=0;
serial_output_buffer=malloc(INPUT_BYTES_NUM * sizeof(char));
serial_fd=open_port();
local_socket=open_local_socket();
FD_ZERO(&input);
FD_SET(serial_fd, &input);
max_fd = serial_fd+1;
while(TRUE) {
loop_num++;
if(loop_num>250) {
pool_from_serial(serial_fd);
loop_num=0;
}
tmp_socket=accept_connection();
if(tmp_socket) {
clients=alloc_client(clients,clients_num);
clients[clients_num].socket=tmp_socket;
clients_num++;
FD_SET(tmp_socket, &input);
max_fd
- Serial port is opened and monitored by select
- Local socket is opened and put in Listen state
- inside loop I call my function accept_connection periodically
- When new connection arrives I allocate new array of struct to handle connections data
- I'm adding socket to &input with FD_SET(socket,&input), and this is what select is monitoring
- I'm checking with FD_ISSET (after select returns) if there is data from some client
- If there is data I'm reading it with process_client
It appears that it is working so far.
My questions are:
- I'm doing this the first time. Are there some obvious mistakes?
- How do I know that client has disconnected, and socket should be cleared, and removed from clients array?
- Should I move some variables like *clients pointer
clients_numoutside themainfunction to have easier access to them from other functions, otherwise next functions can have a really long parameter list?
main.c
```
struct client_struct {
int socket;
char cmd_type;
};
void main {
int loop_num=250;
int local_socket;
int serial_fd;
int max_fd;
int tmp_socket;
fd_set input;
fd_set tmp_input;
char *serial_output_buffer;
struct timeval timeout;
struct client_struct *clients;
int clients_num=0;
serial_output_buffer=malloc(INPUT_BYTES_NUM * sizeof(char));
serial_fd=open_port();
local_socket=open_local_socket();
FD_ZERO(&input);
FD_SET(serial_fd, &input);
max_fd = serial_fd+1;
while(TRUE) {
loop_num++;
if(loop_num>250) {
pool_from_serial(serial_fd);
loop_num=0;
}
tmp_socket=accept_connection();
if(tmp_socket) {
clients=alloc_client(clients,clients_num);
clients[clients_num].socket=tmp_socket;
clients_num++;
FD_SET(tmp_socket, &input);
max_fd
Solution
On your questions:
-
There are some oddities in your code, for example, the use of non-blocking
sockets.
-
Detecting disconnected clients is difficult. I'm not that familiar with
UNIX-domain sockets, but I imagine they are much the same as Internet-domain
sockets. You probably need either to set up periodic keep-alive messages
within whatever protocol you have adopted for the client-server communication
(ie. empty messages whose purpose is just to tell the server that the client
is still there), or add a timeout so that a client that hasn't talked to the
server for some time is assumed to be dead, or both.
-
Global variables are best avoided. Use them only as a last resort,
when the alternatives are really bad. Knowing when you have reached that
point is difficult, so I suggest just not using globals unless there is no
alternative.
Naming:
-
that.
-
would be enough.
-
giving your variables the suffix 'unix', as in
makes it more difficult to change the code (eg if you wanted to switch to
Internet-domain sockets) than it need be.
Formatting:
You need to add spaces around operators ('=', '==', '
-
including the
like:
-
the
-
You use
but not for the listening socket which you instead set non-blocking and poll
each time round the loop. Your
which might allow the loop to reach the
occasionally. That arrangement make no sense.
You should add the listening socket to the input file descriptor set and
handle it when
timeout on
be serviced (eg detecting dead clients). If you use a timeout, initialize
it appropriately.
-
You call
arbitrary (could be 250 serial chars received or 250 clients connecting or a
mixture). I don't know the purpose of this, but it might need to be more
robustly scheduled.
-
There are some oddities in your code, for example, the use of non-blocking
sockets.
-
Detecting disconnected clients is difficult. I'm not that familiar with
UNIX-domain sockets, but I imagine they are much the same as Internet-domain
sockets. You probably need either to set up periodic keep-alive messages
within whatever protocol you have adopted for the client-server communication
(ie. empty messages whose purpose is just to tell the server that the client
is still there), or add a timeout so that a client that hasn't talked to the
server for some time is assumed to be dead, or both.
-
Global variables are best avoided. Use them only as a last resort,
when the alternatives are really bad. Knowing when you have reached that
point is difficult, so I suggest just not using globals unless there is no
alternative.
Naming:
-
struct client_struct tells me no more than struct client, so call itthat.
-
getMaxFd uses camel case, unlike all other functions. Simply max_fdwould be enough.
-
giving your variables the suffix 'unix', as in
adr_unix adds nothing andmakes it more difficult to change the code (eg if you wanted to switch to
Internet-domain sockets) than it need be.
Formatting:
You need to add spaces around operators ('=', '==', '
-
main is too long - extract the contents of the loop into a function (notincluding the
loop_num conditional). So main will have something simple,like:
while (process_connections(sck, serial_fd, clients, nclients)) {
if (++loop_num>250) { ...
}
}-
the
usleep call should be unnecessary and should be removed.-
You use
select to detect activity on accepted sockets or the serial line,but not for the listening socket which you instead set non-blocking and poll
each time round the loop. Your
select call has an uninitialized timeout,which might allow the loop to reach the
accept_connection calloccasionally. That arrangement make no sense.
You should add the listening socket to the input file descriptor set and
handle it when
select returns. And there is probably no need for atimeout on
select unless there is some periodic activity that needs tobe serviced (eg detecting dead clients). If you use a timeout, initialize
it appropriately.
-
You call
pool_from_serial every 250 cycles of the loop. That seems ratherarbitrary (could be 250 serial chars received or 250 clients connecting or a
mixture). I don't know the purpose of this, but it might need to be more
robustly scheduled.
Code Snippets
res=(res > clients[i].socket ? res : clients[i].socket);if (res < clients[i].socket) {
res = clients[i].socket;
}for(int i = 0; i < client_num; i++) {
if (res < clients[i].socket) {
res = clients[i].socket;
}struct client* alloc_client(struct client *clients, size_t num)
{
struct client *c = realloc(clients, (num+1) * sizeof *c);
if (!c) {
error_exit("alloc_client malloc failed");
}
return c;
}int sck_unix; /* Listen Socket */
struct sockaddr_un adr_unix;/* AF_UNIX */
int len_unix; /* length */Context
StackExchange Code Review Q#40039, answer score: 4
Revisions (0)
No revisions yet.