Home | Notes | Projects > TCP/IP Group Chat Application
Developed multi‐threaded client/server socket applications for Linux OS using C and the POSIX thread libraries
Used Vim for software development, and GDB for debugging
Operating System
Ubuntu 22.04.1 LTS (Kernel version: 5.15.0-52-generic)
Compiler
GCC version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04)
Debugger GDB version 12.0.90 (Ubuntu 12.0.90-0ubuntu1)
groupchat_linux_server.c
xxxxxxxxxx1761//========================================================================================2// Filename : groupchat_linux_server.c3// Description : Group chat application - Server4// Author : Kyungjae Lee5// History : 10/20/2022 - Created file6// 10/26/2022 - Added descriptions to the compilation command flags7// 10/27/2022 - Updated code comments8// 11/11/2022 - Modified function name 'print_err' -> 'handle_err'9// 11/18/2022 - Added screen clear feature 'system("clear")'10//----------------------------------------------------------------------------------------11// To compile this program:12// gcc groupchat_linux_server.c -D_REENTRANT -o server -lpthread13// ------------ ---------14// | |15// drives the compiler to use adds support for multithreading16// thread safe (i.e., re-entrant) with the pthreads library17// versions of several functions 18// in the C library 19//20// To run this program:21// ./server <port>22//23// [!] Note: For this program to work properly with clients in the different network, TCP24// port must be configured using port-forwarding.25//========================================================================================26
272829303132333435
363738
39// function declarations40void* serve_cl(void *arg);41void broadcast_msg(char *msg, int len);42void handle_err(char *err_msg);43
44// global variables 45int cl_cnt = 0; // counter for connected clients46int cl_socks[MAX_CLIENT]; // array (list) of the connected clients47pthread_mutex_t mutex; 48
49
50int main(int argc, char *argv[])51{52 int sv_sock, cl_sock; // server/client socket file descriptors53 struct sockaddr_in sv_addr, cl_addr; // Internet socket address structures54 int cl_addr_sz; // size of the client sockaddr_in structure55 pthread_t t_id; // thread ID56
57 // handle invalid number of arguments58 if (argc != 2)59 {60 printf("Usage: %s <port>\n", argv[0]);61 exit(1);62 }63
64 pthread_mutex_init(&mutex, NULL);65
66 // create a TCP socket67 sv_sock = socket(PF_INET, SOCK_STREAM, 0); 68
69 // allocate and initialize Internet socket address structure (server info)70 // specify server's IP address and port71 memset(&sv_addr, 0, sizeof(sv_addr));72 sv_addr.sin_family = AF_INET;73 sv_addr.sin_addr.s_addr = htonl(INADDR_ANY);74 sv_addr.sin_port = htons(atoi(argv[1]));75
76 // bind server's address information to the server socket77 if (bind(sv_sock, (struct sockaddr*)&sv_addr, sizeof(sv_addr)) == -1)78 handle_err("ERROR: bind() fail");79
80 // convert the server socket to listening socket81 if (listen(sv_sock, 5) == -1)82 handle_err("ERROR: listen() fail");83
84 // clear the screen85 system("clear");86
87 // print the welcome message to the screen88 printf("Group chat linux server running ... (port: %s)\n", argv[1]);89
90 while (1)91 {92 // get the size of Internet socket address structure93 cl_addr_sz = sizeof(cl_addr);94
95 // accept client connection96 cl_sock = accept(sv_sock, (struct sockaddr*)&cl_addr, &cl_addr_sz);97
98 // add the new client to the list99 pthread_mutex_lock(&mutex); // enter the critical section 100 cl_socks[cl_cnt++] = cl_sock; 101 pthread_mutex_unlock(&mutex); // leave the critical section102
103 // create a thread to serve the client104 pthread_create(&t_id, NULL, serve_cl, (void*)&cl_sock); 105
106 // detach the thread from the main thread so that it automatically destroys itself107 // upton termination108 pthread_detach(t_id); 109
110 // print the connected client's IP address to the screen111 printf("Connected client IP: %s\n", inet_ntoa(cl_addr.sin_addr));112 }113
114 close(sv_sock);115 116 return 0;117} // end of main118
119// receive client's message, and broadcast it to all connected clients120void* serve_cl(void *arg)121{122 int cl_sock = *((int*)arg);123 int str_len = 0, i;124 char msg[MSG_LEN];125
126 // this while loop won't break until the client terminates the connection127 while ((str_len = read(cl_sock, msg, sizeof(msg))) != 0)128 broadcast_msg(msg, str_len);129 130 pthread_mutex_lock(&mutex); // enter the critical section 131
132 // before terminating the thread, remove itself (disconnected client) from the list133 for (i = 0; i < cl_cnt; i++) 134 {135 if (cl_sock == cl_socks[i])136 {137 while (i < cl_cnt - 1)138 {139 cl_socks[i] = cl_socks[i + 1];140 i++;141 }142
143 break;144 }145 }146 147 cl_cnt--; // decrement the client counter148 pthread_mutex_unlock(&mutex); // leave the critical section 149
150 // terminate the connection151 close(cl_sock);152
153 return NULL;154} // end of serve_cl155
156// broadcast message to all connected clients157void broadcast_msg(char *msg, int len)158{159 int i;160
161 pthread_mutex_lock(&mutex); // enter the critical section 162
163 // send message to every connected clients164 for (i = 0; i < cl_cnt; i++)165 write(cl_socks[i], msg, len);166
167 pthread_mutex_unlock(&mutex); // leave the critical section 168} // end of broadcast_msg169
170// handle error171void handle_err(char *err_msg)172{173 fputs(err_msg, stderr);174 fputc('\n', stderr);175 exit(1);176} // end of handle_errgroupchat_linux_client.c
xxxxxxxxxx11651//========================================================================================2// Filename : groupchat_linux_client.c3// Description : Group chat application - Client4// Author : Kyungjae Lee5// History : 10/20/2022 - Created file6// 10/26/2022 - Added join/leave message broadcasting functionality7// - Added descriptions to the compilation command flags8// 10/27/2022 - Updated code comments9// 11/11/2022 - Modified function name 'print_err' -> 'handle_err'10// 11/18/2022 - Added screen clear feature 'system("clear")'11//----------------------------------------------------------------------------------------12// To compile this program:13// gcc groupchat_linux_client.c -D_REENTRANT -o client -lpthread14// ------------ ---------15// | |16// drives the compiler to use adds support for multithreading17// thread safe (i.e., re-entrant) with the pthreads library18// versions of several functions 19// in the C library 20//21// To run this program:22// ./client <serverIP> <port> <name>23//24// [!] Note: For this program to work properly with the server in the different network,25// there must be a designated TCP port configured on the default gateway of the26// network where the server belongs to.27//========================================================================================28
293031323334353637
383940
41// function declarations42void* send_msg(void *arg);43void* recv_msg(void *arg);44void handle_err(char *err_msg);45
46// global variables 47char name[NAME_LEN] = "noname";48char msg[MSG_LEN];49char info_msg[MSG_LEN];50
51
52int main(int argc, char *argv[])53{54 int sock; // client socket file descriptor55 struct sockaddr_in sv_addr; // Internet socket address structure56 pthread_t send_thread, recv_thread; // two threads of the client process57 void *thread_return; // pointer to the thread's return value58
59 // handle invalid number of arguments60 if (argc != 4)61 {62 printf("Usage: %s <serverIP> <port> <name>\n", argv[0]);63 exit(1);64 }65
66 sprintf(name, "%s", argv[3]); // setup user name67
68 // create a TCP socket69 sock = socket(PF_INET, SOCK_STREAM, 0); 70
71 // allocate and initialize the Internet socket address structure (server info)72 // specify server's IP address and port73 memset(&sv_addr, 0, sizeof(sv_addr));74 sv_addr.sin_family = AF_INET;75 sv_addr.sin_addr.s_addr = inet_addr(argv[1]);76 sv_addr.sin_port = htons(atoi(argv[2]));77
78 // establish connection with server79 if (connect(sock, (struct sockaddr*)&sv_addr, sizeof(sv_addr)) == -1)80 handle_err("ERROR: connect() fail");81
82 // clear the screen83 system("clear");84
85 // print the welcome message to the screen86 printf("Welcome to group chat! (Q/q to quit)\n\n");87
88 // print the join message to the screen89 sprintf(msg, "+++++ %s has joined! +++++\n", name);90 write(sock, msg, strlen(msg));91
92 // create two threads to carry out send and receive operations, respectively93 pthread_create(&send_thread, NULL, send_msg, (void*)&sock);94 pthread_create(&recv_thread, NULL, recv_msg, (void*)&sock);95 pthread_join(send_thread, &thread_return);96 pthread_join(recv_thread, &thread_return);97
98 // terminate connection99 close(sock);100
101 return 0;102} // end of main103
104// send message to the server105void* send_msg(void *arg)106{107 int sock = *((int*)arg);108 char name_msg[NAME_LEN + MSG_LEN + 2]; // add '2' for brackets around the name109
110 while (1)111 {112 fgets(msg, MSG_LEN, stdin);113
114 // terminate the connection upon user input Q/q115 if (!strcmp(msg, "q\n") || !strcmp(msg, "Q\n"))116 {117 // print the leave message to the screen118 sprintf(msg, "----- %s has left! -----\n", name);119 write(sock, msg, strlen(msg));120
121 // terminate the connection122 close(sock);123 exit(0);124 }125
126 // construct the message and send it to the server127 sprintf(name_msg, "[%s] %s", name, msg);128 write(sock, name_msg, strlen(name_msg));129 }130 131 return NULL;132} // end of send_msg133
134// receive message and print it to the screen135void* recv_msg(void *arg)136{137 int sock = *((int*)arg);138 char name_msg[NAME_LEN + MSG_LEN + 2];139 int str_len;140
141 while(1)142 {143 // receive the message broadcasted by the server144 str_len = read(sock, name_msg, NAME_LEN + MSG_LEN + 2 - 1); 145 // add '2' for brackets around the name146
147 // terminate the thread if the client has been disconnected148 if (str_len == -1)149 return (void*)-1;150
151 // print the received message to the screen152 name_msg[str_len] = 0;153 fputs(name_msg, stdout);154 }155
156 return NULL;157} // end of recv_msg158
159// handle error160void handle_err(char *err_msg)161{162 fputs(err_msg, stderr);163 fputc('\n', stderr);164 exit(1);165} // end of handle_err