Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Example of client/server with select().
// Simple example of client.
// Client prints received messages to stdout and sends from stdin.
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include "message.h"
peer_t server;
void shutdown_properly(int code);
void handle_signal_action(int sig_number)
{
if (sig_number == SIGINT) {
printf("SIGINT was catched!\n");
shutdown_properly(EXIT_SUCCESS);
}
else if (sig_number == SIGPIPE) {
printf("SIGPIPE was catched!\n");
shutdown_properly(EXIT_SUCCESS);
}
}
int setup_signals()
{
struct sigaction sa;
sa.sa_handler = handle_signal_action;
if (sigaction(SIGINT, &sa, 0) != 0) {
perror("sigaction()");
return -1;
}
if (sigaction(SIGPIPE, &sa, 0) != 0) {
perror("sigaction()");
return -1;
}
return 0;
}
int get_client_name(int argc, char **argv, char *client_name)
{
if (argc > 1)
strcpy(client_name, argv[1]);
else
strcpy(client_name, "no name");
return 0;
}
int connect_server(peer_t *server)
{
// create socket
server->socket = socket(AF_INET, SOCK_STREAM, 0);
if (server->socket < 0) {
perror("socket()");
return -1;
}
// set up addres
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(SERVER_IPV4_ADDR);
server_addr.sin_port = htons(SERVER_LISTEN_PORT);
server->addres = server_addr;
if (connect(server->socket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) != 0) {
perror("connect()");
return -1;
}
printf("Connected to %s:%d.\n", SERVER_IPV4_ADDR, SERVER_LISTEN_PORT);
return 0;
}
int build_fd_sets(peer_t *server, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds)
{
FD_ZERO(read_fds);
FD_SET(STDIN_FILENO, read_fds);
FD_SET(server->socket, read_fds);
FD_ZERO(write_fds);
// there is smth to send, set up write_fd for server socket
if (server->send_buffer.current > 0)
FD_SET(server->socket, write_fds);
FD_ZERO(except_fds);
FD_SET(STDIN_FILENO, except_fds);
FD_SET(server->socket, except_fds);
return 0;
}
int handle_read_from_stdin(peer_t *server, char *client_name)
{
char read_buffer[DATA_MAXSIZE]; // buffer for stdin
if (read_from_stdin(read_buffer, DATA_MAXSIZE) != 0)
return -1;
// Create new message and enqueue it.
message_t new_message;
prepare_message(client_name, read_buffer, &new_message);
print_message(&new_message);
if (peer_add_to_send(server, &new_message) != 0) {
printf("Send buffer is overflowed, we lost this message!\n");
return 0;
}
printf("New message to send was enqueued right now.\n");
return 0;
}
/* You should be careful when using this function in multythread program.
* Ensure that server is thread-safe. */
void shutdown_properly(int code)
{
delete_peer(&server);
printf("Shutdown client properly.\n");
exit(code);
}
int handle_received_message(message_t *message)
{
printf("Received message from server.\n");
print_message(message);
return 0;
}
int main(int argc, char **argv)
{
if (setup_signals() != 0)
exit(EXIT_FAILURE);
char client_name[256];
get_client_name(argc, argv, client_name);
printf("Client '%s' start.\n", client_name);
create_peer(&server);
if (connect_server(&server) != 0)
shutdown_properly(EXIT_FAILURE);
/* Set nonblock for stdin. */
int flag = fcntl(STDIN_FILENO, F_GETFL, 0);
flag |= O_NONBLOCK;
fcntl(STDIN_FILENO, F_SETFL, flag);
fd_set read_fds;
fd_set write_fds;
fd_set except_fds;
printf("Waiting for server message or stdin input. Please, type text to send:\n");
// server socket always will be greater then STDIN_FILENO
int maxfd = server.socket;
while (1) {
// Select() updates fd_set's, so we need to build fd_set's before each select()call.
build_fd_sets(&server, &read_fds, &write_fds, &except_fds);
int activity = select(maxfd + 1, &read_fds, &write_fds, &except_fds, NULL);
switch (activity) {
case -1:
perror("select()");
shutdown_properly(EXIT_FAILURE);
case 0:
// you should never get here
printf("select() returns 0.\n");
shutdown_properly(EXIT_FAILURE);
default:
/* All fd_set's should be checked. */
if (FD_ISSET(STDIN_FILENO, &read_fds)) {
if (handle_read_from_stdin(&server, client_name) != 0)
shutdown_properly(EXIT_FAILURE);
}
if (FD_ISSET(STDIN_FILENO, &except_fds)) {
printf("except_fds for stdin.\n");
shutdown_properly(EXIT_FAILURE);
}
if (FD_ISSET(server.socket, &read_fds)) {
if (receive_from_peer(&server, &handle_received_message) != 0)
shutdown_properly(EXIT_FAILURE);
}
if (FD_ISSET(server.socket, &write_fds)) {
if (send_to_peer(&server) != 0)
shutdown_properly(EXIT_FAILURE);
}
if (FD_ISSET(server.socket, &except_fds)) {
printf("except_fds for server.\n");
shutdown_properly(EXIT_FAILURE);
}
}
printf("And we are still waiting for server or stdin activity. You can type something to send:\n");
}
return 0;
}
#ifndef MESSAGE_H
#define MESSAGE_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Maximum bytes that can be send() or recv() via net by one call.
* It's a good idea to test sending one byte by one.
*/
#define MAX_SEND_SIZE 100
/* Size of send queue (messages). */
#define MAX_MESSAGES_BUFFER_SIZE 10
#define SENDER_MAXSIZE 128
#define DATA_MAXSIZE 512
#define SERVER_IPV4_ADDR "127.0.0.1"
#define SERVER_LISTEN_PORT 33235
// message --------------------------------------------------------------------
typedef struct {
char sender[SENDER_MAXSIZE];
char data[DATA_MAXSIZE];
} message_t;
int prepare_message(char *sender, char *data, message_t *message)
{
sprintf(message->sender, "%s", sender);
sprintf(message->data, "%s", data);
return 0;
}
int print_message(message_t *message)
{
printf("Message: \"%s: %s\"\n", message->sender, message->data);
return 0;
}
// message queue --------------------------------------------------------------
typedef struct {
int size;
message_t *data;
int current;
} message_queue_t;
int create_message_queue(int queue_size, message_queue_t *queue)
{
queue->data = calloc(queue_size, sizeof(message_t));
queue->size = queue_size;
queue->current = 0;
return 0;
}
void delete_message_queue(message_queue_t *queue)
{
free(queue->data);
queue->data = NULL;
}
int enqueue(message_queue_t *queue, message_t *message)
{
if (queue->current == queue->size)
return -1;
memcpy(&queue->data[queue->current], message, sizeof(message_t));
queue->current++;
return 0;
}
int dequeue(message_queue_t *queue, message_t *message)
{
if (queue->current == 0)
return -1;
memcpy(message, &queue->data[queue->current - 1], sizeof(message_t));
queue->current--;
return 0;
}
int dequeue_all(message_queue_t *queue)
{
queue->current = 0;
return 0;
}
// peer -----------------------------------------------------------------------
typedef struct {
int socket;
struct sockaddr_in addres;
/* Messages that waiting for send. */
message_queue_t send_buffer;
/* Buffered sending message.
*
* In case we doesn't send whole message per one call send().
* And current_sending_byte is a pointer to the part of data that will be send next call.
*/
message_t sending_buffer;
size_t current_sending_byte;
/* The same for the receiving message. */
message_t receiving_buffer;
size_t current_receiving_byte;
} peer_t;
int delete_peer(peer_t *peer)
{
close(peer->socket);
delete_message_queue(&peer->send_buffer);
}
int create_peer(peer_t *peer)
{
create_message_queue(MAX_MESSAGES_BUFFER_SIZE, &peer->send_buffer);
peer->current_sending_byte = -1;
peer->current_receiving_byte = 0;
return 0;
}
char *peer_get_addres_str(peer_t *peer)
{
static char ret[INET_ADDRSTRLEN + 10];
char peer_ipv4_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &peer->addres.sin_addr, peer_ipv4_str, INET_ADDRSTRLEN);
sprintf(ret, "%s:%d", peer_ipv4_str, peer->addres.sin_port);
return ret;
}
int peer_add_to_send(peer_t *peer, message_t *message)
{
return enqueue(&peer->send_buffer, message);
}
/* Receive message from peer and handle it with message_handler(). */
int receive_from_peer(peer_t *peer, int (*message_handler)(message_t *))
{
printf("Ready for recv() from %s.\n", peer_get_addres_str(peer));
size_t len_to_receive;
ssize_t received_count;
size_t received_total = 0;
do {
// Is completely received?
if (peer->current_receiving_byte >= sizeof(peer->receiving_buffer)) {
message_handler(&peer->receiving_buffer);
peer->current_receiving_byte = 0;
}
// Count bytes to send.
len_to_receive = sizeof(peer->receiving_buffer) - peer->current_receiving_byte;
if (len_to_receive > MAX_SEND_SIZE)
len_to_receive = MAX_SEND_SIZE;
printf("Let's try to recv() %zd bytes... ", len_to_receive);
received_count = recv(peer->socket, (char *)&peer->receiving_buffer + peer->current_receiving_byte, len_to_receive, MSG_DONTWAIT);
if (received_count < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("peer is not ready right now, try again later.\n");
}
else {
perror("recv() from peer error");
return -1;
}
}
else if (received_count < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
break;
}
// If recv() returns 0, it means that peer gracefully shutdown. Shutdown client.
else if (received_count == 0) {
printf("recv() 0 bytes. Peer gracefully shutdown.\n");
return -1;
}
else if (received_count > 0) {
peer->current_receiving_byte += received_count;
received_total += received_count;
printf("recv() %zd bytes\n", received_count);
}
} while (received_count > 0);
printf("Total recv()'ed %zu bytes.\n", received_total);
return 0;
}
int send_to_peer(peer_t *peer)
{
printf("Ready for send() to %s.\n", peer_get_addres_str(peer));
size_t len_to_send;
ssize_t send_count;
size_t send_total = 0;
do {
// If sending message has completely sent and there are messages in queue, why not send them?
if (peer->current_sending_byte < 0 || peer->current_sending_byte >= sizeof(peer->sending_buffer)) {
printf("There is no pending to send() message, maybe we can find one in queue... ");
if (dequeue(&peer->send_buffer, &peer->sending_buffer) != 0) {
peer->current_sending_byte = -1;
printf("No, there is nothing to send() anymore.\n");
break;
}
printf("Yes, pop and send() one of them.\n");
peer->current_sending_byte = 0;
}
// Count bytes to send.
len_to_send = sizeof(peer->sending_buffer) - peer->current_sending_byte;
if (len_to_send > MAX_SEND_SIZE)
len_to_send = MAX_SEND_SIZE;
printf("Let's try to send() %zd bytes... ", len_to_send);
send_count = send(peer->socket, (char *)&peer->sending_buffer + peer->current_sending_byte, len_to_send, 0);
if (send_count < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("peer is not ready right now, try again later.\n");
}
else {
perror("send() from peer error");
return -1;
}
}
// we have read as many as possible
else if (send_count < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
break;
}
else if (send_count == 0) {
printf("send()'ed 0 bytes. It seems that peer can't accept data right now. Try again later.\n");
break;
}
else if (send_count > 0) {
peer->current_sending_byte += send_count;
send_total += send_count;
printf("send()'ed %zd bytes.\n", send_count);
}
} while (send_count > 0);
printf("Total send()'ed %zu bytes.\n", send_total);
return 0;
}
// common ---------------------------------------------------------------------
/* Reads from stdin and create new message. This message enqueues to send queueu. */
int read_from_stdin(char *read_buffer, size_t max_len)
{
memset(read_buffer, 0, max_len);
ssize_t read_count = 0;
ssize_t total_read = 0;
do {
read_count = read(STDIN_FILENO, read_buffer, max_len);
if (read_count < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
perror("read()");
return -1;
}
else if (read_count < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
break;
}
else if (read_count > 0) {
total_read += read_count;
if (total_read > max_len) {
printf("Message too large and will be chopped. Please try to be shorter next time.\n");
fflush(STDIN_FILENO);
break;
}
}
} while (read_count > 0);
size_t len = strlen(read_buffer);
if (len > 0 && read_buffer[len - 1] == '\n')
read_buffer[len - 1] = '\0';
printf("Read from stdin %zu bytes. Let's prepare message to send.\n", strlen(read_buffer));
return 0;
}
#endif /* MESSAGE_H */
// Simple example of server with select() and multiple clients.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <unistd.h>
#include "message.h"
#define MAX_CLIENTS 10
#define NO_SOCKET -1
#define SERVER_NAME "server"
int listen_sock;
peer_t connection_list[MAX_CLIENTS];
char read_buffer[1024]; // buffer for stdin
void shutdown_properly(int code);
void handle_signal_action(int sig_number)
{
if (sig_number == SIGINT) {
printf("SIGINT was catched!\n");
shutdown_properly(EXIT_SUCCESS);
}
else if (sig_number == SIGPIPE) {
printf("SIGPIPE was catched!\n");
shutdown_properly(EXIT_SUCCESS);
}
}
int setup_signals()
{
struct sigaction sa;
sa.sa_handler = handle_signal_action;
if (sigaction(SIGINT, &sa, 0) != 0) {
perror("sigaction()");
return -1;
}
if (sigaction(SIGPIPE, &sa, 0) != 0) {
perror("sigaction()");
return -1;
}
return 0;
}
/* Start listening socket listen_sock. */
int start_listen_socket(int *listen_sock)
{
// Obtain a file descriptor for our "listening" socket.
*listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (*listen_sock < 0) {
perror("socket");
return -1;
}
int reuse = 1;
if (setsockopt(*listen_sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) != 0) {
perror("setsockopt");
return -1;
}
struct sockaddr_in my_addr;
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = inet_addr(SERVER_IPV4_ADDR);
my_addr.sin_port = htons(SERVER_LISTEN_PORT);
if (bind(*listen_sock, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0) {
perror("bind");
return -1;
}
// start accept client connections
if (listen(*listen_sock, 10) != 0) {
perror("listen");
return -1;
}
printf("Accepting connections on port %d.\n", (int)SERVER_LISTEN_PORT);
return 0;
}
void shutdown_properly(int code)
{
int i;
close(listen_sock);
for (i = 0; i < MAX_CLIENTS; ++i)
if (connection_list[i].socket != NO_SOCKET)
close(connection_list[i].socket);
printf("Shutdown server properly.\n");
exit(code);
}
int build_fd_sets(fd_set *read_fds, fd_set *write_fds, fd_set *except_fds)
{
int i;
FD_ZERO(read_fds);
FD_SET(STDIN_FILENO, read_fds);
FD_SET(listen_sock, read_fds);
for (i = 0; i < MAX_CLIENTS; ++i)
if (connection_list[i].socket != NO_SOCKET)
FD_SET(connection_list[i].socket, read_fds);
FD_ZERO(write_fds);
for (i = 0; i < MAX_CLIENTS; ++i)
if (connection_list[i].socket != NO_SOCKET && connection_list[i].send_buffer.current > 0)
FD_SET(connection_list[i].socket, write_fds);
FD_ZERO(except_fds);
FD_SET(STDIN_FILENO, except_fds);
FD_SET(listen_sock, except_fds);
for (i = 0; i < MAX_CLIENTS; ++i)
if (connection_list[i].socket != NO_SOCKET)
FD_SET(connection_list[i].socket, except_fds);
return 0;
}
int handle_new_connection()
{
struct sockaddr_in client_addr;
memset(&client_addr, 0, sizeof(client_addr));
socklen_t client_len = sizeof(client_addr);
int new_client_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &client_len);
if (new_client_sock < 0) {
perror("accept()");
return -1;
}
char client_ipv4_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client_addr.sin_addr, client_ipv4_str, INET_ADDRSTRLEN);
printf("Incoming connection from %s:%d.\n", client_ipv4_str, client_addr.sin_port);
int i;
for (i = 0; i < MAX_CLIENTS; ++i) {
if (connection_list[i].socket == NO_SOCKET) {
connection_list[i].socket = new_client_sock;
connection_list[i].addres = client_addr;
connection_list[i].current_sending_byte = -1;
connection_list[i].current_receiving_byte = 0;
return 0;
}
}
printf("There is too much connections. Close new connection %s:%d.\n", client_ipv4_str, client_addr.sin_port);
close(new_client_sock);
return -1;
}
int close_client_connection(peer_t *client)
{
printf("Close client socket for %s.\n", peer_get_addres_str(client));
close(client->socket);
client->socket = NO_SOCKET;
dequeue_all(&client->send_buffer);
client->current_sending_byte = -1;
client->current_receiving_byte = 0;
}
/* Reads from stdin and create new message. This message enqueues to send queueu. */
int handle_read_from_stdin()
{
char read_buffer[DATA_MAXSIZE]; // buffer for stdin
if (read_from_stdin(read_buffer, DATA_MAXSIZE) != 0)
return -1;
// Create new message and enqueue it.
message_t new_message;
prepare_message(SERVER_NAME, read_buffer, &new_message);
print_message(&new_message);
/* enqueue message for all clients */
int i;
for (i = 0; i < MAX_CLIENTS; ++i) {
if (connection_list[i].socket != NO_SOCKET) {
if (peer_add_to_send(&connection_list[i], &new_message) != 0) {
printf("Send buffer was overflowed, we lost this message!\n");
continue;
}
printf("New message to send was enqueued right now.\n");
}
}
return 0;
}
int handle_received_message(message_t *message)
{
printf("Received message from client.\n");
print_message(message);
return 0;
}
int main(int argc, char **argv)
{
if (setup_signals() != 0)
exit(EXIT_FAILURE);
if (start_listen_socket(&listen_sock) != 0)
exit(EXIT_FAILURE);
/* Set nonblock for stdin. */
int flag = fcntl(STDIN_FILENO, F_GETFL, 0);
flag |= O_NONBLOCK;
fcntl(STDIN_FILENO, F_SETFL, flag);
int i;
for (i = 0; i < MAX_CLIENTS; ++i) {
connection_list[i].socket = NO_SOCKET;
create_peer(&connection_list[i]);
}
fd_set read_fds;
fd_set write_fds;
fd_set except_fds;
int high_sock = listen_sock;
printf("Waiting for incoming connections.\n");
while (1) {
build_fd_sets(&read_fds, &write_fds, &except_fds);
high_sock = listen_sock;
for (i = 0; i < MAX_CLIENTS; ++i) {
if (connection_list[i].socket > high_sock)
high_sock = connection_list[i].socket;
}
int activity = select(high_sock + 1, &read_fds, &write_fds, &except_fds, NULL);
switch (activity) {
case -1:
perror("select()");
shutdown_properly(EXIT_FAILURE);
case 0:
// you should never get here
printf("select() returns 0.\n");
shutdown_properly(EXIT_FAILURE);
default:
/* All set fds should be checked. */
if (FD_ISSET(STDIN_FILENO, &read_fds)) {
if (handle_read_from_stdin() != 0)
shutdown_properly(EXIT_FAILURE);
}
if (FD_ISSET(listen_sock, &read_fds)) {
handle_new_connection();
}
if (FD_ISSET(STDIN_FILENO, &except_fds)) {
printf("except_fds for stdin.\n");
shutdown_properly(EXIT_FAILURE);
}
if (FD_ISSET(listen_sock, &except_fds)) {
printf("Exception listen socket fd.\n");
shutdown_properly(EXIT_FAILURE);
}
for (i = 0; i < MAX_CLIENTS; ++i) {
if (connection_list[i].socket != NO_SOCKET && FD_ISSET(connection_list[i].socket, &read_fds)) {
if (receive_from_peer(&connection_list[i], &handle_received_message) != 0) {
close_client_connection(&connection_list[i]);
continue;
}
}
if (connection_list[i].socket != NO_SOCKET && FD_ISSET(connection_list[i].socket, &write_fds)) {
if (send_to_peer(&connection_list[i]) != 0) {
close_client_connection(&connection_list[i]);
continue;
}
}
if (connection_list[i].socket != NO_SOCKET && FD_ISSET(connection_list[i].socket, &except_fds)) {
printf("Exception client fd.\n");
close_client_connection(&connection_list[i]);
continue;
}
}
}
printf("And we are still waiting for clients' or stdin activity. You can type something to send:\n");
}
return 0;
}
@Globik

This comment has been minimized.

Copy link

Globik commented May 28, 2018

Why are the listen_sock and the client_sock not non_block?

@johnmiketaylor

This comment has been minimized.

Copy link

johnmiketaylor commented Sep 4, 2018

Is the file message.h available anywhere so that I can run this code?
thanks!

@aleparedes00

This comment has been minimized.

Copy link

aleparedes00 commented Dec 30, 2018

Is the file message.h available anywhere so that I can run this code?
thanks!

It's inside file common.h

@dibhar

This comment has been minimized.

Copy link

dibhar commented Jan 29, 2019

How can I compile this code?

@simpyparveen

This comment has been minimized.

Copy link

simpyparveen commented May 26, 2019

How can I compile this code?
Compile:-
gcc client.c -o client
gcc server.c -o server

Run:-
./server
./client 127.0.0.1

Before this, include common.h in both client.c and server.c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.