handling new clients in a separate thread
This commit is contained in:
parent
86f0bc612f
commit
604665ee05
4 changed files with 99 additions and 95 deletions
11
client.c
11
client.c
|
@ -86,32 +86,31 @@ int client_write(struct client_t *client, char *databuf, int datalen) {
|
||||||
/* Waits for client input in "line mode". Blocks until input arrives. */
|
/* Waits for client input in "line mode". Blocks until input arrives. */
|
||||||
int client_wait_line(struct client_t *client) {
|
int client_wait_line(struct client_t *client) {
|
||||||
|
|
||||||
int ret;
|
|
||||||
fd_set read_fds;
|
fd_set read_fds;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
client->data[0] = '\0';
|
client->data[0] = '\0';
|
||||||
/* loop waiting for client input */
|
/* loop waiting for client input */
|
||||||
while (client->data[0] == '\0') {
|
while (client->data[0] == '\0') {
|
||||||
/* setup select() parameters */
|
/* setup select() parameters */
|
||||||
|
tv.tv_sec = 15; /* 15 second timeout */
|
||||||
|
tv.tv_usec = 0;
|
||||||
FD_ZERO(&read_fds);
|
FD_ZERO(&read_fds);
|
||||||
FD_SET(client->socket, &read_fds);
|
FD_SET(client->socket, &read_fds);
|
||||||
/* send prompt character to client */
|
/* send prompt character to client */
|
||||||
client_write(client, "> ", 2);
|
client_write(client, "> ", 2);
|
||||||
/* block until input arrives */
|
/* block until input arrives */
|
||||||
if (select((client->socket)+1, &read_fds, NULL, NULL, NULL) == -1) return -1;
|
if (select((client->socket)+1, &read_fds, NULL, NULL, &tv) <= 0) return -1;
|
||||||
if (FD_ISSET(client->socket, &read_fds)) {
|
if (FD_ISSET(client->socket, &read_fds)) {
|
||||||
/* read client input */
|
/* read client input */
|
||||||
ret = client_read(client);
|
if (client_read(client) == -1) return -1;
|
||||||
if (ret == -1) return -1;
|
|
||||||
/* we don't want empty data so stop on \r or \n */
|
/* we don't want empty data so stop on \r or \n */
|
||||||
if ( (client->data[0] == '\r') || (client->data[0] == '\n') ) {
|
if ( (client->data[0] == '\r') || (client->data[0] == '\n') ) {
|
||||||
fprintf(stderr, "client data is empty\n");
|
|
||||||
client->data[0] = '\0';
|
client->data[0] = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "client data: %s\n", client->data);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
65
moxerver.c
65
moxerver.c
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
#define NAME "moxerver"
|
#define NAME "moxerver"
|
||||||
|
|
||||||
#define SERVER_WAIT_TIMEOUT 5 /* seconds for select() timeout in server loop */
|
#define SERVER_WAIT_TIMEOUT 2 /* seconds for select() timeout in server loop */
|
||||||
|
|
||||||
/* Prints help message. */
|
/* Prints help message. */
|
||||||
static void usage() {
|
static void usage() {
|
||||||
|
@ -82,15 +82,14 @@ int main(int argc, char *argv[]) {
|
||||||
int fdmax;
|
int fdmax;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
|
|
||||||
pthread_t tty_thread;
|
pthread_t tty_thread, new_client_thread;
|
||||||
|
|
||||||
/* zero init tty_dev */
|
/* zero init tty_dev */
|
||||||
if (cfsetispeed(&(tty_dev.ttyset), B0) < 0 ||
|
if (cfsetispeed(&(tty_dev.ttyset), B0) < 0 ||
|
||||||
cfsetospeed(&(tty_dev.ttyset), B0) < 0) {
|
cfsetospeed(&(tty_dev.ttyset), B0) < 0) {
|
||||||
fprintf(stderr, "[%s] error configuring tty device speed\n", NAME);
|
fprintf(stderr, "[%s] error configuring tty device speed\n", NAME);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* enable catching and handling some quit signals, SIGKILL can't be caught */
|
/* enable catching and handling some quit signals, SIGKILL can't be caught */
|
||||||
signal(SIGTERM, quit_handler);
|
signal(SIGTERM, quit_handler);
|
||||||
|
@ -141,6 +140,7 @@ int main(int argc, char *argv[]) {
|
||||||
/* initialize */
|
/* initialize */
|
||||||
if (server_setup(&server, tcp_port) < 0) return -1;
|
if (server_setup(&server, tcp_port) < 0) return -1;
|
||||||
client.socket = -1;
|
client.socket = -1;
|
||||||
|
new_client.socket = -1;
|
||||||
tty_dev.fd = -1;
|
tty_dev.fd = -1;
|
||||||
|
|
||||||
/* parse config file if any */
|
/* parse config file if any */
|
||||||
|
@ -150,7 +150,7 @@ int main(int argc, char *argv[]) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
else if (!def_conf && ret) {
|
else if (!def_conf && ret) {
|
||||||
fprintf(stderr, "[%s] error parsing congig file %s on line %d\n", NAME, CONFILE, ret);
|
fprintf(stderr, "[%s] error parsing config file %s on line %d\n", NAME, CONFILE, ret);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,9 +177,25 @@ int main(int argc, char *argv[]) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* loop with timeouts waiting for client connection and data */
|
/* loop with timeouts waiting for client connection requests and data */
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
|
/* check if new client is availabe for connection */
|
||||||
|
if ( (client.socket == -1) && (new_client.socket != -1) ) {
|
||||||
|
/* copy new client information */
|
||||||
|
memcpy(&client, &new_client, sizeof(struct client_t));
|
||||||
|
new_client.socket = -1;
|
||||||
|
fprintf(stderr, "[%s] client %s connected\n", NAME, client.ip_string);
|
||||||
|
/* ask client to provide a username before going to "character" mode */
|
||||||
|
if (client_ask_username(&client) != 0) {
|
||||||
|
/* close client if not able to provide a username */
|
||||||
|
client_close(&client);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* put client in "character" mode */
|
||||||
|
telnet_set_character_mode(&client);
|
||||||
|
}
|
||||||
|
|
||||||
/* setup parameters for select() */
|
/* setup parameters for select() */
|
||||||
tv.tv_sec = SERVER_WAIT_TIMEOUT;
|
tv.tv_sec = SERVER_WAIT_TIMEOUT;
|
||||||
tv.tv_usec = 0;
|
tv.tv_usec = 0;
|
||||||
|
@ -188,7 +204,7 @@ int main(int argc, char *argv[]) {
|
||||||
if (client.socket != -1) {
|
if (client.socket != -1) {
|
||||||
FD_SET(client.socket, &read_fds); /* wait for client if connected */
|
FD_SET(client.socket, &read_fds); /* wait for client if connected */
|
||||||
}
|
}
|
||||||
fdmax = (server.socket > client.socket) ? server.socket : client.socket;
|
fdmax = (server.socket > client.socket) ? server.socket : client.socket;
|
||||||
|
|
||||||
/* wait with select() */
|
/* wait with select() */
|
||||||
ret = select(fdmax+1, &read_fds, NULL, NULL, &tv);
|
ret = select(fdmax+1, &read_fds, NULL, NULL, &tv);
|
||||||
|
@ -201,36 +217,11 @@ int main(int argc, char *argv[]) {
|
||||||
/* check server status */
|
/* check server status */
|
||||||
if (FD_ISSET(server.socket, &read_fds)) {
|
if (FD_ISSET(server.socket, &read_fds)) {
|
||||||
fprintf(stderr, "[%s] received client connection request\n", NAME);
|
fprintf(stderr, "[%s] received client connection request\n", NAME);
|
||||||
/* accept connection request if no client is connected */
|
/* handle new client connection request in a separate thread */
|
||||||
if (client.socket == -1) {
|
if (pthread_create(&new_client_thread, NULL, server_new_client_thread, NULL) != 0) {
|
||||||
ret = server_accept(&server, &client);
|
/* print error but continue waiting for connection request */
|
||||||
if ( ret != 0) {
|
fprintf(stderr, "[%s] problem with handling client connection request\n", NAME);
|
||||||
/* print error but continue waiting for connection request */
|
continue;
|
||||||
//TODO maybe we should break here to avoid endless loop, what are possible causes of this failure?
|
|
||||||
fprintf(stderr, "[%s] problem accepting client\n", NAME);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* ask client to provide a username before going to "character" mode */
|
|
||||||
client_ask_username(&client);
|
|
||||||
/* put client in "character" mode */
|
|
||||||
telnet_set_character_mode(&client);
|
|
||||||
}
|
|
||||||
/* if a client is already connected then new clients need to be handled */
|
|
||||||
else {
|
|
||||||
/* decide if current client needs to be dropped or new client needs to be rejected */
|
|
||||||
ret = server_drop(&server, &client);
|
|
||||||
if (ret < 0) {
|
|
||||||
/* error in handling new clients, print but continue waiting for new clients */
|
|
||||||
fprintf(stderr, "[%s] problem handling new client request\n", NAME);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* if current client was dropped we need to set up new client */
|
|
||||||
if (ret > 0) {
|
|
||||||
/* ask client to provide a username before going to "character" mode */
|
|
||||||
client_ask_username(&client);
|
|
||||||
/* put client in "character" mode */
|
|
||||||
telnet_set_character_mode(&client);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* check client status if connected */
|
/* check client status if connected */
|
||||||
|
|
45
moxerver.h
45
moxerver.h
|
@ -21,34 +21,35 @@
|
||||||
|
|
||||||
/* Structures used for communication parameters. */
|
/* Structures used for communication parameters. */
|
||||||
struct server_t {
|
struct server_t {
|
||||||
int socket; /* server socket */
|
int socket; /* server socket */
|
||||||
struct sockaddr_in address; /* server address information */
|
struct sockaddr_in address; /* server address information */
|
||||||
unsigned int port; /* server port in host byte order, practical reference */
|
unsigned int port; /* server port in host byte order, practical reference */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct client_t {
|
struct client_t {
|
||||||
int socket; /* client socket */
|
int socket; /* client socket */
|
||||||
struct sockaddr_in address; /* client address information */
|
struct sockaddr_in address; /* client address information */
|
||||||
char ip_string[INET_ADDRSTRLEN]; /* client IP address as a string */
|
char ip_string[INET_ADDRSTRLEN]; /* client IP address as a string */
|
||||||
time_t last_active; /* time of client's last activity in seconds from Epoch */
|
time_t last_active; /* time of client's last activity in seconds from Epoch */
|
||||||
char username[USERNAME_LEN]; /* username for human identification */
|
char username[USERNAME_LEN]; /* username for human identification */
|
||||||
char data[DATABUF_LEN]; /* buffer for data received from client */
|
char data[DATABUF_LEN]; /* buffer for data received from client */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tty_t {
|
struct tty_t {
|
||||||
int fd; /* tty file descriptor */
|
int fd; /* tty file descriptor */
|
||||||
struct termios ttysetdef; /* default tty termios settings */
|
struct termios ttysetdef; /* default tty termios settings */
|
||||||
struct termios ttyset; /* tty termios settings */
|
struct termios ttyset; /* tty termios settings */
|
||||||
char path[DEV_PATH]; /* tty device path */
|
char path[DEV_PATH]; /* tty device path */
|
||||||
char data[DATABUF_LEN]; /* buffer for data received from tty */
|
char data[DATABUF_LEN]; /* buffer for data received from tty */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Global variables used throughout the application. */
|
/* Global variables used throughout the application. */
|
||||||
int debug_messages; /* if > 0 debug messages will be printed */
|
int debug_messages; /* if > 0 debug messages will be printed */
|
||||||
struct server_t server; /* main server structure */
|
struct server_t server; /* main server structure */
|
||||||
struct client_t client; /* connected client structure */ //TODO working with only 1 client, this can be expanded into a list
|
struct client_t client; /* connected client structure */ //TODO working with only 1 client, this can be expanded into a list
|
||||||
struct tty_t tty_dev; /* connected tty device */
|
struct client_t new_client; /* client structure for new client request */
|
||||||
|
struct tty_t tty_dev; /* connected tty device */
|
||||||
|
|
||||||
|
|
||||||
/* Global functions used throughout the application. */
|
/* Global functions used throughout the application. */
|
||||||
|
@ -91,15 +92,17 @@ int server_close(struct server_t *server);
|
||||||
int server_accept(struct server_t *server, struct client_t *accepted_client);
|
int server_accept(struct server_t *server, struct client_t *accepted_client);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks new incoming client connection if current client needs to be dropped.
|
* Thread function handling new client connections.
|
||||||
* If current client doesn't need to be dropped then new client is rejected.
|
* If there is no connected client then first client request is accepted.
|
||||||
|
* If there is a connected client then new client is asked if connected client should be dropped.
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* - positive value if current client was dropped
|
* Return value from this thread function is not used.
|
||||||
* - 0 if new client was rejected
|
* Function handles global client variables:
|
||||||
* - negative value if error occurred
|
* - client structure is reset if new client is available to connect to
|
||||||
|
* - new_client structure stores information about the client to connect to
|
||||||
*/
|
*/
|
||||||
int server_drop(struct server_t *server, struct client_t *client);
|
void* server_new_client_thread(void *args);
|
||||||
|
|
||||||
|
|
||||||
/* Functions handling communication with clients. */
|
/* Functions handling communication with clients. */
|
||||||
|
|
73
server.c
73
server.c
|
@ -47,8 +47,8 @@ int server_setup(struct server_t *server, unsigned int port) {
|
||||||
server->port = port;
|
server->port = port;
|
||||||
fprintf(stderr,"[%s] assigned port %u\n", __func__, server->port); //ntohs(server->address.sin_port)
|
fprintf(stderr,"[%s] assigned port %u\n", __func__, server->port); //ntohs(server->address.sin_port)
|
||||||
|
|
||||||
/* listen for a client connection, allow 2 connections in queue */
|
/* listen for a client connection, allow some connections in queue */
|
||||||
if (listen(server->socket, 2) == -1) {
|
if (listen(server->socket, 1) == -1) {
|
||||||
fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno));
|
fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno));
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
@ -98,58 +98,69 @@ int server_accept(struct server_t *server, struct client_t *accepted_client) {
|
||||||
|
|
||||||
/* print client information */
|
/* print client information */
|
||||||
time2string(accepted_client->last_active, timestamp);
|
time2string(accepted_client->last_active, timestamp);
|
||||||
fprintf(stderr, "[%s] accepted client %s on port %u @ %s\n", __func__,
|
fprintf(stderr, "[%s] accepted client %s @ %s\n",
|
||||||
accepted_client->ip_string, server->port, timestamp);
|
__func__, accepted_client->ip_string, timestamp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Drops current client or rejects new client. */
|
/* Thread function handling new client connections. Handles global client variables. */
|
||||||
int server_drop(struct server_t *server, struct client_t *current_client) {
|
void* server_new_client_thread(void *args) {
|
||||||
|
|
||||||
struct client_t new_client;
|
|
||||||
char msg[DATABUF_LEN];
|
char msg[DATABUF_LEN];
|
||||||
char timestamp[TIMESTAMP_LEN];
|
char timestamp[TIMESTAMP_LEN];
|
||||||
|
struct client_t temp_client;
|
||||||
|
|
||||||
/* accept new connection request */
|
/* accept new connection request */
|
||||||
if (server_accept(server, &new_client) != 0) return -1;
|
if (server_accept(&server, &temp_client) != 0) return (void *) -1;
|
||||||
|
|
||||||
|
/* if no client is connected then make new client available for connection */
|
||||||
|
if (client.socket == -1) {
|
||||||
|
memcpy(&new_client, &temp_client, sizeof(struct client_t));
|
||||||
|
return (void *) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if there is already a new client being handled then reject this client request */
|
||||||
|
if (new_client.socket != -1) {
|
||||||
|
sprintf(msg, "\nToo many connection requests, please try later.\n");
|
||||||
|
send(temp_client.socket, msg, strlen(msg), 0);
|
||||||
|
client_close(&temp_client);
|
||||||
|
time2string(time(NULL), timestamp);
|
||||||
|
fprintf(stderr, "[%s] rejected new client request %s @ %s\n",
|
||||||
|
__func__, temp_client.ip_string, timestamp);
|
||||||
|
return (void *) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make new client available for connection */
|
||||||
|
memcpy(&new_client, &temp_client, sizeof(struct client_t));
|
||||||
|
|
||||||
/* inform new client that port is already in use */
|
/* inform new client that port is already in use */
|
||||||
time2string(current_client->last_active, timestamp);
|
time2string(client.last_active, timestamp);
|
||||||
sprintf(msg, "\nPort %u is already being used!\nCurrent user and last activity:\n%s @ %s\n",
|
sprintf(msg, "\nPort %u is already being used!\nCurrent user and last activity:\n%s @ %s\n",
|
||||||
server->port, current_client->username, timestamp);
|
server.port, client.username, timestamp);
|
||||||
send(new_client.socket, msg, strlen(msg), 0);
|
send(new_client.socket, msg, strlen(msg), 0);
|
||||||
|
|
||||||
/* ask new client if current client should be dropped */
|
/* ask new client if current client should be dropped */
|
||||||
sprintf(msg, "\nDo you want to drop the current user?\nIf yes then please type YES DROP (in uppercase):");
|
sprintf(msg, "\nDo you want to drop the current user?\nIf yes then please type YES DROP (in uppercase):\n");
|
||||||
send(new_client.socket, msg, strlen(msg), 0);
|
send(new_client.socket, msg, strlen(msg), 0);
|
||||||
|
|
||||||
/* wait for client input */
|
/* wait for client input */
|
||||||
if (client_wait_line(&new_client) != 0) return -1;
|
client_wait_line(&new_client);
|
||||||
|
|
||||||
/* check client confirmation */
|
/* check client confirmation */
|
||||||
if (strncmp(new_client.data, "YES DROP", 8) == 0) {
|
if (strncmp(new_client.data, "YES DROP", 8) == 0) {
|
||||||
/* current client should be dropped, drop it */
|
/* drop connected client */
|
||||||
client_close(current_client);
|
client_close(&client);
|
||||||
time2string(time(NULL), timestamp);
|
fprintf(stderr, "[%s] dropped client %s @ %s\n",
|
||||||
fprintf(stderr, "[%s] dropped current client %s on port %u @ %s\n", __func__,
|
__func__, client.ip_string, timestamp);
|
||||||
current_client->ip_string, server->port, timestamp);
|
|
||||||
/* make new client the current client */
|
|
||||||
memcpy(current_client, &new_client, sizeof(struct client_t));
|
|
||||||
time2string(time(NULL), timestamp);
|
|
||||||
fprintf(stderr, "[%s] accepted new client request %s on port %u @ %s\n", __func__,
|
|
||||||
current_client->ip_string, server->port, timestamp);
|
|
||||||
/* return 1 because current client was dropped */
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* new connection should be rejected, close connection */
|
/* reject this client request */
|
||||||
close(new_client.socket);
|
client_close(&new_client);
|
||||||
time2string(time(NULL), timestamp);
|
time2string(time(NULL), timestamp);
|
||||||
fprintf(stderr, "[%s] rejected new client request %s on port %u @ %s\n", __func__,
|
fprintf(stderr, "[%s] rejected new client request %s @ %s\n",
|
||||||
new_client.ip_string, server->port, timestamp);
|
__func__, temp_client.ip_string, timestamp);
|
||||||
/* return 0 because new client was rejected */
|
return (void *) 1;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (void *) 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue