├── Makefile
├── README.md
├── client.c
├── server.c
└── server_single.c
/Makefile:
--------------------------------------------------------------------------------
1 | all:
2 | gcc -o server server.c
3 | gcc -o client client.c
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # C Socket Programming: Simple Server and Client
2 |
3 | `server.c` - multithreaded server
4 |
5 | `server_single.c` - singlethreaded server
6 |
7 | `client.c` - client
8 |
9 | ## Socket Programming
10 |
11 | This is pre-project tutorial for socket programming
12 |
13 | If you are familiar to socket programming, then jump to Let’s Practice Part
14 |
15 | ### 1. What is Socket?
16 |
17 | * With socket, two different processes can communicate each other
18 | * Socket is nothing but a ```file```
19 | * You can just imagine that two different processes have files (socket) and the they read received data from socket and write to socket for sending data to network
20 | * So, socket has file descriptor, which is just an integer to identify opened file
21 |
22 |
23 |
24 |
25 |
26 | ### 2. Socket Types
27 |
28 | There are two commonly used socket types ```Stream Sockets``` and ```Datagram Sockets```. Stream sockets uses TCP for data transmission, and Datagram sockets uses UDP.
29 |
30 | ### 3. Client Process & Server Process
31 |
32 | #### Client : Typically request to server for information.
33 |
34 | * Create a socket with the ```socket()``` system call
35 | * Connect socket to the address of the server using the ```connect()``` system call
36 | * Send and receive data. There are a number of ways to do this, but the simplest way is to use the ```read()``` and ```write()``` system calls
37 |
38 | #### Server : Takes request from the clients, perform required processing, and send it to the client
39 |
40 | * Create a socket with the ```socket()``` system call
41 | * Bind the socket to an address (IP + port) using the ```bind()``` system call.
42 | * Listen for connections with the ```listen()``` system call
43 | * Accept a connection with the ```accept()``` system call. This call typically blocks the connection until a client connects with the server
44 | * Send and receive data using the ```read()``` and ```write()``` system calls
45 |
46 |
47 |
48 |
49 |
50 | Interaction between server and client
51 |
52 | ### Preliminary knowledge before programming
53 |
54 | ## 1. Structures
55 |
56 | You will use socket functions, and most of the socket functions use socket address structures.
57 |
58 | - `sockaddr` : generic socket address structure
59 |
60 | ```objectivec
61 | struct sockaddr {
62 | // represents an address family, most of cases AF_INET)
63 | unsigned short sa_family;
64 |
65 | // 14 bytes of protocol specific address, for the internet
66 | // family, port number IP address (sockaddr_in) is used
67 | char sa_data[14];
68 | }
69 | ```
70 |
71 | - `sockaddr_in` : one type of sockaddr, it represents port number IP address
72 |
73 | ```objectivec
74 | struct sockaddr_in {
75 | short int sin_family; // AF_INET
76 | unsigned short int sin_port; // 16-bit port number
77 | struct in_addr sin_addr; // 32-bit IP address
78 | unsigned char sin_zero[8];
79 | }
80 | ```
81 |
82 | - `in_addr` : structure used in abote sockaddr_in
83 |
84 | ```objectivec
85 | struct in_addr {
86 | unsigned long s_addr;
87 | }
88 | ```
89 |
90 | - `hostent` : contains information related to host
91 |
92 | ```objectivec
93 | struct hostent {
94 | char *h_name; // e.g. unist.ac.kr
95 | char **h_aliases; // list of host name alias
96 | int h_addrtype; // AF_INET
97 | int h_length; // length of ip address
98 | char **h_addr_list; // points to structure in_addr
99 | #define h_addr h_addr_list[0]
100 | };
101 | ```
102 |
103 | ## 2. Network Byte Orders
104 |
105 | All computers doesn’t store bytes in same order. → Two different ways
106 |
107 | - Little Endian : low-order byte is stored on the starting addresses
108 | - Bit Endian : high-order byte is stored on the starting address
109 |
110 | → To make machines with different byte order communicate each other, Internet protocol specify a canonical byte order convention for data transmitted over the network. This is called Network Byte Order.
111 |
112 | `sin_port` `sin_addr` of `sockaddr_in` should be set with this Network Byte Order.
113 |
114 | ```objectivec
115 | htons() : Host to Network Short
116 | htonl() : Host to Network Long
117 | ntohl() : Network to Host Long
118 | ntohs() : Network to Host Short
119 | ```
120 |
121 | ## 3. IP Address Function
122 |
123 | These functions manipulate IP addresses between ASCII strings and network byte ordered binary values.
124 |
125 | - int **inet_aton**(const char *strptr, struct in_addr *addrptr)
126 |
127 | ```objectivec
128 | #include
129 | int retval;
130 | struct in_addr addrptr
131 | memset(&addrptr, '\0', sizeof(addrptr));
132 | retval = inet_aton("68.178.157.132", &addrptr);
133 | ```
134 |
135 | - in_addr_t **inet_addr**(const char *strptr)
136 |
137 | ```objectivec
138 | #include
139 | struct sockaddr_in dest;
140 | memset(&dest, '\0', sizeof(dest));
141 | dest.sin_addr.s_addr = inet_addr("68.178.157.132");
142 | ```
143 |
144 | char ***inet_ntoa**(struct in_addr inaddr)
145 |
146 | ```objectivec
147 | #include
148 | char *ip;
149 | ip = inet_ntoa(dest.sin_addr);
150 | printf("IP Address is : %s\n", ip);
151 | ```
152 |
153 | ## 4. Socket Functions
154 |
155 | (you can use bold scripted parameter for the first use)
156 |
157 | 1. `socket`
158 |
159 | ```objectivec
160 | #include
161 | #include
162 | int socket (int family, int type, int protocol);
163 | ```
164 |
165 | - family : AF_INET, AF_INET6, AF_LOCAL, AF_ROUTE, AF_KEY
166 | - type : SOCK_STREAM (TCP), SOCK_DGRAM (UDP), SOCK_SEQPACKET, SOCK_RAW
167 | - protocol : IPPROTO_TCP, IPPROTO_UDP, IPPROTO_SCTP, (0 : system default)
168 |
169 | → This function returns socket descriptor, so you can use it for another functions
170 |
171 | 2. `connect`
172 |
173 | ```objectivec
174 | #include
175 | #include
176 |
177 | int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
178 | ```
179 |
180 | - sockfd : socket descriptor returned by the socket function
181 | - serv_addr : sockaddr that contains destination IP address and port
182 | - addrlen : set it to sizeof(struct sockaddr)
183 |
184 | 3. `bind`
185 |
186 | ```objectivec
187 | #include
188 | #include
189 |
190 | int bind(int sockfd, struct sockaddr *my_addr,int addrlen);
191 | ```
192 |
193 | - my_addr : sockaddr that contains local IP address and port
194 |
195 | 4. `listen`
196 |
197 | ```objectivec
198 | #include
199 | #include
200 |
201 | int listen(int sockfd,int backlog);
202 | ```
203 |
204 | - converts unconnected socket to passive socket (kernel should accept incoming connection request directed to this socket)
205 | - **backlog** : maximum number of connections the kernel should queue for this socket
206 |
207 | 5. `accept`
208 |
209 | ```objectivec
210 | #include
211 | #include
212 |
213 | int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen)
214 | ```
215 |
216 | - returns the next completed connection from the front of the completed connection queue
217 | - cliaddr : sockaddr struct that contains client IP address and port
218 | - **addrlen : set it to sizeof (struct sockaddr)**
219 |
220 | 5. `send`
221 |
222 | ```objectivec
223 | int send(int sockfd, const void *msg, int len, int flags);
224 | ```
225 |
226 | 6. `recv`
227 |
228 | ```objectivec
229 | int recv(int sockfd, void *buf, int len, unsigned int flags);
230 | ```
231 | - buf : buffer to read the information into
232 | - len : It is the maximum length of the buffer
233 | - flags : set it to 0
234 |
235 | 7. for UDP connection
236 |
237 | ```objectivec
238 | int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
239 | int recvfrom(int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen);
240 | ```
241 | - `sendto` and `recvfrom` functions are used instead of `send` `recv`
242 |
243 | 8. `close`
244 |
245 | ```objectivec
246 | int close( int sockfd );
247 | ```
248 |
249 | - close communication
250 |
251 | ### 5. Additional Functions
252 |
253 | 1. `fork`
254 |
255 | - creates new process which is exact copy of current process
256 | - current process is parent, and copied process is child
257 |
258 | ```objectivec
259 | #include
260 | #include
261 |
262 | int fork(void);
263 | ```
264 |
265 | - fork returns 0 when it is child process, and returns child process id when it is parent process. If it is failed, returns -1
266 |
267 | 2. `bzero`
268 |
269 | - place *n*bytes of null byte to string s
270 |
271 | ```objectivec
272 | void bzero(void *s, int nbyte);
273 | ```
274 |
275 | 3. `bcmp`
276 |
277 | - compare nbytes of byte string s1 and byte string s2
278 |
279 | ```objectivec
280 | int bcmp(const void *s1, const void *s2, int nbyte);
281 | ```
282 |
283 | returns 0 if identical, 1 otherwise.
284 |
285 | 4. `bcopy`
286 |
287 | - copy nbytes of byte string s1 to byte string s2
288 |
289 | ```objectivec
290 | void bcopy(const void *s1, void *s2, int nbyte);
291 | ```
292 |
293 | 5. `memset`
294 |
295 | - allocate memory and return pointer which points to the newly allocated memory
296 |
297 | ```objectivec
298 | void *memset(void *s, int c, int nbyte);
299 | ```
300 |
301 | - s : source to be set
302 | - c : character to set on nbyte places
303 | - nbyte : number of bytes
304 |
305 | ## 6. Let’s Practice : echo server and client
306 |
307 | - You have to implement connecting server and client,
308 | and your server and client should quit when the client sends “quit”
309 | you can compile code with command line in the directory of project :
310 |
311 | ```
312 | > make
313 | ```
314 |
315 | - run server and client on server at the same time
316 | - with two different terminal windows
317 |
318 | ```
319 | > ./client
320 | > ./server
321 | ```
322 |
323 | - Sample Output
324 |
325 |
326 |
327 |
328 |
329 | client
330 |
331 |
332 |
333 |
334 |
335 | server
336 |
337 | ## 7. Practice more! : multi user server
338 |
339 | - There can be multiple clients which tries to connect to server simultaneously
340 | - Current echo server doesn’t accept new connection if it is already accepted
341 | - You have to change echo server. Using fork() function, each connection should be run concurrently.
342 | - Hint (pseudo code of multi user server) :
343 |
344 | ```
345 | listen()
346 | while (1) {
347 | newsockfd = accept();
348 | pid = fork();
349 | if (pid == 0) { // client process
350 | close(sockfd);
351 | // do some process - read and write
352 | exit(0);
353 | } else { // parent process
354 | close(newsockfd);
355 | }
356 | }
357 | ```
358 |
359 | - Test : you can just open three terminal windows, run one server and two clients
360 | - test order :
361 |
362 | ```
363 | > run one server and two clients
364 | > client1 sends “hello1”
365 | > client2 sends “hello2”
366 | > client2 sends “world2”
367 | > client1 sends “world1”
368 | > client1 sends “quit” and client2 sends “quit”
369 | ```
370 |
371 | Output :
372 |
373 |
374 |
375 |
376 |
377 | client1
378 |
379 |
380 |
381 |
382 |
383 | client2
384 |
385 |
386 |
387 |
388 |
389 | multi user server
390 |
--------------------------------------------------------------------------------
/client.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include
5 | #include
6 |
7 | #include
8 |
9 | int main(int argc, char *argv[]) {
10 | int sockfd, portno, n;
11 | struct sockaddr_in serv_addr;
12 |
13 | struct hostent *server;
14 |
15 | char buffer[256];
16 | portno = 5001;
17 |
18 | // create socket and get file descriptor
19 | sockfd = socket(AF_INET, SOCK_STREAM, 0);
20 |
21 | server = gethostbyname("127.0.0.1");
22 |
23 | if (server == NULL) {
24 | fprintf(stderr,"ERROR, no such host\n");
25 | exit(0);
26 | }
27 |
28 | bzero((char *) &serv_addr, sizeof(serv_addr));
29 | serv_addr.sin_family = AF_INET;
30 | bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
31 | serv_addr.sin_port = htons(portno);
32 |
33 |
34 | // connect to server with server address which is set above (serv_addr)
35 |
36 | if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
37 | perror("ERROR while connecting");
38 | exit(1);
39 | }
40 |
41 | // inside this while loop, implement communicating with read/write or send/recv function
42 | while (1) {
43 | printf("What do you want to say? ");
44 | bzero(buffer,256);
45 | scanf("%s", buffer);
46 |
47 | n = write(sockfd,buffer,strlen(buffer));
48 |
49 | if (n < 0){
50 | perror("ERROR while writing to socket");
51 | exit(1);
52 | }
53 |
54 | bzero(buffer,256);
55 | n = read(sockfd, buffer, 255);
56 |
57 | if (n < 0){
58 | perror("ERROR while reading from socket");
59 | exit(1);
60 | }
61 | printf("server replied: %s \n", buffer);
62 |
63 | // escape this loop, if the server sends message "quit"
64 |
65 | if (!bcmp(buffer, "quit", 4))
66 | break;
67 | }
68 | return 0;
69 | }
70 |
--------------------------------------------------------------------------------
/server.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include
5 | #include
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | void bzero(void *a, size_t n) {
13 | memset(a, 0, n);
14 | }
15 |
16 | void bcopy(const void *src, void *dest, size_t n) {
17 | memmove(dest, src, n);
18 | }
19 |
20 | struct sockaddr_in* init_sockaddr_in(uint16_t port_number) {
21 | struct sockaddr_in *socket_address = malloc(sizeof(struct sockaddr_in));
22 | memset(socket_address, 0, sizeof(*socket_address));
23 | socket_address -> sin_family = AF_INET;
24 | socket_address -> sin_addr.s_addr = htonl(INADDR_ANY);
25 | socket_address -> sin_port = htons(port_number);
26 | return socket_address;
27 | }
28 |
29 | char* process_operation(char *input) {
30 | size_t n = strlen(input) * sizeof(char);
31 | char *output = malloc(n);
32 | memcpy(output, input, n);
33 | return output;
34 | }
35 |
36 | int main( int argc, char *argv[] ) {
37 |
38 | const uint16_t port_number = 5001;
39 | int server_fd = socket(AF_INET, SOCK_STREAM, 0);
40 |
41 | struct sockaddr_in *server_sockaddr = init_sockaddr_in(port_number);
42 | struct sockaddr_in *client_sockaddr = malloc(sizeof(struct sockaddr_in));
43 | socklen_t server_socklen = sizeof(*server_sockaddr);
44 | socklen_t client_socklen = sizeof(*client_sockaddr);
45 |
46 |
47 | if (bind(server_fd, (const struct sockaddr *) server_sockaddr, server_socklen) < 0)
48 | {
49 | printf("Error! Bind has failed\n");
50 | exit(0);
51 | }
52 | if (listen(server_fd, 3) < 0)
53 | {
54 | printf("Error! Can't listen\n");
55 | exit(0);
56 | }
57 |
58 |
59 | const size_t buffer_len = 256;
60 | char *buffer = malloc(buffer_len * sizeof(char));
61 | char *response = NULL;
62 | time_t last_operation;
63 | __pid_t pid = -1;
64 |
65 | while (1) {
66 | int client_fd = accept(server_fd, (struct sockaddr *) &client_sockaddr, &client_socklen);
67 |
68 | pid = fork();
69 |
70 | if (pid == 0) {
71 | close(server_fd);
72 |
73 | if (client_fd == -1) {
74 | exit(0);
75 | }
76 |
77 | printf("Connection with `%d` has been established and delegated to the process %d.\nWaiting for a query...\n", client_fd, getpid());
78 |
79 | last_operation = clock();
80 |
81 | while (1) {
82 | read(client_fd, buffer, buffer_len);
83 |
84 | if (buffer == "close") {
85 | printf("Process %d: ", getpid());
86 | close(client_fd);
87 | printf("Closing session with `%d`. Bye!\n", client_fd);
88 | break;
89 | }
90 |
91 | if (strlen(buffer) == 0) {
92 | clock_t d = clock() - last_operation;
93 | double dif = 1.0 * d / CLOCKS_PER_SEC;
94 |
95 | if (dif > 5.0) {
96 | printf("Process %d: ", getpid());
97 | close(client_fd);
98 | printf("Connection timed out after %.3lf seconds. ", dif);
99 | printf("Closing session with `%d`. Bye!\n", client_fd);
100 | break;
101 | }
102 |
103 | continue;
104 | }
105 |
106 | printf("Process %d: ", getpid());
107 | printf("Received `%s`. Processing... ", buffer);
108 |
109 | free(response);
110 | response = process_operation(buffer);
111 | bzero(buffer, buffer_len * sizeof(char));
112 |
113 | send(client_fd, response, strlen(response), 0);
114 | printf("Responded with `%s`. Waiting for a new query...\n", response);
115 |
116 | last_operation = clock();
117 | }
118 | exit(0);
119 | }
120 | else {
121 | close(client_fd);
122 | }
123 | }
124 | }
--------------------------------------------------------------------------------
/server_single.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include
5 | #include
6 |
7 | #include
8 |
9 | int main( int argc, char *argv[] ) {
10 | int sockfd, newsockfd, portno, clilen;
11 | char buffer[256];
12 | struct sockaddr_in serv_addr, cli_addr;
13 | int n;
14 |
15 | /* Initialize socket structure */
16 | bzero((char *) &serv_addr, sizeof(serv_addr));
17 | portno = 5001;
18 |
19 | // create socket and get file descriptor
20 | sockfd = socket(AF_INET, SOCK_STREAM, 0);
21 |
22 | serv_addr.sin_family = AF_INET;
23 | serv_addr.sin_addr.s_addr = INADDR_ANY;
24 | serv_addr.sin_port = htons(portno);
25 |
26 | clilen = sizeof(cli_addr);
27 |
28 | // bind the host address using bind() call
29 | if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){
30 | perror("ERROR on binding\n");
31 | exit(1);
32 | }
33 |
34 | // start listening for the clients,
35 | // here process will go in sleep mode and will wait for the incoming connection
36 | listen(sockfd, 5);
37 |
38 | // accept actual connection from the client
39 | newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
40 |
41 | // inside this while loop, implemented communication with read/write or send/recv function
42 | printf("start");
43 | while (1) {
44 | bzero(buffer,256);
45 | n = read(newsockfd, buffer, 255);
46 |
47 | if (n < 0){
48 | perror("ERROR in reading from socket");
49 | exit(1);
50 | }
51 |
52 | printf("client said: %s \n", buffer);
53 |
54 | n = write(newsockfd, buffer, strlen(buffer));
55 |
56 | if (n < 0){
57 | perror("ERROR in writing to socket");
58 | exit(1);
59 | }
60 |
61 | // escape this loop, if the client sends message "quit"
62 | if (!bcmp(buffer, "quit", 4))
63 | break;
64 | }
65 | return 0;
66 | }
67 |
--------------------------------------------------------------------------------