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
xxxxxxxxxx
1761//========================================================================================
2// Filename : groupchat_linux_server.c
3// Description : Group chat application - Server
4// Author : Kyungjae Lee
5// History : 10/20/2022 - Created file
6// 10/26/2022 - Added descriptions to the compilation command flags
7// 10/27/2022 - Updated code comments
8// 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 -lpthread
13// ------------ ---------
14// | |
15// drives the compiler to use adds support for multithreading
16// thread safe (i.e., re-entrant) with the pthreads library
17// 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, TCP
24// port must be configured using port-forwarding.
25//========================================================================================
26
27
28
29
30
31
32
33
34
35
36
37
38
39// function declarations
40void* 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 clients
46int cl_socks[MAX_CLIENT]; // array (list) of the connected clients
47pthread_mutex_t mutex;
48
49
50int main(int argc, char *argv[])
51{
52 int sv_sock, cl_sock; // server/client socket file descriptors
53 struct sockaddr_in sv_addr, cl_addr; // Internet socket address structures
54 int cl_addr_sz; // size of the client sockaddr_in structure
55 pthread_t t_id; // thread ID
56
57 // handle invalid number of arguments
58 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 socket
67 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 port
71 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 socket
77 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 socket
81 if (listen(sv_sock, 5) == -1)
82 handle_err("ERROR: listen() fail");
83
84 // clear the screen
85 system("clear");
86
87 // print the welcome message to the screen
88 printf("Group chat linux server running ... (port: %s)\n", argv[1]);
89
90 while (1)
91 {
92 // get the size of Internet socket address structure
93 cl_addr_sz = sizeof(cl_addr);
94
95 // accept client connection
96 cl_sock = accept(sv_sock, (struct sockaddr*)&cl_addr, &cl_addr_sz);
97
98 // add the new client to the list
99 pthread_mutex_lock(&mutex); // enter the critical section
100 cl_socks[cl_cnt++] = cl_sock;
101 pthread_mutex_unlock(&mutex); // leave the critical section
102
103 // create a thread to serve the client
104 pthread_create(&t_id, NULL, serve_cl, (void*)&cl_sock);
105
106 // detach the thread from the main thread so that it automatically destroys itself
107 // upton termination
108 pthread_detach(t_id);
109
110 // print the connected client's IP address to the screen
111 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 main
118
119// receive client's message, and broadcast it to all connected clients
120void* 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 connection
127 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 list
133 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 counter
148 pthread_mutex_unlock(&mutex); // leave the critical section
149
150 // terminate the connection
151 close(cl_sock);
152
153 return NULL;
154} // end of serve_cl
155
156// broadcast message to all connected clients
157void 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 clients
164 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_msg
169
170// handle error
171void handle_err(char *err_msg)
172{
173 fputs(err_msg, stderr);
174 fputc('\n', stderr);
175 exit(1);
176} // end of handle_err
groupchat_linux_client.c
xxxxxxxxxx
11651//========================================================================================
2// Filename : groupchat_linux_client.c
3// Description : Group chat application - Client
4// Author : Kyungjae Lee
5// History : 10/20/2022 - Created file
6// 10/26/2022 - Added join/leave message broadcasting functionality
7// - Added descriptions to the compilation command flags
8// 10/27/2022 - Updated code comments
9// 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 -lpthread
14// ------------ ---------
15// | |
16// drives the compiler to use adds support for multithreading
17// thread safe (i.e., re-entrant) with the pthreads library
18// 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 the
26// network where the server belongs to.
27//========================================================================================
28
29
30
31
32
33
34
35
36
37
38
39
40
41// function declarations
42void* 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 descriptor
55 struct sockaddr_in sv_addr; // Internet socket address structure
56 pthread_t send_thread, recv_thread; // two threads of the client process
57 void *thread_return; // pointer to the thread's return value
58
59 // handle invalid number of arguments
60 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 name
67
68 // create a TCP socket
69 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 port
73 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 server
79 if (connect(sock, (struct sockaddr*)&sv_addr, sizeof(sv_addr)) == -1)
80 handle_err("ERROR: connect() fail");
81
82 // clear the screen
83 system("clear");
84
85 // print the welcome message to the screen
86 printf("Welcome to group chat! (Q/q to quit)\n\n");
87
88 // print the join message to the screen
89 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, respectively
93 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 connection
99 close(sock);
100
101 return 0;
102} // end of main
103
104// send message to the server
105void* 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 name
109
110 while (1)
111 {
112 fgets(msg, MSG_LEN, stdin);
113
114 // terminate the connection upon user input Q/q
115 if (!strcmp(msg, "q\n") || !strcmp(msg, "Q\n"))
116 {
117 // print the leave message to the screen
118 sprintf(msg, "----- %s has left! -----\n", name);
119 write(sock, msg, strlen(msg));
120
121 // terminate the connection
122 close(sock);
123 exit(0);
124 }
125
126 // construct the message and send it to the server
127 sprintf(name_msg, "[%s] %s", name, msg);
128 write(sock, name_msg, strlen(name_msg));
129 }
130
131 return NULL;
132} // end of send_msg
133
134// receive message and print it to the screen
135void* 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 server
144 str_len = read(sock, name_msg, NAME_LEN + MSG_LEN + 2 - 1);
145 // add '2' for brackets around the name
146
147 // terminate the thread if the client has been disconnected
148 if (str_len == -1)
149 return (void*)-1;
150
151 // print the received message to the screen
152 name_msg[str_len] = 0;
153 fputs(name_msg, stdout);
154 }
155
156 return NULL;
157} // end of recv_msg
158
159// handle error
160void handle_err(char *err_msg)
161{
162 fputs(err_msg, stderr);
163 fputc('\n', stderr);
164 exit(1);
165} // end of handle_err