1 | /////////////////////////////////////////////////////////////////////////////////////// |
---|
2 | // file : tcp_chat.c |
---|
3 | // author : Alain Greiner |
---|
4 | // date : december 2020 |
---|
5 | /////////////////////////////////////////////////////////////////////////////////////// |
---|
6 | // This file describes a TCP chat application. Depending on the "is_server argument, |
---|
7 | // it can be used to launch the the TCP client, or the TCP server application. |
---|
8 | // The client send the first message. The server wait this first message. |
---|
9 | // The 4 command line arguments are: is_server, local_ip_addr, remote_ip_addr, port |
---|
10 | /////////////////////////////////////////////////////////////////////////////////////// |
---|
11 | |
---|
12 | #include <stdio.h> |
---|
13 | #include <stdlib.h> |
---|
14 | #include <string.h> |
---|
15 | #include <almosmkh.h> |
---|
16 | #include <unistd.h> |
---|
17 | #include <sys/socket.h> |
---|
18 | |
---|
19 | #define BUF_SIZE 256 |
---|
20 | |
---|
21 | //////////////////////////// |
---|
22 | void client_chat( int fdid ) |
---|
23 | { |
---|
24 | char buffer[BUF_SIZE]; // string buffer (for both send and receive) |
---|
25 | int size; // string length (including NUL) |
---|
26 | int nbytes; // number of characters actually sent or received |
---|
27 | |
---|
28 | while( 1 ) |
---|
29 | { |
---|
30 | //////// display client prompt to wait local client message //////// |
---|
31 | printf("\n[local client] "); |
---|
32 | |
---|
33 | // build local client message |
---|
34 | size = get_string( buffer , BUF_SIZE ); |
---|
35 | |
---|
36 | // exit chat function when client message is the "exit" string |
---|
37 | if( strncmp( "exit" , buffer , 4 ) == 0 ) |
---|
38 | { |
---|
39 | printf("\n[client_chat] local message is <exit> => return to main\n" ); |
---|
40 | return; |
---|
41 | } |
---|
42 | |
---|
43 | // send local client message |
---|
44 | nbytes = send( fdid , buffer , size , 0 ); |
---|
45 | |
---|
46 | if( nbytes != size ) |
---|
47 | { |
---|
48 | printf("\n[client_chat error] cannot send => return to main\n"); |
---|
49 | return; |
---|
50 | } |
---|
51 | |
---|
52 | ///////// display server prompt to wait remote server message /////// |
---|
53 | printf("\n[remote server] "); |
---|
54 | |
---|
55 | // receive remote server message |
---|
56 | nbytes = recv( fdid , buffer , BUF_SIZE , 0 ); |
---|
57 | |
---|
58 | if( nbytes < 0 ) |
---|
59 | { |
---|
60 | printf("\n\n[client_chat error] cannot receive => return to main\n" ); |
---|
61 | return; |
---|
62 | } |
---|
63 | else if( nbytes == 0 ) |
---|
64 | { |
---|
65 | printf("\n\n[client_chat] receive EOF => return to main\n" ); |
---|
66 | return; |
---|
67 | } |
---|
68 | |
---|
69 | // display remote server message |
---|
70 | printf("%s\n", buffer ); |
---|
71 | } |
---|
72 | } // end client_chat() |
---|
73 | |
---|
74 | |
---|
75 | //////////////////////////// |
---|
76 | void server_chat( int fdid ) |
---|
77 | { |
---|
78 | char buffer[BUF_SIZE]; // string buffer (for send and receive) |
---|
79 | int size; // string length (including NUL) |
---|
80 | int nbytes; // number of characters actually sent or received |
---|
81 | |
---|
82 | while( 1 ) |
---|
83 | { |
---|
84 | //////// display client prompt to wait remote client message /////// |
---|
85 | printf("\n[remote client] "); |
---|
86 | |
---|
87 | // receive remote client message |
---|
88 | nbytes = recv( fdid , buffer , BUF_SIZE , 0 ); |
---|
89 | |
---|
90 | if( nbytes < 0 ) |
---|
91 | { |
---|
92 | printf("\n\n[server_chat error] cannot receive => return to main\n" ); |
---|
93 | return; |
---|
94 | } |
---|
95 | else if( nbytes == 0 ) |
---|
96 | { |
---|
97 | printf("\n\n[server_chat] receive EOF => return to main\n" ); |
---|
98 | return; |
---|
99 | } |
---|
100 | |
---|
101 | // display remote client message |
---|
102 | printf("%s\n", buffer ); |
---|
103 | |
---|
104 | //////// display server prompt to wait local server message ////// |
---|
105 | printf("\n[local server] "); |
---|
106 | |
---|
107 | // build local server message |
---|
108 | size = get_string( buffer , BUF_SIZE ); |
---|
109 | |
---|
110 | // exit chat function when server message is the "exit" string |
---|
111 | if( strncmp( "exit" , buffer , 4 ) == 0 ) |
---|
112 | { |
---|
113 | printf("\n[server_chat] local message is <exit> => return to main\n" ); |
---|
114 | return; |
---|
115 | } |
---|
116 | |
---|
117 | // send local server message |
---|
118 | nbytes = send( fdid , buffer , size , 0 ); |
---|
119 | |
---|
120 | if( nbytes != size ) |
---|
121 | { |
---|
122 | printf("\n[server_chat error] cannot send => return to main\n"); |
---|
123 | return; |
---|
124 | } |
---|
125 | } |
---|
126 | } // end server_chat() |
---|
127 | |
---|
128 | /////////////////////// |
---|
129 | int main( int argc, |
---|
130 | char ** argv ) |
---|
131 | { |
---|
132 | int pid; // process identifier |
---|
133 | int fdid; // file index of local socket |
---|
134 | int new_fdid; // file index for new derver socket |
---|
135 | int error; |
---|
136 | |
---|
137 | sockaddr_in_t local_sin; // local socket internet address |
---|
138 | sockaddr_in_t remote_sin; // remote socket internet address |
---|
139 | |
---|
140 | int addr_length = sizeof(sockaddr_t); |
---|
141 | |
---|
142 | unsigned long long start_cycle; |
---|
143 | |
---|
144 | // get start cycle |
---|
145 | get_cycle( &start_cycle ); |
---|
146 | |
---|
147 | // get PID |
---|
148 | pid = getpid(); |
---|
149 | |
---|
150 | // get arguments |
---|
151 | if( argc != 4 ) |
---|
152 | { |
---|
153 | printf("\n usage : tcp_chat is_server local_addr remote_addr port\n"); |
---|
154 | exit( 0 ); |
---|
155 | } |
---|
156 | |
---|
157 | int is_server = atoi( argv[0] ); |
---|
158 | int local_addr = atoi( argv[1] ); |
---|
159 | int remote_addr = atoi( argv[2] ); |
---|
160 | int port = atoi( argv[3] ); |
---|
161 | |
---|
162 | if( is_server ) |
---|
163 | printf("\n[%s] SERVER / pid %x / cycle %d / local %x / remote %x / port %x\n", |
---|
164 | __FUNCTION__, pid, (unsigned int)start_cycle, local_addr, remote_addr, port ); |
---|
165 | else |
---|
166 | printf("\n[%s] CLIENT / pid %x / cycle %d / local %x / remote %x / port %x\n", |
---|
167 | __FUNCTION__, pid, (unsigned int)start_cycle, local_addr, remote_addr, port ); |
---|
168 | |
---|
169 | // initialize local_sin |
---|
170 | local_sin.sin_domain = HTONS( AF_INET ); |
---|
171 | local_sin.sin_addr = HTONL( local_addr ); |
---|
172 | local_sin.sin_port = HTONS( port ); |
---|
173 | |
---|
174 | // initialize remote_sin |
---|
175 | remote_sin.sin_domain = HTONS( AF_INET ); |
---|
176 | remote_sin.sin_addr = HTONL( remote_addr ); |
---|
177 | remote_sin.sin_port = HTONS( port ); |
---|
178 | |
---|
179 | // create local TCP socket |
---|
180 | fdid = socket( AF_INET, |
---|
181 | SOCK_STREAM, |
---|
182 | 0 ); |
---|
183 | |
---|
184 | if( fdid < 0 ) |
---|
185 | { |
---|
186 | printf("\n[error] in %s : cannot create socket\n", __FUNCTION__ ); |
---|
187 | exit( 0 ); |
---|
188 | } |
---|
189 | else |
---|
190 | { |
---|
191 | printf("\n[%s] created socket[%x,%d]\n", __FUNCTION__, pid, fdid ); |
---|
192 | } |
---|
193 | |
---|
194 | // bind local socket |
---|
195 | error = bind( fdid, |
---|
196 | (sockaddr_t *)(&local_sin), |
---|
197 | sizeof(sockaddr_t) ); |
---|
198 | if( error ) |
---|
199 | { |
---|
200 | printf("\n[error] in %s : cannot bind socket[%x,%d]\n", __FUNCTION__, pid, fdid ); |
---|
201 | exit( 0 ); |
---|
202 | } |
---|
203 | else |
---|
204 | { |
---|
205 | printf("\n[%s] client socket[%x,%d] bound\n", __FUNCTION__, pid, fdid ); |
---|
206 | } |
---|
207 | |
---|
208 | /////////////// |
---|
209 | if( is_server ) |
---|
210 | { |
---|
211 | // Listen client connection request |
---|
212 | error = listen( fdid , 1 ); |
---|
213 | |
---|
214 | if( error ) |
---|
215 | { |
---|
216 | printf("\n[error] in %s : socket[%x,%d] cannot listen\n", |
---|
217 | __FUNCTION__, pid, fdid ); |
---|
218 | exit( 0 ); |
---|
219 | } |
---|
220 | else |
---|
221 | { |
---|
222 | printf("\n[%s] listening socket[%x,%d] wait client connection\n", |
---|
223 | __FUNCTION__ , pid , fdid ); |
---|
224 | } |
---|
225 | |
---|
226 | // get new chat socket fdid |
---|
227 | new_fdid = accept( fdid, |
---|
228 | (sockaddr_t *)&remote_sin, |
---|
229 | &addr_length ); |
---|
230 | |
---|
231 | if( new_fdid < 0 ) |
---|
232 | { |
---|
233 | printf("\n[error] in %s : listening socket[%x,%d] cannot accept\n", |
---|
234 | __FUNCTION__, pid, fdid ); |
---|
235 | exit( 0 ); |
---|
236 | } |
---|
237 | else |
---|
238 | { |
---|
239 | printf("\n[%s] new socket[%x,%d] connected to client\n", |
---|
240 | __FUNCTION__, pid, new_fdid ); |
---|
241 | } |
---|
242 | |
---|
243 | // call server_chat function |
---|
244 | server_chat( new_fdid ); |
---|
245 | |
---|
246 | // 6 close both listen and chat sockets |
---|
247 | close( fdid ); |
---|
248 | close( new_fdid ); |
---|
249 | |
---|
250 | printf("\n[tcp_server] closed both <listen> & <chat> sockets\n" ); |
---|
251 | } |
---|
252 | ////////////////// |
---|
253 | else // is_client |
---|
254 | { |
---|
255 | // connect to remote socket |
---|
256 | error = connect( fdid, |
---|
257 | (sockaddr_t *)(&remote_sin), |
---|
258 | sizeof(sockaddr_t) ); |
---|
259 | if( error ) |
---|
260 | { |
---|
261 | printf("\n[error] in %s : socket[%x,%d] cannot connect\n", |
---|
262 | __FUNCTION__ , pid , fdid ); |
---|
263 | exit( 0 ); |
---|
264 | } |
---|
265 | else |
---|
266 | { |
---|
267 | printf("\n[%s] socket[%x,%d] connected to server\n", |
---|
268 | __FUNCTION__ , pid, fdid ); |
---|
269 | } |
---|
270 | |
---|
271 | // call client_chat function |
---|
272 | client_chat( fdid ); |
---|
273 | |
---|
274 | // close local socket |
---|
275 | close( fdid ); |
---|
276 | |
---|
277 | printf("\n[%s] client closed socket[%x,%d]\n", |
---|
278 | __FUNCTION__, pid, fdid ); |
---|
279 | } |
---|
280 | |
---|
281 | exit(0); |
---|
282 | |
---|
283 | return 0; |
---|
284 | } |
---|