Last active
September 24, 2023 13:47
-
-
Save DmitryKhlus/b5b81363fdad02091f6f to your computer and use it in GitHub Desktop.
Unix domain sockets + select + pthread
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdint.h> | |
#include <string.h> //strlen | |
#include <stdlib.h> | |
#include <errno.h> | |
#include <unistd.h> //close | |
#include <sys/un.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <sys/stat.h> | |
#include <netinet/in.h> | |
#include <sys/time.h> //FD_SET, FD_ISSET, FD_ZERO macros | |
#include<pthread.h> //for threading , link with lpthread | |
#include <fcntl.h> | |
#define SOCK_PATH "/tmp/echo_socket" | |
bool daemonize() { | |
int res = fork(); | |
if (res < 0) { | |
perror("fork"); | |
return false; | |
} | |
if (res != 0) | |
_exit(0); | |
// Now we're running as the daemon... | |
res = setsid(); | |
if (res < 0) { | |
perror("setsid"); | |
return false; | |
} | |
if (false) { | |
int fd = open("/dev/null", O_RDWR, 0); | |
if (fd >= 0) { | |
dup2(fd, STDIN_FILENO); | |
dup2(fd, STDOUT_FILENO); | |
dup2(fd, STDERR_FILENO); | |
if (fd > 2) | |
close(fd); | |
} | |
} | |
return true; | |
} | |
/* | |
* This will handle connection for each client | |
* */ | |
void *connection_handler(void *socket_desc) | |
{ | |
//Get the socket descriptor | |
int& sock = *(int*)socket_desc; | |
int read_size; | |
char client_message[2000]; | |
//Send some messages to the client | |
const char *message = "Greetings! I am your connection handler\n"; | |
write(sock , message , strlen(message)); | |
message = "Now type something and i shall repeat what you type \n"; | |
write(sock , message , strlen(message)); | |
//Receive a message from client | |
while( (read_size = recv(sock , client_message , 2000 , 0)) > 0 ) | |
{ | |
//end of string marker | |
client_message[read_size] = '\0'; | |
//Send the message back to client | |
write(sock , client_message , strlen(client_message)); | |
//clear the message buffer | |
memset(client_message, 0, 2000); | |
} | |
if(read_size == 0) | |
{ | |
puts("Client disconnected"); | |
fflush(stdout); | |
} | |
else if(read_size == -1) | |
{ | |
perror("recv failed"); | |
} | |
// | |
sock = 0; | |
return 0; | |
} | |
int main(int argc , char *argv[]) | |
{ | |
bool dflag = false; | |
if (dflag && !daemonize()) { | |
return -1; | |
} | |
int master_socket; | |
int client_socket[30]; | |
int max_clients = 30; | |
struct sockaddr_un address; | |
//set of socket descriptors | |
fd_set readfds; | |
//initialize all client_socket[] to 0 so not checked | |
memset(client_socket, 0, sizeof(client_socket)); | |
//create a master socket | |
if( (master_socket = socket(AF_UNIX , SOCK_STREAM , 0)) == 0) | |
{ | |
perror("socket failed"); | |
exit(EXIT_FAILURE); | |
} | |
unlink(SOCK_PATH); | |
//type of socket created | |
address.sun_family = AF_UNIX; | |
strcpy(address.sun_path, SOCK_PATH); | |
size_t len = strlen(address.sun_path) + sizeof(address.sun_family); | |
if (bind(master_socket, (struct sockaddr *)&address, len)<0) | |
{ | |
perror("bind failed"); | |
exit(EXIT_FAILURE); | |
} | |
printf("Listener on port %s \n", SOCK_PATH); | |
//try to specify maximum of 3 pending connections for the master socket | |
if (listen(master_socket, 3) < 0) | |
{ | |
perror("listen"); | |
exit(EXIT_FAILURE); | |
} | |
//accept the incoming connection | |
size_t addrlen = sizeof(address); | |
puts("Waiting for connections ..."); | |
while(true) | |
{ | |
//clear the socket set | |
FD_ZERO(&readfds); | |
//add master socket to set | |
FD_SET(master_socket, &readfds); | |
int max_sd = master_socket; | |
//add child sockets to set | |
for (int i = 0 ; i < max_clients ; i++) | |
{ | |
//socket descriptor | |
int sd = client_socket[i]; | |
//if valid socket descriptor then add to read list | |
if(sd > 0) | |
FD_SET( sd , &readfds); | |
//highest file descriptor number, need it for the select function | |
if(sd > max_sd) | |
max_sd = sd; | |
} | |
//wait for an activity on one of the sockets , timeout is NULL , so wait indefinitely | |
int activity = select( max_sd + 1 , &readfds , NULL , NULL , NULL); | |
if ((activity < 0) && (errno != EINTR)) | |
{ | |
printf("select error"); | |
} | |
//If something happened on the master socket , then its an incoming connection | |
if (FD_ISSET(master_socket, &readfds)) | |
{ | |
int new_socket = 0; | |
if ((new_socket = accept(master_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) | |
{ | |
perror("accept"); | |
exit(EXIT_FAILURE); | |
} | |
puts("Welcome message sent successfully"); | |
//add new socket to array of sockets | |
for (int i = 0; i < max_clients; i++) | |
{ | |
//if position is empty | |
if( client_socket[i] == 0 ) | |
{ | |
client_socket[i] = new_socket; | |
printf("Adding to list of sockets as %d\n" , i); | |
pthread_t thread_id; | |
if( pthread_create( &thread_id , NULL , connection_handler , (void*) &client_socket[i]) < 0) | |
{ | |
perror("could not create thread"); | |
return 1; | |
} | |
pthread_detach(thread_id); | |
break; | |
} | |
} | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment