├── 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 | --------------------------------------------------------------------------------