├── 00_tutorials ├── 00_hello_socket.c ├── 01_create_a_socket.c ├── 02_resolve_hostname.c ├── 03_resolve_port_number.c ├── 04_connect_http_server.c ├── 05_send_first_http_request.c ├── 06_receive_http_response.c ├── 07_http_redirection.c ├── 08_simple_client_server.c ├── 09_multithread_client_server.c ├── 10_peer_to_peer_client_server.c ├── 11_non_blocking_sockets.c ├── 12_multiplexing_select_client_server.c ├── 13_multiplexing_poll_client_server.c ├── 14_broadcasting.c └── CMakeLists.txt ├── 01_networking_libraries ├── libcurl │ ├── CMakeLists.txt │ └── src │ │ ├── basic_curl.cpp │ │ ├── curl_multi_handle.cpp │ │ └── curl_multithreaded.cpp ├── my_http_server │ ├── CMakeLists.txt │ ├── README.md │ ├── defs.h │ ├── http_connection_handler.cpp │ ├── http_connection_handler.h │ ├── http_parser.cpp │ ├── http_parser.h │ ├── http_request.h │ ├── http_response.cpp │ ├── http_response.h │ ├── http_root │ │ ├── 200 │ │ │ └── index.html │ │ ├── 400 │ │ │ └── index.html │ │ ├── 403 │ │ │ └── index.html │ │ ├── 404 │ │ │ └── index.html │ │ ├── 500 │ │ │ └── index.html │ │ └── index.html │ ├── http_router.cpp │ ├── http_router.h │ ├── http_server.cpp │ ├── http_server.h │ ├── http_server_design.png │ ├── logging.h │ ├── main.cpp │ ├── utils.cpp │ └── utils.h └── openssl │ ├── CMakeLists.txt │ ├── certificate │ ├── make_cert.sh │ ├── server.crt │ ├── server.csr │ └── server.key │ └── src │ ├── https_client.c │ └── ssl_client_server.c ├── README.md ├── SSL_client_workflow.png ├── SSL_server_workflow.png ├── how_https_work.png ├── http_connection.png ├── tcp_based_client_server.png └── udp_based_client_server.png /00_tutorials/00_hello_socket.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | printf("%d\n", getprotobyname("tcp")->p_proto); 9 | 10 | return 0; 11 | } -------------------------------------------------------------------------------- /00_tutorials/01_create_a_socket.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | int fd_sock = socket(AF_INET, SOCK_STREAM, getprotobyname("tcp")->p_proto); 9 | if (fd_sock == -1) 10 | { 11 | printf("failed to create a new socket\n"); 12 | } 13 | else 14 | { 15 | printf("closing newly created socket\n"); 16 | close(fd_sock); 17 | } 18 | return 0; 19 | } -------------------------------------------------------------------------------- /00_tutorials/02_resolve_hostname.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | const char* CPP_HOSTNAME = "httpstat.us"; 11 | 12 | struct hostent* host_info = gethostbyname(CPP_HOSTNAME); 13 | if (host_info == NULL) 14 | { 15 | fprintf(stderr, "Error: Could not resolve hostname %s\n", CPP_HOSTNAME); 16 | return -1; 17 | } 18 | 19 | printf("Official name: %s\n", host_info->h_name); 20 | for (char** p_alias = host_info->h_aliases; *p_alias != NULL; p_alias++) 21 | { 22 | printf("Alias name %ld: %s\n", p_alias - host_info->h_aliases, *p_alias); 23 | } 24 | 25 | for (char** p_addr = host_info->h_addr_list; *p_addr != NULL; p_addr++) 26 | { 27 | printf("IP address %ld: %s\n", p_addr - host_info->h_addr_list, inet_ntoa(*(struct in_addr*)*p_addr)); 28 | } 29 | 30 | struct addrinfo hints; 31 | struct addrinfo* res; 32 | memset(&hints, 0, sizeof(hints)); 33 | hints.ai_family = AF_INET; 34 | hints.ai_socktype = SOCK_STREAM; 35 | int rc = getaddrinfo(CPP_HOSTNAME, "80", &hints, &res); 36 | if (rc != 0) 37 | { 38 | fprintf(stderr, "Error: getaddrinfo failed"); 39 | return -1; 40 | } 41 | 42 | struct sockaddr_in* addr = (struct sockaddr_in*)res->ai_addr; 43 | struct sockaddr_in server_address = *addr; 44 | char ip_str[INET_ADDRSTRLEN]; 45 | inet_ntop(AF_INET, &server_address.sin_addr.s_addr, ip_str, sizeof(ip_str)); 46 | printf("IP address: %s\n", ip_str); 47 | printf("Port: %d\n", ntohs(server_address.sin_port)); 48 | 49 | freeaddrinfo(res); 50 | 51 | return 0; 52 | } -------------------------------------------------------------------------------- /00_tutorials/03_resolve_port_number.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() 8 | { 9 | struct servent* service_info; 10 | service_info = getservbyname("http", getprotobyname("tcp")->p_name); 11 | if (service_info == NULL) 12 | { 13 | fprintf(stderr, "Error: Can not resolve service http\n"); 14 | return -1; 15 | } 16 | 17 | printf("Service name: %s\n", service_info->s_name); 18 | printf("Port number: %d\n", ntohs(service_info->s_port)); 19 | printf("Protocol: %s\n", service_info->s_proto); 20 | 21 | return 0; 22 | } -------------------------------------------------------------------------------- /00_tutorials/04_connect_http_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | const char* CPP_HOSTNAME = "httpstat.us"; 9 | 10 | int main() 11 | { 12 | int sock_fd = socket(AF_INET, SOCK_STREAM, getprotobyname("tcp")->p_proto); 13 | if (sock_fd < 0) 14 | { 15 | fprintf(stderr, "Error: socket() return invalid fd\n"); 16 | return -1; 17 | } 18 | 19 | struct addrinfo hints; 20 | struct addrinfo* res; 21 | memset(&hints, 0, sizeof(hints)); 22 | hints.ai_family = AF_INET; 23 | hints.ai_socktype = SOCK_STREAM; 24 | int rc = getaddrinfo(CPP_HOSTNAME, "80", &hints, &res); 25 | if (rc != 0) 26 | { 27 | fprintf(stderr, "Error: getaddrinfo() failed\n"); 28 | return -1; 29 | } 30 | 31 | struct sockaddr server_addr = *(res->ai_addr); 32 | freeaddrinfo(res); 33 | 34 | rc = connect(sock_fd, &server_addr, sizeof(server_addr)); 35 | if (rc != 0) 36 | { 37 | fprintf(stderr, "Error: connect() failed\n"); 38 | return -1; 39 | } 40 | 41 | close(sock_fd); 42 | 43 | return 0; 44 | } -------------------------------------------------------------------------------- /00_tutorials/05_send_first_http_request.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | const char* CPP_HOSTNAME = "httpstat.us"; 10 | 11 | int main() 12 | { 13 | struct protoent* p_proto = getprotobyname("tcp"); 14 | if (p_proto == NULL) 15 | { 16 | fprintf(stderr, "Error: TCP protocol is not available\n"); 17 | return -1; 18 | } 19 | 20 | struct servent* p_service = getservbyname("http", p_proto->p_name); 21 | if (p_service == NULL) 22 | { 23 | fprintf(stderr, "Error: HTTP service is not available\n"); 24 | return -1; 25 | } 26 | char port_str[6] = {'\0', }; 27 | sprintf(port_str, "%d", ntohs(p_service->s_port)); 28 | 29 | struct addrinfo hints; 30 | memset(&hints, 0, sizeof(hints)); 31 | hints.ai_family = AF_INET; 32 | hints.ai_protocol = p_proto->p_proto; 33 | hints.ai_socktype = SOCK_STREAM; 34 | 35 | struct addrinfo* res; 36 | int rc = getaddrinfo(CPP_HOSTNAME, port_str, &hints, &res); 37 | if (rc != 0) 38 | { 39 | fprintf(stderr, "Error: Can not resolve hostname %s\n", CPP_HOSTNAME); 40 | return -1; 41 | } 42 | 43 | struct sockaddr server_addr = *(res->ai_addr); 44 | 45 | int sock_fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 46 | if (sock_fd < 0) 47 | { 48 | fprintf(stderr, "Error: socket() failed\n"); 49 | return -1; 50 | } 51 | 52 | rc = connect(sock_fd, &server_addr, sizeof(server_addr)); 53 | if (rc != 0) 54 | { 55 | fprintf(stderr, "Error: connect() failed\n"); 56 | return -1; 57 | } 58 | 59 | char request[1024] = {'\0', }; 60 | sprintf(request, "GET / HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", CPP_HOSTNAME); 61 | ssize_t request_len = strlen(request); 62 | ssize_t sent_bytes = 0; 63 | while (sent_bytes < request_len) 64 | { 65 | ssize_t sent = send(sock_fd, request, strlen(request), 0); 66 | if (rc < 0) 67 | { 68 | fprintf(stderr, "Error: send() failed\n"); 69 | close(sock_fd); 70 | return -1; 71 | } 72 | 73 | sent_bytes += sent; 74 | printf("sent %d bytes\n", sent_bytes); 75 | } 76 | 77 | close(sock_fd); 78 | freeaddrinfo(res); 79 | 80 | return 0; 81 | } -------------------------------------------------------------------------------- /00_tutorials/06_receive_http_response.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | const char* CPP_HOSTNAME = "httpstat.us"; 10 | const int MESSAGE_SIZE = 1024; 11 | 12 | void on_func_failure(const char* message) 13 | { 14 | fprintf(stderr, "Error: %s\n", message); 15 | exit(EXIT_FAILURE); 16 | } 17 | 18 | int main() 19 | { 20 | struct protoent* p_proto_ent = getprotobyname("tcp"); 21 | if (p_proto_ent == NULL) 22 | { 23 | on_func_failure("TCP protocol is not available"); 24 | } 25 | 26 | struct servent* p_service_ent = getservbyname("http", p_proto_ent->p_name); 27 | if (p_service_ent == NULL) 28 | { 29 | on_func_failure("HTTP service is not available"); 30 | } 31 | 32 | char port_buffer[6]; 33 | memset(port_buffer, 0, sizeof(port_buffer)); 34 | sprintf(port_buffer, "%d", ntohs(p_service_ent->s_port)); 35 | 36 | struct addrinfo hints; 37 | memset(&hints, 0, sizeof(hints)); 38 | hints.ai_family = AF_INET; 39 | hints.ai_protocol = p_proto_ent->p_proto; 40 | hints.ai_socktype = SOCK_STREAM; 41 | 42 | struct addrinfo* server_addr; 43 | int rc = getaddrinfo(CPP_HOSTNAME, port_buffer, &hints, &server_addr); 44 | if (rc != 0) 45 | { 46 | on_func_failure("Failed to resolve hostname"); 47 | } 48 | 49 | int sock_fd = socket(server_addr->ai_family, server_addr->ai_socktype, server_addr->ai_protocol); 50 | if (sock_fd < 0) 51 | { 52 | freeaddrinfo(server_addr); 53 | on_func_failure("socket() failed"); 54 | } 55 | 56 | rc = connect(sock_fd, server_addr->ai_addr, sizeof(struct sockaddr)); 57 | if (rc != 0) 58 | { 59 | freeaddrinfo(server_addr); 60 | on_func_failure("connect() failed"); 61 | } 62 | 63 | char http_request[MESSAGE_SIZE]; 64 | memset(http_request, 0, MESSAGE_SIZE); 65 | sprintf(http_request, "GET /200 HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", CPP_HOSTNAME); 66 | 67 | int http_request_len = strlen(http_request); 68 | int sent_bytes = 0; 69 | while (sent_bytes < http_request_len) 70 | { 71 | int sent_rc = send(sock_fd, http_request + sent_bytes, http_request_len - sent_bytes, 0); 72 | if (sent_rc < 0) 73 | { 74 | close(sock_fd); 75 | freeaddrinfo(server_addr); 76 | on_func_failure("send() failed"); 77 | } 78 | printf("sent %d bytes\n", sent_rc); 79 | sent_bytes += sent_rc; 80 | } 81 | 82 | char http_response[MESSAGE_SIZE]; 83 | memset(http_response, 0, MESSAGE_SIZE); 84 | int received_bytes = 0; 85 | while (1 == 1) 86 | { 87 | int received_rc = recv(sock_fd, http_response + received_bytes, MESSAGE_SIZE - received_bytes, 0); 88 | if (received_rc <= 0) 89 | { 90 | break; 91 | } 92 | 93 | printf("Received %d bytes\n", received_rc); 94 | received_bytes += received_rc; 95 | } 96 | 97 | if (received_bytes <= 0) 98 | { 99 | close(sock_fd); 100 | freeaddrinfo(server_addr); 101 | on_func_failure("recv() failed"); 102 | } 103 | 104 | printf("HTTP Response:\n%s\n", http_response); 105 | 106 | close(sock_fd); 107 | freeaddrinfo(server_addr); 108 | 109 | return 0; 110 | } -------------------------------------------------------------------------------- /00_tutorials/07_http_redirection.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | const char* CPP_HOSTNAME = "httpstat.us"; 10 | const int MESSAGE_SIZE = 1024; 11 | const int MAX_REDIRECTION = 5; 12 | 13 | enum HTTP_RESULT 14 | { 15 | HTTP_UNKNOWN = -1, 16 | HTTP_SUCCESSUL = 200, 17 | HTTP_REDIRECT = 301, 18 | HTTP_NOT_FOUND = 404, 19 | }; 20 | 21 | void report_error(const char* message) 22 | { 23 | fprintf(stderr, "Error: %s\n", message); 24 | } 25 | 26 | int perform_http_request(const char* host_name, const char* path, char* response, int redirect_count) 27 | { 28 | if (redirect_count > MAX_REDIRECTION) 29 | { 30 | return HTTP_NOT_FOUND; 31 | } 32 | 33 | struct protoent* http_proto = getprotobyname("tcp"); 34 | if (http_proto == NULL) 35 | { 36 | report_error("TCP protocol is not supported"); 37 | return HTTP_UNKNOWN; 38 | } 39 | 40 | struct servent* http_service = getservbyname("http", http_proto->p_name); 41 | if (http_service == NULL) 42 | { 43 | report_error("HTTP service is not available"); 44 | return HTTP_UNKNOWN; 45 | } 46 | 47 | char http_port[6]; 48 | memset(http_port, 0, 6); 49 | sprintf(http_port, "%d", ntohs(http_service->s_port)); 50 | 51 | struct addrinfo hints; 52 | memset(&hints, 0, sizeof(hints)); 53 | hints.ai_family = AF_INET; 54 | hints.ai_socktype = SOCK_STREAM; 55 | hints.ai_protocol = http_proto->p_proto; 56 | struct addrinfo* http_addr; 57 | int addr_rc = getaddrinfo(host_name, http_port, &hints, &http_addr); 58 | if (addr_rc != 0) 59 | { 60 | char http_temp[MESSAGE_SIZE]; 61 | memset(http_temp, 0, MESSAGE_SIZE); 62 | sprintf(http_temp, "Failed to resolve hostname: %s", host_name); 63 | report_error(http_temp); 64 | return HTTP_UNKNOWN; 65 | } 66 | 67 | int http_sock = socket(http_addr->ai_family, http_addr->ai_socktype, http_addr->ai_protocol); 68 | if (http_sock < 0) 69 | { 70 | report_error("socket() failed"); 71 | freeaddrinfo(http_addr); 72 | return HTTP_UNKNOWN; 73 | } 74 | 75 | int con_rc = connect(http_sock, http_addr->ai_addr, sizeof(struct sockaddr)); 76 | if (con_rc != 0) 77 | { 78 | report_error("connect() failed"); 79 | freeaddrinfo(http_addr); 80 | close(http_sock); 81 | return HTTP_UNKNOWN; 82 | } 83 | 84 | char http_request[MESSAGE_SIZE]; 85 | memset(http_request, 0, MESSAGE_SIZE); 86 | sprintf(http_request, "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", path, host_name); 87 | printf("HTTP request:\n%s\n", http_request); 88 | 89 | int http_request_len = strlen(http_request); 90 | int http_sent_bytes = 0; 91 | while (http_sent_bytes < http_request_len) 92 | { 93 | int sent_bytes = send(http_sock, http_request + http_sent_bytes, http_request_len - http_sent_bytes, 0); 94 | if (sent_bytes <= 0) 95 | { 96 | break; 97 | } 98 | printf("http sent %d bytes\n", sent_bytes); 99 | http_sent_bytes += sent_bytes; 100 | } 101 | 102 | if (http_sent_bytes < http_request_len) 103 | { 104 | report_error("Can not send a complete http request"); 105 | freeaddrinfo(http_addr); 106 | close(http_sock); 107 | return HTTP_UNKNOWN; 108 | } 109 | 110 | char http_response[MESSAGE_SIZE]; 111 | memset(http_response, 0, MESSAGE_SIZE); 112 | int http_received_bytes = 0; 113 | int received_bytes; 114 | while ((received_bytes = recv(http_sock, http_response + http_received_bytes, MESSAGE_SIZE - http_received_bytes, 0)) > 0) 115 | { 116 | http_received_bytes += received_bytes; 117 | } 118 | 119 | printf("HTTP received %d bytes\n\n", http_received_bytes); 120 | 121 | if (response != NULL) 122 | { 123 | memcpy(response, http_response, http_received_bytes); 124 | } 125 | 126 | if (strstr(http_response, "301 Moved Permanently") != NULL) 127 | { 128 | printf("HTTP is redirected\n\n"); 129 | char* location_start = strstr(http_response, "Location: "); 130 | if (location_start != NULL) 131 | { 132 | location_start += 10; 133 | char* location_end = strstr(location_start, "\r\n"); 134 | if (location_end != NULL) 135 | { 136 | int new_http_host_len = location_end - location_start; 137 | char new_http_host[MESSAGE_SIZE]; 138 | strncpy(new_http_host, location_start, new_http_host_len); 139 | new_http_host[new_http_host_len] = '\0'; 140 | return perform_http_request(new_http_host, "/", response, redirect_count + 1); 141 | } 142 | } 143 | } 144 | 145 | freeaddrinfo(http_addr); 146 | close(http_sock); 147 | return HTTP_SUCCESSUL; 148 | } 149 | 150 | int main() 151 | { 152 | char http_response[MESSAGE_SIZE]; 153 | memset(http_response, 0, MESSAGE_SIZE); 154 | 155 | (void)perform_http_request(CPP_HOSTNAME, "/200", http_response, 0); 156 | printf("%s\n", http_response); 157 | 158 | (void)perform_http_request(CPP_HOSTNAME, "/301", http_response, 0); 159 | printf("%s\n", http_response); 160 | 161 | (void)perform_http_request(CPP_HOSTNAME, "/403", http_response, 0); 162 | printf("%s\n", http_response); 163 | 164 | (void)perform_http_request(CPP_HOSTNAME, "/404", http_response, 0); 165 | printf("%s\n", http_response); 166 | 167 | return 0; 168 | } -------------------------------------------------------------------------------- /00_tutorials/08_simple_client_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define PROTOCOL "tcp" 12 | #define TCP_PORT 45123 13 | #define MESSAGE_SIZE 1024 14 | #define HOST_NAME "localhost" 15 | 16 | void print_usage(const char *program_name) 17 | { 18 | fprintf(stderr, "Usage: %s \n", program_name); 19 | } 20 | 21 | void report_error(const char* message) 22 | { 23 | fprintf(stderr, "Error: %s\n", message); 24 | } 25 | 26 | void run_server() 27 | { 28 | int rc; 29 | 30 | struct protoent* tcp_proto = getprotobyname(PROTOCOL); 31 | if (tcp_proto == NULL) 32 | { 33 | report_error("TCP protocol is not supported"); 34 | return; 35 | } 36 | 37 | char server_port[6]; 38 | memset(server_port, 0, 6); 39 | sprintf(server_port, "%d", htons(TCP_PORT)); 40 | 41 | struct addrinfo addr_hints; 42 | memset(&addr_hints, 0, sizeof(addr_hints)); 43 | addr_hints.ai_family = AF_INET; 44 | addr_hints.ai_socktype = SOCK_STREAM; 45 | addr_hints.ai_protocol = tcp_proto->p_proto; 46 | struct addrinfo* addr_server; 47 | rc = getaddrinfo(NULL, server_port, &addr_hints, &addr_server); 48 | if (rc != 0) 49 | { 50 | report_error("Can not resolve server hostname"); 51 | return; 52 | } 53 | 54 | int sock_server = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 55 | if (sock_server < 0) 56 | { 57 | report_error("Server socket() failed"); 58 | freeaddrinfo(addr_server); 59 | return; 60 | } 61 | 62 | int sock_server_opt = 1; 63 | rc = setsockopt(sock_server, SOL_SOCKET, SO_REUSEADDR | SO_KEEPALIVE, &sock_server_opt, sizeof(sock_server_opt)); 64 | if (rc < 0) 65 | { 66 | report_error("Server setsockopt() failed"); 67 | } 68 | 69 | for (struct addrinfo* p_server = addr_server; p_server != NULL; p_server = p_server->ai_next) 70 | { 71 | rc = bind(sock_server, p_server->ai_addr, p_server->ai_addrlen); 72 | if (rc == 0) 73 | { 74 | break; 75 | } 76 | } 77 | 78 | if (rc != 0) 79 | { 80 | report_error("Server bind() failed"); 81 | close(sock_server); 82 | freeaddrinfo(addr_server); 83 | return; 84 | } 85 | 86 | rc = listen(sock_server, 3); 87 | if (rc < 0) 88 | { 89 | report_error("Server listen() failed"); 90 | close(sock_server); 91 | freeaddrinfo(addr_server); 92 | return; 93 | } 94 | 95 | // server loop 96 | int sock_client = -1; 97 | while (1) 98 | { 99 | printf("Server is ready to process new request\n"); 100 | 101 | struct sockaddr addr_client; 102 | socklen_t addr_len = sizeof(addr_client); 103 | sock_client = accept(sock_server, (struct sockaddr*)&addr_client, &addr_len); 104 | if (sock_client < 0) 105 | { 106 | report_error("Server accept() failed"); 107 | continue; 108 | } 109 | 110 | char client_host[NI_MAXHOST]; 111 | char client_service[NI_MAXSERV]; 112 | rc = getnameinfo(&addr_client, addr_len, client_host, sizeof(client_host), client_service, sizeof(client_service), NI_NUMERICHOST | NI_NUMERICSERV); 113 | if (rc == 0) 114 | { 115 | printf("Server accepted client connection %s:%s\n", client_host, client_service); 116 | } 117 | 118 | char request_buffer[MESSAGE_SIZE]; 119 | char response_buffer[MESSAGE_SIZE]; 120 | while (1) 121 | { 122 | memset(request_buffer, 0, MESSAGE_SIZE); 123 | memset(response_buffer, 0, MESSAGE_SIZE); 124 | 125 | int received_bytes = recv(sock_client, request_buffer, MESSAGE_SIZE, 0); 126 | if (received_bytes <= 0) 127 | { 128 | report_error("Client is disconnected\n"); 129 | break; 130 | } 131 | request_buffer[received_bytes] = '\0'; 132 | 133 | printf("Received client request: %s\n", request_buffer); 134 | 135 | if (strcmp(request_buffer, "exit") == 0 136 | || strcmp(request_buffer, "quit") == 0 137 | || strcmp(request_buffer, "shutdown") == 0) 138 | { 139 | sprintf(response_buffer, "OK"); 140 | rc = send(sock_client, response_buffer, MESSAGE_SIZE, 0); 141 | close(sock_client); 142 | break; 143 | } 144 | else if (strcmp(request_buffer, "time") == 0) 145 | { 146 | sprintf(response_buffer, "%d", time(NULL)); 147 | rc = send(sock_client, response_buffer, MESSAGE_SIZE, 0); 148 | } 149 | else 150 | { 151 | sprintf(response_buffer, "Unknown request"); 152 | rc = send(sock_client, response_buffer, MESSAGE_SIZE, 0); 153 | } 154 | 155 | if (rc <= 0) 156 | { 157 | report_error("Server send() failed"); 158 | } 159 | } 160 | } 161 | 162 | close(sock_server); 163 | freeaddrinfo(addr_server); 164 | printf("Server exit with normal status\n"); 165 | } 166 | 167 | void run_client() 168 | { 169 | int rc; 170 | 171 | struct protoent* tcp_proto = getprotobyname(PROTOCOL); 172 | if (tcp_proto == NULL) 173 | { 174 | report_error("TCP protocol is not supported"); 175 | return; 176 | } 177 | 178 | char server_port[6]; 179 | memset(server_port, 0, 6); 180 | sprintf(server_port, "%d", htons(TCP_PORT)); 181 | 182 | struct addrinfo addr_hints; 183 | memset(&addr_hints, 0, sizeof(addr_hints)); 184 | addr_hints.ai_family = AF_INET; 185 | addr_hints.ai_socktype = SOCK_STREAM; 186 | addr_hints.ai_protocol = tcp_proto->p_proto; 187 | struct addrinfo* addr_server; 188 | rc = getaddrinfo(HOST_NAME, server_port, &addr_hints, &addr_server); 189 | if (rc != 0) 190 | { 191 | report_error("Failed to resolve hostname"); 192 | return; 193 | } 194 | 195 | int sock_client = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 196 | if (sock_client < 0) 197 | { 198 | report_error("client socket() failed"); 199 | freeaddrinfo(addr_server); 200 | return; 201 | } 202 | 203 | for (struct addrinfo* p_server = addr_server; p_server != NULL; p_server = p_server->ai_next) 204 | { 205 | rc = connect(sock_client, p_server->ai_addr, p_server->ai_addrlen); 206 | if (rc == 0) 207 | { 208 | break; 209 | } 210 | } 211 | 212 | if (rc != 0) 213 | { 214 | report_error("client connect() failed"); 215 | close(sock_client); 216 | freeaddrinfo(addr_server); 217 | return; 218 | } 219 | 220 | // client loop 221 | while (1) 222 | { 223 | printf("Client is ready to create new request\n"); 224 | char request_buffer[MESSAGE_SIZE]; 225 | memset(request_buffer, 0, MESSAGE_SIZE); 226 | 227 | printf("Enter command: "); 228 | fgets(request_buffer, MESSAGE_SIZE, stdin); 229 | printf("Request: %s\n", request_buffer); 230 | 231 | // Remove newline character from the request buffer 232 | request_buffer[strcspn(request_buffer, "\n")] = 0; 233 | 234 | int request_buffer_len = strlen(request_buffer); 235 | int sent_bytes = send(sock_client, request_buffer, request_buffer_len, 0); 236 | if (sent_bytes <= 0) 237 | { 238 | report_error("Client sent request fail"); 239 | continue; 240 | } 241 | 242 | char response_buffer[MESSAGE_SIZE]; 243 | memset(response_buffer, 0, MESSAGE_SIZE); 244 | int received_bytes = recv(sock_client, response_buffer, MESSAGE_SIZE, 0); 245 | if (received_bytes <= 0) 246 | { 247 | report_error("Client recv() failed"); 248 | } 249 | else 250 | { 251 | printf("Response: %s\n", response_buffer); 252 | } 253 | 254 | if (strcmp(request_buffer, "exit") == 0 255 | || strcmp(request_buffer, "quit") == 0 256 | || strcmp(request_buffer, "shutdown") == 0) 257 | { 258 | break; 259 | } 260 | } 261 | 262 | close(sock_client); 263 | freeaddrinfo(addr_server); 264 | printf("Client exit with normal status\n"); 265 | } 266 | 267 | int main(int argc, char** argv) 268 | { 269 | if (argc != 2) 270 | { 271 | print_usage(argv[0]); 272 | return -1; 273 | } 274 | 275 | if (strcmp(argv[1], "server") == 0) 276 | { 277 | run_server(); 278 | } 279 | else if (strcmp(argv[1], "client") == 0) 280 | { 281 | run_client(); 282 | } 283 | else 284 | { 285 | print_usage(argv[0]); 286 | return -1; 287 | } 288 | 289 | return 0; 290 | } -------------------------------------------------------------------------------- /00_tutorials/09_multithread_client_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #define PROTOCOL "tcp" 15 | #define TCP_PORT 45123 16 | #define MESSAGE_SIZE 1024 17 | #define HOST_NAME "localhost" 18 | #define MAX_CONNECTION 100 19 | 20 | void print_usage(const char *program_name) 21 | { 22 | fprintf(stderr, "Usage: %s \n", program_name); 23 | } 24 | 25 | void report_error(const char* message) 26 | { 27 | fprintf(stderr, "Error: %s\n", message); 28 | } 29 | 30 | void* server_handle_client(void* arg) 31 | { 32 | int* sock_client = ((int*)arg); 33 | if (sock_client == NULL) 34 | { 35 | return NULL; 36 | } 37 | 38 | int rc; 39 | char request_buffer[MESSAGE_SIZE]; 40 | char response_buffer[MESSAGE_SIZE]; 41 | while (1) 42 | { 43 | memset(request_buffer, 0, MESSAGE_SIZE); 44 | memset(response_buffer, 0, MESSAGE_SIZE); 45 | 46 | int received_bytes = recv(*sock_client, request_buffer, MESSAGE_SIZE, 0); 47 | if (received_bytes <= 0) 48 | { 49 | report_error("Client is disconnected"); 50 | break; 51 | } 52 | request_buffer[received_bytes] = '\0'; 53 | 54 | printf("Client %d requests: %s\n", *sock_client, request_buffer); 55 | 56 | if (strcmp(request_buffer, "exit") == 0 57 | || strcmp(request_buffer, "quit") == 0 58 | || strcmp(request_buffer, "shutdown") == 0) 59 | { 60 | sprintf(response_buffer, "OK"); 61 | rc = send(*sock_client, response_buffer, MESSAGE_SIZE, 0); 62 | close(*sock_client); 63 | break; 64 | } 65 | else if (strcmp(request_buffer, "time") == 0) 66 | { 67 | sprintf(response_buffer, "%d", time(NULL)); 68 | rc = send(*sock_client, response_buffer, MESSAGE_SIZE, 0); 69 | } 70 | else 71 | { 72 | sprintf(response_buffer, "Unknown request"); 73 | rc = send(*sock_client, response_buffer, MESSAGE_SIZE, 0); 74 | } 75 | 76 | if (rc <= 0) 77 | { 78 | report_error("Server send() failed"); 79 | } 80 | } 81 | 82 | if (sock_client != NULL) 83 | { 84 | free(sock_client); 85 | } 86 | 87 | return NULL; 88 | } 89 | 90 | void run_server() 91 | { 92 | int rc; 93 | 94 | struct protoent* tcp_proto = getprotobyname(PROTOCOL); 95 | if (tcp_proto == NULL) 96 | { 97 | report_error("TCP protocol is not supported"); 98 | return; 99 | } 100 | 101 | char server_port[6]; 102 | memset(server_port, 0, 6); 103 | sprintf(server_port, "%d", htons(TCP_PORT)); 104 | 105 | struct addrinfo hints; 106 | memset(&hints, 0, sizeof(hints)); 107 | hints.ai_family = AF_INET; 108 | hints.ai_socktype = SOCK_STREAM; 109 | hints.ai_protocol = tcp_proto->p_proto; 110 | struct addrinfo* addr_server; 111 | rc = getaddrinfo(NULL, server_port, &hints, &addr_server); 112 | if (rc != 0) 113 | { 114 | report_error("Can not resolve server hostname"); 115 | return; 116 | } 117 | 118 | int sock_server = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 119 | if (sock_server < 0) 120 | { 121 | report_error("Server socket() failed"); 122 | freeaddrinfo(addr_server); 123 | return; 124 | } 125 | 126 | int sock_server_opt = 1; 127 | rc = setsockopt(sock_server, SOL_SOCKET, SO_REUSEADDR | SO_KEEPALIVE, &sock_server_opt, sizeof(sock_server_opt)); 128 | if (rc < 0) 129 | { 130 | report_error("Server setsockopt() failed"); 131 | } 132 | 133 | for (struct addrinfo* p_server = addr_server; p_server != NULL; p_server = p_server->ai_next) 134 | { 135 | rc = bind(sock_server, p_server->ai_addr, p_server->ai_addrlen); 136 | if (rc == 0) 137 | { 138 | break; 139 | } 140 | } 141 | 142 | if (rc != 0) 143 | { 144 | report_error("Server bind() failed"); 145 | freeaddrinfo(addr_server); 146 | close(sock_server); 147 | return; 148 | } 149 | 150 | rc = listen(sock_server, MAX_CONNECTION); 151 | if (rc < 0) 152 | { 153 | report_error("Server listen() failed"); 154 | freeaddrinfo(addr_server); 155 | close(sock_server); 156 | return; 157 | } 158 | 159 | // server loop 160 | while (1) 161 | { 162 | printf("Server is ready to process new request\n"); 163 | 164 | struct sockaddr addr_client; 165 | socklen_t addr_client_len = sizeof(addr_client); 166 | int sock_client = accept(sock_server, &addr_client, &addr_client_len); 167 | if (sock_client < 0) 168 | { 169 | report_error("Server accept() failed"); 170 | continue; 171 | } 172 | 173 | char ip_client[NI_MAXHOST]; 174 | char service_client[NI_MAXSERV]; 175 | rc = getnameinfo(&addr_client, addr_client_len, ip_client, sizeof(ip_client), service_client, sizeof(service_client), NI_NUMERICHOST | NI_NUMERICSERV); 176 | if (rc == 0) 177 | { 178 | printf("Server accepted client connection %s:%s\n", ip_client, service_client); 179 | } 180 | 181 | int* p_sock_client = (int*)calloc(1, sizeof(int)); 182 | *p_sock_client = sock_client; 183 | pthread_t client_thread; 184 | rc = pthread_create(&client_thread, NULL, server_handle_client, p_sock_client); 185 | if (rc != 0) 186 | { 187 | report_error("Server create thread for new client failed"); 188 | continue; 189 | } 190 | 191 | rc = pthread_detach(client_thread); 192 | if (rc != 0) 193 | { 194 | report_error("Detach client thread failed"); 195 | } 196 | } 197 | 198 | freeaddrinfo(addr_server); 199 | close(sock_server); 200 | } 201 | 202 | void run_client() 203 | { 204 | int rc; 205 | 206 | struct protoent* tcp_proto = getprotobyname(PROTOCOL); 207 | if (tcp_proto == NULL) 208 | { 209 | report_error("TCP protocol is not available"); 210 | return; 211 | } 212 | 213 | char server_port[6]; 214 | memset(server_port, 0, 6); 215 | sprintf(server_port, "%d", htons(TCP_PORT)); 216 | 217 | struct addrinfo hints; 218 | memset(&hints, 0, sizeof(hints)); 219 | hints.ai_family = AF_INET; 220 | hints.ai_socktype = SOCK_STREAM; 221 | hints.ai_protocol = tcp_proto->p_proto; 222 | struct addrinfo* addr_server; 223 | rc = getaddrinfo(HOST_NAME, server_port, &hints, &addr_server); 224 | if (rc != 0) 225 | { 226 | report_error("Can not resolve hostname"); 227 | return; 228 | } 229 | 230 | int sock_client = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 231 | if (sock_client < 0) 232 | { 233 | report_error("Client socket() failed"); 234 | freeaddrinfo(addr_server); 235 | return; 236 | } 237 | 238 | for (struct addrinfo* p_server = addr_server; p_server != NULL; p_server = p_server->ai_next) 239 | { 240 | rc = connect(sock_client, p_server->ai_addr, p_server->ai_addrlen); 241 | if (rc == 0) 242 | { 243 | break; 244 | } 245 | } 246 | 247 | if (rc != 0) 248 | { 249 | report_error("Client connect() failed"); 250 | freeaddrinfo(addr_server); 251 | close(sock_client); 252 | return; 253 | } 254 | 255 | // client loop 256 | while (1) 257 | { 258 | printf("Client is ready to reate a new request\n"); 259 | char request_buffer[MESSAGE_SIZE]; 260 | memset(request_buffer, 0, MESSAGE_SIZE); 261 | 262 | printf("Enter command: "); 263 | fgets(request_buffer, MESSAGE_SIZE, stdin); 264 | request_buffer[strcspn(request_buffer, "\r\n")] = 0; 265 | 266 | printf("Request: %s\n", request_buffer); 267 | 268 | int request_buffer_len = strlen(request_buffer); 269 | int sent_bytes = send(sock_client, request_buffer, request_buffer_len, 0); 270 | if (sent_bytes != request_buffer_len) 271 | { 272 | report_error("Client sent request fail"); 273 | continue; 274 | } 275 | 276 | char response_buffer[MESSAGE_SIZE]; 277 | memset(response_buffer, 0, MESSAGE_SIZE); 278 | int received_bytes = recv(sock_client, response_buffer, MESSAGE_SIZE, 0); 279 | if (received_bytes <= 0) 280 | { 281 | report_error("Client recv() failed"); 282 | } 283 | else 284 | { 285 | printf("Response: %s\n", response_buffer); 286 | } 287 | 288 | if (strcmp(request_buffer, "exit") == 0 289 | || strcmp(request_buffer, "quit") == 0 290 | || strcmp(request_buffer, "shutdown") == 0) 291 | { 292 | break; 293 | } 294 | } 295 | 296 | freeaddrinfo(addr_server); 297 | close(sock_client); 298 | 299 | printf("Client exit with normal status\n"); 300 | } 301 | 302 | int main(int argc, char** argv) 303 | { 304 | if (argc != 2) 305 | { 306 | print_usage(argv[0]); 307 | return -1; 308 | } 309 | 310 | if (strcmp(argv[1], "server") == 0) 311 | { 312 | run_server(); 313 | } 314 | else if (strcmp(argv[1], "client") == 0) 315 | { 316 | run_client(); 317 | } 318 | else 319 | { 320 | print_usage(argv[0]); 321 | return -1; 322 | } 323 | 324 | return 0; 325 | } -------------------------------------------------------------------------------- /00_tutorials/10_peer_to_peer_client_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #define PROTOCOL "udp" 15 | #define UDP_PORT 45123 16 | #define MESSAGE_SIZE 1024 17 | #define HOST_NAME "localhost" 18 | 19 | void print_usage(const char *program_name) 20 | { 21 | fprintf(stderr, "Usage: %s \n", program_name); 22 | } 23 | 24 | void report_error(const char* message) 25 | { 26 | fprintf(stderr, "Error: %s\n", message); 27 | } 28 | 29 | void run_server() 30 | { 31 | int rc; 32 | 33 | struct protoent* udp_protocol = getprotobyname(PROTOCOL); 34 | if (udp_protocol == NULL) 35 | { 36 | report_error("UDP protocol is not supported"); 37 | return; 38 | } 39 | 40 | char port_server[6]; 41 | memset(port_server, 0, 6); 42 | sprintf(port_server, "%d", htons(UDP_PORT)); 43 | 44 | struct addrinfo hints; 45 | memset(&hints, 0, sizeof(hints)); 46 | hints.ai_family = AF_INET; 47 | hints.ai_socktype = SOCK_DGRAM; 48 | hints.ai_protocol = udp_protocol->p_proto; 49 | struct addrinfo* addr_server; 50 | rc = getaddrinfo(NULL, port_server, &hints, &addr_server); // INADDR_ANY 51 | if (rc != 0) 52 | { 53 | report_error("Server can not resolve hostname"); 54 | return; 55 | } 56 | 57 | int sock_server = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 58 | if (sock_server < 0) 59 | { 60 | report_error("Server socket() failed"); 61 | freeaddrinfo(addr_server); 62 | return; 63 | } 64 | 65 | for (struct addrinfo* p_server = addr_server; p_server != NULL; p_server = p_server->ai_next) 66 | { 67 | rc = bind(sock_server, p_server->ai_addr, p_server->ai_addrlen); 68 | if (rc == 0) 69 | { 70 | break; 71 | } 72 | } 73 | 74 | if (rc != 0) 75 | { 76 | report_error("Server bind() failed"); 77 | freeaddrinfo(addr_server); 78 | close(sock_server); 79 | return; 80 | } 81 | 82 | printf("Server is ready to receive client requests\n"); 83 | 84 | char request_buffer[MESSAGE_SIZE]; 85 | char response_buffer[MESSAGE_SIZE]; 86 | while (1) 87 | { 88 | memset(request_buffer, 0, MESSAGE_SIZE); 89 | memset(response_buffer, 0, MESSAGE_SIZE); 90 | 91 | struct sockaddr addr_client; 92 | socklen_t addr_client_len = sizeof(struct sockaddr); 93 | int received_bytes = recvfrom(sock_server, request_buffer, MESSAGE_SIZE, 0, &addr_client, &addr_client_len); 94 | if (received_bytes <= 0) 95 | { 96 | report_error("Server recvfrom() failed"); 97 | continue; 98 | } 99 | 100 | request_buffer[received_bytes] = '\0'; 101 | 102 | char ip_client[NI_MAXHOST]; 103 | char serv_client[NI_MAXSERV]; 104 | rc = getnameinfo(&addr_client, addr_client_len, ip_client, sizeof(ip_client), serv_client, sizeof(serv_client), NI_NUMERICHOST | NI_NUMERICSERV); 105 | if (rc == 0) 106 | { 107 | printf("Server received %s:%s client request: %s\n", ip_client, serv_client, request_buffer); 108 | } 109 | else 110 | { 111 | printf("Server received unknown client request: %s\n", request_buffer); 112 | } 113 | 114 | sprintf(response_buffer, "Server received request at %d", time(NULL)); 115 | int response_buffer_len = strlen(response_buffer); 116 | rc = sendto(sock_server, response_buffer, response_buffer_len, 0, &addr_client, addr_client_len); 117 | if (rc != response_buffer_len) 118 | { 119 | report_error("Server sendto() failed"); 120 | } 121 | } 122 | 123 | freeaddrinfo(addr_server); 124 | close(sock_server); 125 | 126 | printf("Server exit with normal status\n"); 127 | } 128 | 129 | void run_client() 130 | { 131 | int rc; 132 | 133 | struct protoent* udp_protocol = getprotobyname(PROTOCOL); 134 | if (udp_protocol == NULL) 135 | { 136 | report_error("UDP protocol is not supported"); 137 | return; 138 | } 139 | 140 | char port_server[6]; 141 | memset(port_server, 0, 6); 142 | sprintf(port_server, "%d", htons(UDP_PORT)); 143 | 144 | struct addrinfo hints; 145 | memset(&hints, 0, sizeof(hints)); 146 | hints.ai_family = AF_INET; 147 | hints.ai_socktype = SOCK_DGRAM; 148 | hints.ai_protocol = udp_protocol->p_proto; 149 | struct addrinfo* addr_server; 150 | rc = getaddrinfo(HOST_NAME, port_server, &hints, &addr_server); 151 | if (rc != 0) 152 | { 153 | report_error("Client can not resolve hostname"); 154 | return; 155 | } 156 | 157 | int sock_client = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 158 | if (sock_client < 0) 159 | { 160 | report_error("Client socket() failed"); 161 | freeaddrinfo(addr_server); 162 | return; 163 | } 164 | 165 | // Using sendto() function allows us to specify the destination address and port for each sending packet, no need prior connect() call. 166 | // Using connect() with UDP socket is still useful sometimes, it is possible to use send() and recv() functions instead of sendto() and recvfrom() functions. 167 | // for (addrinfo* p_server = addr_server; p_server != NULL; p_server = p_server->ai_next) 168 | // { 169 | // rc = connect(sock_client, p_server->ai_addr, p_server->ai_addrlen); 170 | // if (rc == 0) 171 | // { 172 | // break; 173 | // } 174 | // } 175 | 176 | // if (rc != 0) 177 | // { 178 | // report_error("Client connect() failed"); 179 | // return; 180 | // } 181 | 182 | printf("Client is ready to send requests\n"); 183 | 184 | char request_buffer[MESSAGE_SIZE]; 185 | char response_buffer[MESSAGE_SIZE]; 186 | while (1) 187 | { 188 | memset(request_buffer, 0, MESSAGE_SIZE); 189 | memset(response_buffer, 0, MESSAGE_SIZE); 190 | 191 | printf("Enter command: "); 192 | fgets(request_buffer, MESSAGE_SIZE, stdin); 193 | request_buffer[strcspn(request_buffer, "\r\n")] = '\0'; 194 | 195 | int request_buffer_len = strlen(request_buffer); 196 | rc = sendto(sock_client, request_buffer, request_buffer_len, 0, addr_server->ai_addr, addr_server->ai_addrlen); 197 | if (rc != request_buffer_len) 198 | { 199 | report_error("Client sendto() failed"); 200 | continue; 201 | } 202 | 203 | int received_bytes = recvfrom(sock_client, response_buffer, MESSAGE_SIZE, 0, addr_server->ai_addr, &addr_server->ai_addrlen); 204 | if (received_bytes <= 0) 205 | { 206 | report_error("Client recvfrom() failed"); 207 | continue; 208 | } 209 | response_buffer[received_bytes] = '\0'; 210 | printf("Client received response: %s\n", response_buffer); 211 | 212 | if (strcmp(request_buffer, "exit") == 0 213 | || strcmp(request_buffer, "quit") == 0 214 | || strcmp(request_buffer, "shutdown") == 0) 215 | { 216 | break; 217 | } 218 | } 219 | 220 | freeaddrinfo(addr_server); 221 | close(sock_client); 222 | printf("Client exit with normal status\n"); 223 | } 224 | 225 | int main(int argc, char** argv) 226 | { 227 | if (argc != 2) 228 | { 229 | print_usage(argv[0]); 230 | return -1; 231 | } 232 | 233 | if (strcmp(argv[1], "server") == 0) 234 | { 235 | run_server(); 236 | } 237 | else if (strcmp(argv[1], "client") == 0) 238 | { 239 | run_client(); 240 | } 241 | else 242 | { 243 | print_usage(argv[0]); 244 | return -1; 245 | } 246 | 247 | return 0; 248 | } -------------------------------------------------------------------------------- /00_tutorials/11_non_blocking_sockets.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define PROTOCOL "tcp" 15 | #define TCP_PORT 45123 16 | #define MESSAGE_SIZE 1024 17 | #define HOST_NAME "localhost" 18 | #define MAX_CONNECTION 100 19 | 20 | void print_usage(const char *program_name) 21 | { 22 | fprintf(stderr, "Usage: %s \n", program_name); 23 | } 24 | 25 | void report_error(const char* message) 26 | { 27 | fprintf(stderr, "Error: %s\n", message); 28 | } 29 | 30 | void set_non_blocking(int socket) 31 | { 32 | int flags = fcntl(socket, F_GETFL, 0); 33 | if (flags == -1) 34 | { 35 | report_error("fcntl(F_GETFL) failed"); 36 | return; 37 | } 38 | 39 | if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) 40 | { 41 | report_error("fcntl(F_SETFL) failed"); 42 | } 43 | } 44 | 45 | void run_server() 46 | { 47 | int rc; 48 | 49 | struct protoent* tcp_proto = getprotobyname(PROTOCOL); 50 | if (tcp_proto == NULL) 51 | { 52 | report_error("TCP protocol is not supported"); 53 | return; 54 | } 55 | 56 | char port_server[6]; 57 | memset(port_server, 0, 6); 58 | sprintf(port_server, "%d", htons(TCP_PORT)); 59 | 60 | struct addrinfo hints; 61 | memset(&hints, 0, sizeof(hints)); 62 | hints.ai_family = AF_INET; 63 | hints.ai_socktype = SOCK_STREAM; 64 | hints.ai_protocol = tcp_proto->p_proto; 65 | hints.ai_flags = AI_PASSIVE; 66 | struct addrinfo* addr_server; 67 | rc = getaddrinfo(HOST_NAME, port_server, &hints, &addr_server); 68 | if (rc != 0) 69 | { 70 | report_error("Server getaddrinfo() failed"); 71 | return; 72 | } 73 | 74 | int sock_server = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 75 | if (sock_server < 0) 76 | { 77 | report_error("Server socket() failed"); 78 | freeaddrinfo(addr_server); 79 | return; 80 | } 81 | 82 | set_non_blocking(sock_server); 83 | 84 | for (struct addrinfo* p_server = addr_server; p_server != NULL; p_server = p_server->ai_next) 85 | { 86 | rc = bind(sock_server, p_server->ai_addr, p_server->ai_addrlen); 87 | if (rc == 0) 88 | { 89 | break; 90 | } 91 | } 92 | 93 | if (rc != 0) 94 | { 95 | report_error("Server bind() failed"); 96 | freeaddrinfo(addr_server); 97 | close(sock_server); 98 | return; 99 | } 100 | 101 | rc = listen(sock_server, MAX_CONNECTION); 102 | if (rc != 0) 103 | { 104 | report_error("Server listen() failed"); 105 | freeaddrinfo(addr_server); 106 | close(sock_server); 107 | return; 108 | } 109 | 110 | // Server Loop 111 | int sock_client = -1; 112 | char buffer[MESSAGE_SIZE]; 113 | while (1) 114 | { 115 | struct sockaddr addr_client; 116 | socklen_t addr_client_len = sizeof(struct sockaddr); 117 | if (sock_client < 0) 118 | { 119 | sock_client = accept(sock_server, &addr_client, &addr_client_len); 120 | if (sock_client < 0) 121 | { 122 | if (errno != EAGAIN && errno != EWOULDBLOCK) 123 | { 124 | report_error("Server accept() failed"); 125 | break; 126 | } 127 | else 128 | { 129 | printf("No client connection\n"); 130 | } 131 | } 132 | else 133 | { 134 | char ip_client[NI_MAXHOST]; 135 | char service_client[NI_MAXSERV]; 136 | rc = getnameinfo(&addr_client, addr_client_len, ip_client, sizeof(ip_client), service_client, sizeof(service_client), NI_NUMERICHOST | NI_NUMERICSERV); 137 | if (rc == 0) 138 | { 139 | printf("Server accepted client connection %s:%s\n", ip_client, service_client); 140 | } 141 | 142 | set_non_blocking(sock_client); 143 | } 144 | } 145 | 146 | if (sock_client > 0) 147 | { 148 | memset(buffer, 0, MESSAGE_SIZE); 149 | int received_bytes = recv(sock_client, buffer, MESSAGE_SIZE, 0); 150 | if (received_bytes < 0) 151 | { 152 | if (errno != EAGAIN && errno != EWOULDBLOCK) 153 | { 154 | report_error("Server recv() failed"); 155 | break; 156 | } 157 | } 158 | else if (received_bytes == 0) 159 | { 160 | printf("Client is disconnected\n"); 161 | } 162 | else 163 | { 164 | buffer[received_bytes] = 0; 165 | printf("Server received data: %s\n", buffer); 166 | int sent_bytes = send(sock_client, "Status OK", 10, 0); 167 | if (sent_bytes != 10) 168 | { 169 | report_error("Server send() failed"); 170 | } 171 | } 172 | } 173 | } 174 | 175 | close(sock_server); 176 | close(sock_client); 177 | freeaddrinfo(addr_server); 178 | } 179 | 180 | void run_client() 181 | { 182 | int rc; 183 | 184 | struct protoent* tcp_proto = getprotobyname(PROTOCOL); 185 | if (tcp_proto == NULL) 186 | { 187 | report_error("TCP protocol is not supported"); 188 | return; 189 | } 190 | 191 | char port_server[6]; 192 | memset(port_server, 0, 6); 193 | sprintf(port_server, "%d", htons(TCP_PORT)); 194 | 195 | struct addrinfo hints; 196 | memset(&hints, 0, sizeof(hints)); 197 | hints.ai_family = AF_INET; 198 | hints.ai_socktype = SOCK_STREAM; 199 | hints.ai_protocol = tcp_proto->p_proto; 200 | struct addrinfo* addr_server; 201 | rc = getaddrinfo(HOST_NAME, port_server, &hints, &addr_server); 202 | if (rc != 0) 203 | { 204 | report_error("Server getaddrinfo() failed"); 205 | return; 206 | } 207 | 208 | int sock_client = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 209 | if (sock_client < 0) 210 | { 211 | report_error("Client socket() failed"); 212 | freeaddrinfo(addr_server); 213 | return; 214 | } 215 | 216 | // set_non_blocking(sock_client); 217 | 218 | for (struct addrinfo* p_server = addr_server; p_server != NULL; p_server = p_server->ai_next) 219 | { 220 | rc = connect(sock_client, p_server->ai_addr, p_server->ai_addrlen); 221 | if (rc == 0) 222 | { 223 | break; 224 | } 225 | } 226 | 227 | if (rc != 0) 228 | { 229 | report_error("Client connect() failed"); 230 | freeaddrinfo(addr_server); 231 | close(sock_client); 232 | return; 233 | } 234 | 235 | // Client Loop 236 | 237 | char buffer[MESSAGE_SIZE]; 238 | while (1) 239 | { 240 | memset(buffer, 0, MESSAGE_SIZE); 241 | sprintf(buffer, "Client time: %d\n", time(NULL)); 242 | int buffer_len = strcspn(buffer, "\n"); 243 | buffer[buffer_len] = 0; 244 | 245 | int sent_bytes = send(sock_client, buffer, buffer_len, 0); 246 | if (sent_bytes != buffer_len) 247 | { 248 | if (errno != EAGAIN && errno != EWOULDBLOCK) 249 | { 250 | report_error("Client send() failed"); 251 | break; 252 | } 253 | } 254 | else 255 | { 256 | printf("Client sent: %s\n", buffer); 257 | } 258 | 259 | memset(buffer, 0, MESSAGE_SIZE); 260 | int received_bytes = recv(sock_client, buffer, MESSAGE_SIZE, 0); 261 | if (received_bytes < 0) 262 | { 263 | if (errno != EAGAIN && errno != EWOULDBLOCK) 264 | { 265 | report_error("Client recv() failed"); 266 | break; 267 | } 268 | } 269 | else if (received_bytes == 0) 270 | { 271 | printf("Server is down\n"); 272 | } 273 | else 274 | { 275 | printf("Client get response: %s\n", buffer); 276 | } 277 | } 278 | 279 | freeaddrinfo(addr_server); 280 | close(sock_client); 281 | } 282 | 283 | int main(int argc, char** argv) 284 | { 285 | if (argc != 2) 286 | { 287 | print_usage(argv[0]); 288 | return -1; 289 | } 290 | 291 | if (strcmp(argv[1], "server") == 0) 292 | { 293 | run_server(); 294 | } 295 | else if (strcmp(argv[1], "client") == 0) 296 | { 297 | run_client(); 298 | } 299 | else 300 | { 301 | print_usage(argv[0]); 302 | return -1; 303 | } 304 | 305 | return 0; 306 | } -------------------------------------------------------------------------------- /00_tutorials/12_multiplexing_select_client_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define PROTOCOL "tcp" 16 | #define TCP_PORT 45123 17 | #define MESSAGE_SIZE 1024 18 | #define HOST_NAME "localhost" 19 | #define MAX_CONNECTION 100 20 | 21 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 22 | 23 | int global_max_fd = 0; 24 | 25 | void print_usage(const char *program_name) 26 | { 27 | fprintf(stderr, "Usage: %s \n", program_name); 28 | } 29 | 30 | void report_error(const char* message) 31 | { 32 | fprintf(stderr, "Error: %s\n", message); 33 | } 34 | 35 | void set_non_blocking(int socket) 36 | { 37 | int flags = fcntl(socket, F_GETFL, 0); 38 | if (flags == -1) 39 | { 40 | report_error("fcntl(F_GETFL) failed"); 41 | return; 42 | } 43 | 44 | if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) 45 | { 46 | report_error("fcntl(F_SETFL) failed"); 47 | } 48 | } 49 | 50 | void run_server() 51 | { 52 | int rc; 53 | 54 | struct protoent* tcp_proto = getprotobyname(PROTOCOL); 55 | if (tcp_proto == NULL) 56 | { 57 | report_error("TCP protocol is not supported"); 58 | return; 59 | } 60 | 61 | char port_server[6]; 62 | memset(port_server, 0, 6); 63 | sprintf(port_server, "%d", htons(TCP_PORT)); 64 | 65 | struct addrinfo hints; 66 | memset(&hints, 0, sizeof(hints)); 67 | hints.ai_family = AF_INET; 68 | hints.ai_socktype = SOCK_STREAM; 69 | hints.ai_protocol = tcp_proto->p_proto; 70 | hints.ai_flags = AI_PASSIVE; 71 | struct addrinfo* addr_server; 72 | rc = getaddrinfo(HOST_NAME, port_server, &hints, &addr_server); 73 | if (rc != 0) 74 | { 75 | report_error("Server getaddrinfo() failed"); 76 | return; 77 | } 78 | 79 | int sock_server = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 80 | if (sock_server < 0) 81 | { 82 | report_error("Server socket() failed"); 83 | freeaddrinfo(addr_server); 84 | return; 85 | } 86 | 87 | set_non_blocking(sock_server); 88 | 89 | for (struct addrinfo* p_server = addr_server; p_server != NULL; p_server = p_server->ai_next) 90 | { 91 | rc = bind(sock_server, p_server->ai_addr, p_server->ai_addrlen); 92 | if (rc == 0) 93 | { 94 | break; 95 | } 96 | } 97 | 98 | if (rc != 0) 99 | { 100 | report_error("Server bind() failed"); 101 | freeaddrinfo(addr_server); 102 | close(sock_server); 103 | return; 104 | } 105 | 106 | rc = listen(sock_server, MAX_CONNECTION); 107 | if (rc != 0) 108 | { 109 | report_error("Server listen() failed"); 110 | freeaddrinfo(addr_server); 111 | close(sock_server); 112 | return; 113 | } 114 | 115 | // Server Loop 116 | char request_buffer[MESSAGE_SIZE]; 117 | char response_buffer[MESSAGE_SIZE]; 118 | 119 | int fds_client[MAX_CONNECTION]; 120 | memset(fds_client, 0, sizeof(fds_client)); 121 | 122 | fd_set read_set; 123 | fd_set master_set; 124 | FD_ZERO(&master_set); 125 | FD_SET(sock_server, &master_set); 126 | global_max_fd = MAX(global_max_fd, sock_server); 127 | while (1) 128 | { 129 | read_set = master_set; 130 | 131 | int activity = select(global_max_fd + 1, &read_set, NULL, NULL, NULL); 132 | if (activity < 0) 133 | { 134 | report_error("Server select() failed"); 135 | } 136 | 137 | for (int i = 0; i <= global_max_fd; i++) 138 | { 139 | if (FD_ISSET(i, &read_set)) 140 | { 141 | if (i == sock_server) 142 | { 143 | struct sockaddr addr_client; 144 | socklen_t addr_client_len = sizeof(struct sockaddr); 145 | int sock_client = accept(sock_server, &addr_client, &addr_client_len); 146 | if (sock_client < 0) 147 | { 148 | report_error("Server accept() failed"); 149 | continue; 150 | } 151 | else 152 | { 153 | char ip_client[NI_MAXHOST]; 154 | char service_client[NI_MAXSERV]; 155 | rc = getnameinfo(&addr_client, addr_client_len, ip_client, sizeof(ip_client), service_client, sizeof(service_client), NI_NUMERICHOST | NI_NUMERICSERV); 156 | if (rc == 0) 157 | { 158 | printf("Server accepted client connection %s:%s\n", ip_client, service_client); 159 | } 160 | 161 | for (int i = 0; i < MAX_CONNECTION; i++) 162 | { 163 | if (fds_client[i] == 0) 164 | { 165 | fds_client[i] = sock_client; 166 | FD_SET(sock_client, &master_set); 167 | global_max_fd = MAX(global_max_fd, sock_client); 168 | break; 169 | } 170 | } 171 | } 172 | } 173 | else 174 | { 175 | memset(request_buffer, 0, MESSAGE_SIZE); 176 | int received_bytes = recv(i, request_buffer, MESSAGE_SIZE, 0); 177 | if (received_bytes <= 0) 178 | { 179 | if (received_bytes < 0) 180 | { 181 | report_error("Server recv() failed"); 182 | } 183 | else 184 | { 185 | printf("Client %d disconnected\n", i); 186 | } 187 | 188 | close(i); 189 | FD_CLR(i, &master_set); 190 | for (int j = 0; j < MAX_CONNECTION; j++) 191 | { 192 | if (fds_client[j] == i) 193 | { 194 | fds_client[j] = 0; 195 | break; 196 | } 197 | } 198 | } 199 | else 200 | { 201 | request_buffer[received_bytes] = 0; 202 | printf("Server received %d request: %s\n", i, request_buffer); 203 | 204 | memset(response_buffer, 0, MESSAGE_SIZE); 205 | sprintf(response_buffer, "Server time: %ld", time(NULL)); 206 | 207 | int should_disconnect = 0; 208 | int sent_bytes = send(i, response_buffer, strlen(response_buffer), 0); 209 | if (sent_bytes <= 0) 210 | { 211 | should_disconnect = 1; 212 | } 213 | 214 | if (strcmp(request_buffer, "quit") == 0 || strcmp(request_buffer, "exit") == 0) 215 | { 216 | should_disconnect = 1; 217 | } 218 | 219 | if (should_disconnect) 220 | { 221 | printf("Client %d disconnect\n", i); 222 | close(i); 223 | FD_CLR(i, &master_set); 224 | for (int j = 0; j < MAX_CONNECTION; j++) 225 | { 226 | if (fds_client[j] == i) 227 | { 228 | fds_client[j] = 0; 229 | break; 230 | } 231 | } 232 | } 233 | } 234 | } 235 | } 236 | } 237 | } 238 | 239 | printf("Server exit with normal status"); 240 | freeaddrinfo(addr_server); 241 | close(sock_server); 242 | } 243 | 244 | void run_client() 245 | { 246 | int rc; 247 | 248 | struct protoent* tcp_proto = getprotobyname(PROTOCOL); 249 | if (tcp_proto == NULL) 250 | { 251 | report_error("TCP protocol is not available"); 252 | return; 253 | } 254 | 255 | char server_port[6]; 256 | memset(server_port, 0, 6); 257 | sprintf(server_port, "%d", htons(TCP_PORT)); 258 | 259 | struct addrinfo hints; 260 | memset(&hints, 0, sizeof(hints)); 261 | hints.ai_family = AF_INET; 262 | hints.ai_socktype = SOCK_STREAM; 263 | hints.ai_protocol = tcp_proto->p_proto; 264 | struct addrinfo* addr_server; 265 | rc = getaddrinfo(HOST_NAME, server_port, &hints, &addr_server); 266 | if (rc != 0) 267 | { 268 | report_error("Can not resolve hostname"); 269 | return; 270 | } 271 | 272 | int sock_client = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 273 | if (sock_client < 0) 274 | { 275 | report_error("Client socket() failed"); 276 | freeaddrinfo(addr_server); 277 | return; 278 | } 279 | 280 | for (struct addrinfo* p_server = addr_server; p_server != NULL; p_server = p_server->ai_next) 281 | { 282 | rc = connect(sock_client, p_server->ai_addr, p_server->ai_addrlen); 283 | if (rc == 0) 284 | { 285 | break; 286 | } 287 | } 288 | 289 | if (rc != 0) 290 | { 291 | report_error("Client connect() failed"); 292 | freeaddrinfo(addr_server); 293 | close(sock_client); 294 | return; 295 | } 296 | 297 | // client loop 298 | while (1) 299 | { 300 | printf("Client is ready to reate a new request\n"); 301 | char request_buffer[MESSAGE_SIZE]; 302 | memset(request_buffer, 0, MESSAGE_SIZE); 303 | 304 | printf("Enter command: "); 305 | fgets(request_buffer, MESSAGE_SIZE, stdin); 306 | request_buffer[strcspn(request_buffer, "\r\n")] = 0; 307 | 308 | printf("Request: %s\n", request_buffer); 309 | 310 | int request_buffer_len = strlen(request_buffer); 311 | int sent_bytes = send(sock_client, request_buffer, request_buffer_len, 0); 312 | if (sent_bytes != request_buffer_len) 313 | { 314 | report_error("Client sent request fail"); 315 | continue; 316 | } 317 | 318 | char response_buffer[MESSAGE_SIZE]; 319 | memset(response_buffer, 0, MESSAGE_SIZE); 320 | int received_bytes = recv(sock_client, response_buffer, MESSAGE_SIZE, 0); 321 | if (received_bytes <= 0) 322 | { 323 | report_error("Client recv() failed"); 324 | } 325 | else 326 | { 327 | printf("Response: %s\n", response_buffer); 328 | } 329 | 330 | if (strcmp(request_buffer, "exit") == 0 331 | || strcmp(request_buffer, "quit") == 0 332 | || strcmp(request_buffer, "shutdown") == 0) 333 | { 334 | break; 335 | } 336 | } 337 | 338 | freeaddrinfo(addr_server); 339 | close(sock_client); 340 | 341 | printf("Client exit with normal status\n"); 342 | } 343 | 344 | int main(int argc, char** argv) 345 | { 346 | if (argc != 2) 347 | { 348 | print_usage(argv[0]); 349 | return -1; 350 | } 351 | 352 | if (strcmp(argv[1], "server") == 0) 353 | { 354 | run_server(); 355 | } 356 | else if (strcmp(argv[1], "client") == 0) 357 | { 358 | run_client(); 359 | } 360 | else 361 | { 362 | print_usage(argv[0]); 363 | return -1; 364 | } 365 | 366 | return 0; 367 | } -------------------------------------------------------------------------------- /00_tutorials/13_multiplexing_poll_client_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define PROTOCOL "tcp" 16 | #define TCP_PORT 45123 17 | #define MESSAGE_SIZE 1024 18 | #define HOST_NAME "localhost" 19 | #define MAX_CONNECTION 100 20 | 21 | void print_usage(const char *program_name) 22 | { 23 | fprintf(stderr, "Usage: %s \n", program_name); 24 | } 25 | 26 | void report_error(const char* message) 27 | { 28 | fprintf(stderr, "Error: %s\n", message); 29 | } 30 | 31 | void set_non_blocking(int socket) 32 | { 33 | int flags = fcntl(socket, F_GETFL, 0); 34 | if (flags == -1) 35 | { 36 | report_error("fcntl(F_GETFL) failed"); 37 | return; 38 | } 39 | 40 | if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) 41 | { 42 | report_error("fcntl(F_SETFL) failed"); 43 | } 44 | } 45 | 46 | void run_server() 47 | { 48 | int rc; 49 | 50 | struct protoent* tcp_proto = getprotobyname(PROTOCOL); 51 | if (tcp_proto == NULL) 52 | { 53 | report_error("TCP protocol is not supported"); 54 | return; 55 | } 56 | 57 | char port_server[6]; 58 | memset(port_server, 0, 6); 59 | sprintf(port_server, "%d", htons(TCP_PORT)); 60 | 61 | struct addrinfo hints; 62 | memset(&hints, 0, sizeof(hints)); 63 | hints.ai_family = AF_INET; 64 | hints.ai_socktype = SOCK_STREAM; 65 | hints.ai_protocol = tcp_proto->p_proto; 66 | hints.ai_flags = AI_PASSIVE; 67 | struct addrinfo* addr_server; 68 | rc = getaddrinfo(HOST_NAME, port_server, &hints, &addr_server); 69 | if (rc != 0) 70 | { 71 | report_error("Server getaddrinfo() failed"); 72 | return; 73 | } 74 | 75 | int sock_server = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 76 | if (sock_server < 0) 77 | { 78 | report_error("Server socket() failed"); 79 | freeaddrinfo(addr_server); 80 | return; 81 | } 82 | 83 | set_non_blocking(sock_server); 84 | 85 | for (struct addrinfo* p_server = addr_server; p_server != NULL; p_server = p_server->ai_next) 86 | { 87 | rc = bind(sock_server, p_server->ai_addr, p_server->ai_addrlen); 88 | if (rc == 0) 89 | { 90 | break; 91 | } 92 | } 93 | 94 | if (rc != 0) 95 | { 96 | report_error("Server bind() failed"); 97 | freeaddrinfo(addr_server); 98 | close(sock_server); 99 | return; 100 | } 101 | 102 | rc = listen(sock_server, MAX_CONNECTION); 103 | if (rc != 0) 104 | { 105 | report_error("Server listen() failed"); 106 | freeaddrinfo(addr_server); 107 | close(sock_server); 108 | return; 109 | } 110 | 111 | // Server Loop 112 | char request_buffer[MESSAGE_SIZE]; 113 | char response_buffer[MESSAGE_SIZE]; 114 | 115 | struct pollfd fds[MAX_CONNECTION]; 116 | memset(&fds, 0, sizeof(fds)); 117 | 118 | fds[0].fd = sock_server; 119 | fds[0].events = POLLIN; 120 | 121 | int nfds = 1; 122 | 123 | while (1) 124 | { 125 | int activity = poll(fds, nfds, -1); 126 | if (activity < 0) 127 | { 128 | report_error("Server poll() failed"); 129 | break; 130 | } 131 | 132 | if (fds[0].revents & POLLIN) 133 | { 134 | struct sockaddr addr_client; 135 | socklen_t addr_client_len = sizeof(struct sockaddr); 136 | int sock_client = accept(fds[0].fd, &addr_client, &addr_client_len); 137 | if (sock_client < 0) 138 | { 139 | report_error("Server accept() failed"); 140 | continue; 141 | } 142 | else 143 | { 144 | char ip_client[NI_MAXHOST]; 145 | char service_client[NI_MAXSERV]; 146 | rc = getnameinfo(&addr_client, addr_client_len, ip_client, sizeof(ip_client), service_client, sizeof(service_client), NI_NUMERICHOST | NI_NUMERICSERV); 147 | if (rc == 0) 148 | { 149 | printf("Server accepted client connection %s:%s\n", ip_client, service_client); 150 | } 151 | 152 | if (nfds < MAX_CONNECTION) 153 | { 154 | fds[nfds].fd = sock_client; 155 | fds[nfds].events = POLLIN; 156 | nfds++; 157 | } 158 | } 159 | } 160 | 161 | for (int i = 1; i >= 1 && i < nfds; i++) 162 | { 163 | if (fds[i].revents & POLLIN) 164 | { 165 | memset(request_buffer, 0, MESSAGE_SIZE); 166 | int received_bytes = recv(fds[i].fd, request_buffer, MESSAGE_SIZE, 0); 167 | if (received_bytes <= 0) 168 | { 169 | if (received_bytes < 0) 170 | { 171 | report_error("Server recv() failed"); 172 | } 173 | else 174 | { 175 | printf("Client %d disconnected\n", fds[i].fd); 176 | } 177 | 178 | close(fds[i].fd); 179 | fds[i].fd = fds[nfds - 1].fd; 180 | nfds--; 181 | i--; 182 | } 183 | else 184 | { 185 | request_buffer[received_bytes] = 0; 186 | printf("Server received %d request: %s\n", fds[i].fd, request_buffer); 187 | 188 | memset(response_buffer, 0, MESSAGE_SIZE); 189 | sprintf(response_buffer, "Server time: %ld", time(NULL)); 190 | 191 | int should_disconnect = 0; 192 | int sent_bytes = send(fds[i].fd, response_buffer, strlen(response_buffer), 0); 193 | if (sent_bytes <= 0) 194 | { 195 | should_disconnect = 1; 196 | } 197 | 198 | if (strcmp(request_buffer, "quit") == 0 || strcmp(request_buffer, "exit") == 0) 199 | { 200 | should_disconnect = 1; 201 | } 202 | 203 | if (should_disconnect) 204 | { 205 | printf("Client %d disconnect\n", fds[i].fd); 206 | close(fds[i].fd); 207 | fds[i].fd = fds[nfds - 1].fd; 208 | nfds--; 209 | i--; 210 | } 211 | } 212 | } 213 | } 214 | } 215 | 216 | printf("Server exit with normal status"); 217 | freeaddrinfo(addr_server); 218 | close(sock_server); 219 | } 220 | 221 | void run_client() 222 | { 223 | int rc; 224 | 225 | struct protoent* tcp_proto = getprotobyname(PROTOCOL); 226 | if (tcp_proto == NULL) 227 | { 228 | report_error("TCP protocol is not available"); 229 | return; 230 | } 231 | 232 | char server_port[6]; 233 | memset(server_port, 0, 6); 234 | sprintf(server_port, "%d", htons(TCP_PORT)); 235 | 236 | struct addrinfo hints; 237 | memset(&hints, 0, sizeof(hints)); 238 | hints.ai_family = AF_INET; 239 | hints.ai_socktype = SOCK_STREAM; 240 | hints.ai_protocol = tcp_proto->p_proto; 241 | struct addrinfo* addr_server; 242 | rc = getaddrinfo(HOST_NAME, server_port, &hints, &addr_server); 243 | if (rc != 0) 244 | { 245 | report_error("Can not resolve hostname"); 246 | return; 247 | } 248 | 249 | int sock_client = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 250 | if (sock_client < 0) 251 | { 252 | report_error("Client socket() failed"); 253 | freeaddrinfo(addr_server); 254 | return; 255 | } 256 | 257 | for (struct addrinfo* p_server = addr_server; p_server != NULL; p_server = p_server->ai_next) 258 | { 259 | rc = connect(sock_client, p_server->ai_addr, p_server->ai_addrlen); 260 | if (rc == 0) 261 | { 262 | break; 263 | } 264 | } 265 | 266 | if (rc != 0) 267 | { 268 | report_error("Client connect() failed"); 269 | freeaddrinfo(addr_server); 270 | close(sock_client); 271 | return; 272 | } 273 | 274 | // client loop 275 | while (1) 276 | { 277 | printf("Client is ready to reate a new request\n"); 278 | char request_buffer[MESSAGE_SIZE]; 279 | memset(request_buffer, 0, MESSAGE_SIZE); 280 | 281 | printf("Enter command: "); 282 | fgets(request_buffer, MESSAGE_SIZE, stdin); 283 | request_buffer[strcspn(request_buffer, "\r\n")] = 0; 284 | 285 | printf("Request: %s\n", request_buffer); 286 | 287 | int request_buffer_len = strlen(request_buffer); 288 | int sent_bytes = send(sock_client, request_buffer, request_buffer_len, 0); 289 | if (sent_bytes != request_buffer_len) 290 | { 291 | report_error("Client sent request fail"); 292 | continue; 293 | } 294 | 295 | char response_buffer[MESSAGE_SIZE]; 296 | memset(response_buffer, 0, MESSAGE_SIZE); 297 | int received_bytes = recv(sock_client, response_buffer, MESSAGE_SIZE, 0); 298 | if (received_bytes <= 0) 299 | { 300 | report_error("Client recv() failed"); 301 | } 302 | else 303 | { 304 | printf("Response: %s\n", response_buffer); 305 | } 306 | 307 | if (strcmp(request_buffer, "exit") == 0 308 | || strcmp(request_buffer, "quit") == 0 309 | || strcmp(request_buffer, "shutdown") == 0) 310 | { 311 | break; 312 | } 313 | } 314 | 315 | freeaddrinfo(addr_server); 316 | close(sock_client); 317 | 318 | printf("Client exit with normal status\n"); 319 | } 320 | 321 | int main(int argc, char** argv) 322 | { 323 | if (argc != 2) 324 | { 325 | print_usage(argv[0]); 326 | return -1; 327 | } 328 | 329 | if (strcmp(argv[1], "server") == 0) 330 | { 331 | run_server(); 332 | } 333 | else if (strcmp(argv[1], "client") == 0) 334 | { 335 | run_client(); 336 | } 337 | else 338 | { 339 | print_usage(argv[0]); 340 | return -1; 341 | } 342 | 343 | return 0; 344 | } -------------------------------------------------------------------------------- /00_tutorials/14_broadcasting.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #define PROTOCOL "udp" 18 | #define BROADCAST_ADDR "255.255.255.255" 19 | #define BROADCAST_PORT 5555 20 | #define MESSAGE_SIZE 1024 21 | 22 | struct broadcast_t 23 | { 24 | int fd; 25 | struct sockaddr_in addr_receiver; 26 | socklen_t addr_receiver_len; 27 | }; 28 | 29 | void print_usage(const char *program_name) 30 | { 31 | fprintf(stderr, "Usage: %s \n", program_name); 32 | } 33 | 34 | void report_error(const char* message) 35 | { 36 | fprintf(stderr, "Error: %s\n", message); 37 | } 38 | 39 | int setup_broadcast_receiver(struct broadcast_t* receiver_info) 40 | { 41 | int rc; 42 | 43 | receiver_info->fd = socket(AF_INET, SOCK_DGRAM, 0); 44 | if (receiver_info->fd < 0) 45 | { 46 | report_error("socket() failed for receiver"); 47 | return -1; 48 | } 49 | 50 | int optval = 1; 51 | rc = setsockopt(receiver_info->fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); 52 | if (rc != 0) 53 | { 54 | report_error("setsockopt(SO_REUSEADDR) failed"); 55 | return -1; 56 | } 57 | 58 | receiver_info->addr_receiver.sin_family = AF_INET; 59 | receiver_info->addr_receiver.sin_port = htons(BROADCAST_PORT); 60 | receiver_info->addr_receiver.sin_addr.s_addr = htonl(INADDR_ANY); 61 | receiver_info->addr_receiver_len = sizeof(receiver_info->addr_receiver); 62 | 63 | rc = bind(receiver_info->fd, (struct sockaddr *)&receiver_info->addr_receiver, receiver_info->addr_receiver_len); 64 | if (rc < 0) 65 | { 66 | report_error("bind() failed for receiver"); 67 | return -1; 68 | } 69 | 70 | return 0; 71 | } 72 | 73 | int setup_broadcast_sender(struct broadcast_t* sender_info) 74 | { 75 | int rc; 76 | 77 | sender_info->fd = socket(AF_INET, SOCK_DGRAM, 0); 78 | if (sender_info->fd < 0) 79 | { 80 | report_error("socket() failed for sender"); 81 | return -1; 82 | } 83 | 84 | int optval = 1; 85 | rc = setsockopt(sender_info->fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)); 86 | if (rc != 0) 87 | { 88 | report_error("setsockopt(SO_BROADCAST) failed"); 89 | return -1; 90 | } 91 | 92 | sender_info->addr_receiver.sin_family = AF_INET; 93 | sender_info->addr_receiver.sin_port = htons(BROADCAST_PORT); 94 | inet_pton(AF_INET, BROADCAST_ADDR, &sender_info->addr_receiver.sin_addr); 95 | sender_info->addr_receiver_len = sizeof(sender_info->addr_receiver); 96 | 97 | return 0; 98 | } 99 | 100 | void* broadcast_receiver_thread_func(void* arg) 101 | { 102 | struct broadcast_t* broadcast_receiver_info = (struct broadcast_t*)calloc(1, sizeof(struct broadcast_t)); 103 | if (setup_broadcast_receiver(broadcast_receiver_info) != 0) 104 | { 105 | report_error("setup_broadcast_receiver() failed"); 106 | return NULL; 107 | } 108 | 109 | char buffer[MESSAGE_SIZE]; 110 | 111 | printf("Start to listen broadcast messages\n"); 112 | while (1) 113 | { 114 | memset(buffer, 0, MESSAGE_SIZE); 115 | int received_bytes = recvfrom(broadcast_receiver_info->fd, buffer, MESSAGE_SIZE, 0, (struct sockaddr*)&broadcast_receiver_info->addr_receiver, &broadcast_receiver_info->addr_receiver_len); 116 | if (received_bytes <= 0) 117 | { 118 | report_error("Broadcast receiver recvfrom() failed"); 119 | } 120 | else 121 | { 122 | printf("Received broadcast message: %s\n", buffer); 123 | } 124 | } 125 | 126 | close(broadcast_receiver_info->fd); 127 | free(broadcast_receiver_info); 128 | 129 | return NULL; 130 | } 131 | 132 | void* broadcast_sender_thread_func(void* arg) 133 | { 134 | char* nick_name = (char*)arg; 135 | 136 | struct broadcast_t* broadcast_sender_info = (struct broadcast_t*)calloc(1, sizeof(struct broadcast_t)); 137 | if (setup_broadcast_sender(broadcast_sender_info) != 0) 138 | { 139 | report_error("setup_broadcast_sender() failed"); 140 | return NULL; 141 | } 142 | 143 | char broadcast_message[MESSAGE_SIZE]; 144 | while (1) 145 | { 146 | memset(broadcast_message, 0, MESSAGE_SIZE); 147 | sprintf(broadcast_message, "%s is active", nick_name); 148 | int sent_bytes = sendto(broadcast_sender_info->fd, broadcast_message, MESSAGE_SIZE, 0, (struct sockaddr*)&broadcast_sender_info->addr_receiver, broadcast_sender_info->addr_receiver_len); 149 | if (sent_bytes <= 0) 150 | { 151 | report_error("Send broadcast message failed"); 152 | } 153 | sleep(1); 154 | } 155 | 156 | close(broadcast_sender_info->fd); 157 | free(broadcast_sender_info); 158 | 159 | return NULL; 160 | } 161 | 162 | int main(int argc, char** argv) 163 | { 164 | if (argc != 2) 165 | { 166 | print_usage(argv[0]); 167 | return -1; 168 | } 169 | 170 | pthread_t broadcast_receiver_thread; 171 | pthread_create(&broadcast_receiver_thread, NULL, broadcast_receiver_thread_func, NULL); 172 | pthread_detach(broadcast_receiver_thread); 173 | 174 | pthread_t broadcast_sender_thread; 175 | pthread_create(&broadcast_sender_thread, NULL, broadcast_sender_thread_func, argv[1]); 176 | pthread_detach(broadcast_sender_thread); 177 | 178 | char buffer_stdin[MESSAGE_SIZE]; 179 | while (1) 180 | { 181 | printf("User input: "); 182 | memset(buffer_stdin, 0, MESSAGE_SIZE); 183 | fgets(buffer_stdin, MESSAGE_SIZE, stdin); 184 | buffer_stdin[strcspn(buffer_stdin, "\r\n")] = 0; 185 | 186 | if (strcmp(buffer_stdin, "exit") == 0 187 | || strcmp(buffer_stdin, "quit") == 0) 188 | { 189 | break; 190 | } 191 | } 192 | 193 | return 0; 194 | } -------------------------------------------------------------------------------- /00_tutorials/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10...3.28) 2 | project(SocketTutorials) 3 | 4 | set(CMAKE_BUILD_TYPE Debug) 5 | 6 | function(compile_executables target_name file_path) 7 | add_executable(${target_name} ${file_path}) 8 | 9 | target_link_libraries(${target_name} 10 | PRIVATE 11 | pthread 12 | ) 13 | endfunction() 14 | 15 | file(GLOB_RECURSE C_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.c") 16 | 17 | foreach(file_path ${C_FILES}) 18 | get_filename_component(target_name ${file_path} NAME_WE) 19 | compile_executables(${target_name} ${CMAKE_CURRENT_SOURCE_DIR}/${file_path}) 20 | endforeach() 21 | -------------------------------------------------------------------------------- /01_networking_libraries/libcurl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11...3.20) 2 | project(CurlExamples) 3 | 4 | set(CMAKE_BUILD_TYPE Debug) 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | 8 | find_package(Threads) 9 | find_package(CURL REQUIRED) 10 | 11 | function(compile_executables target_name file_path) 12 | add_executable(${target_name} ${file_path}) 13 | target_link_libraries(${target_name} 14 | PRIVATE 15 | CURL::libcurl 16 | Threads::Threads 17 | ) 18 | endfunction() 19 | 20 | compile_executables(basic_curl ${CMAKE_CURRENT_SOURCE_DIR}/src/basic_curl.cpp) 21 | compile_executables(curl_multithreaded ${CMAKE_CURRENT_SOURCE_DIR}/src/curl_multithreaded.cpp) 22 | compile_executables(curl_multi_handle ${CMAKE_CURRENT_SOURCE_DIR}/src/curl_multi_handle.cpp) 23 | -------------------------------------------------------------------------------- /01_networking_libraries/libcurl/src/basic_curl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) 6 | { 7 | size_t total_size = size * nmemb; 8 | ((std::string*)userp)->append((char*)contents, total_size); 9 | return total_size; 10 | } 11 | 12 | int main() 13 | { 14 | curl_version_info_data* info = curl_version_info(CURLVERSION_NOW); 15 | if (info) 16 | { 17 | std::cout << "libcurl version: " << info->version << std::endl; 18 | std::cout << "SSL version: " << info->ssl_version << std::endl; 19 | std::cout << "Libz version: " << info->libz_version << std::endl; 20 | std::cout << "Features: " << info->features << std::endl; 21 | 22 | const char *const *protocols = info->protocols; 23 | if (protocols) 24 | { 25 | std::cout << "Supported protocols: "; 26 | for (int i = 0; protocols[i] != NULL; ++i) 27 | { 28 | std::cout << protocols[i] << " "; 29 | } 30 | std::cout << std::endl; 31 | } 32 | } 33 | else 34 | { 35 | std::cerr << "Failed to get libcurl version info." << std::endl; 36 | } 37 | 38 | CURL *curl; 39 | CURLcode res; 40 | std::string readBuffer; 41 | 42 | curl = curl_easy_init(); 43 | if (curl) { 44 | curl_easy_setopt(curl, CURLOPT_URL, "http://httpstat.us/200"); 45 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); 46 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); 47 | 48 | res = curl_easy_perform(curl); 49 | 50 | if (res != CURLE_OK) 51 | { 52 | std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl; 53 | } 54 | else 55 | { 56 | std::cout << "Response data: " << readBuffer << std::endl; 57 | } 58 | 59 | curl_easy_cleanup(curl); 60 | } 61 | else 62 | { 63 | std::cerr << "Failed to initialize libcurl" << std::endl; 64 | } 65 | 66 | return 0; 67 | } -------------------------------------------------------------------------------- /01_networking_libraries/libcurl/src/curl_multi_handle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | using namespace std; 9 | 10 | struct CurlEasyHandle 11 | { 12 | CURL* easy_handle; 13 | std::string url; 14 | std::string data; 15 | }; 16 | 17 | std::size_t perform_callback(char* ptr, std::size_t size, std::size_t nmemb, void* userdata) 18 | { 19 | std::string* str = static_cast(userdata); 20 | std::size_t total_size = size * nmemb; 21 | str->append(ptr, total_size); 22 | return total_size; 23 | } 24 | 25 | int perform_progress(void* ptr, double download_size, double downloaded, double upoad_size, double uploaded) 26 | { 27 | CurlEasyHandle* progData = (CurlEasyHandle*)ptr; 28 | std::cout << "Downloaded " << progData->url << ": " << downloaded << " bytes" << std::endl; 29 | 30 | return 0; 31 | } 32 | 33 | int main() 34 | { 35 | const std::vector urls = { 36 | "http://www.example.com", 37 | "http://www.google.com", 38 | "http://www.bing.com", 39 | "http://www.speedtest.net", 40 | }; 41 | 42 | CURLM* curl_multi; 43 | int running_status; 44 | 45 | curl_global_init(CURL_GLOBAL_DEFAULT); 46 | 47 | curl_multi = curl_multi_init(); 48 | 49 | std::vector easy_handles(urls.size()); 50 | for (int i = 0; i < urls.size(); i++) 51 | { 52 | easy_handles[i].easy_handle = curl_easy_init(); 53 | easy_handles[i].url = urls[i]; 54 | 55 | curl_easy_setopt(easy_handles[i].easy_handle, CURLOPT_URL, urls[i].c_str()); 56 | curl_easy_setopt(easy_handles[i].easy_handle, CURLOPT_WRITEFUNCTION, perform_callback); 57 | curl_easy_setopt(easy_handles[i].easy_handle, CURLOPT_WRITEDATA, &easy_handles[i].data); 58 | curl_easy_setopt(easy_handles[i].easy_handle, CURLOPT_NOPROGRESS, 0L); 59 | curl_easy_setopt(easy_handles[i].easy_handle, CURLOPT_PROGRESSFUNCTION, perform_progress); 60 | curl_easy_setopt(easy_handles[i].easy_handle, CURLOPT_PROGRESSDATA, &easy_handles[i]); 61 | 62 | curl_multi_add_handle(curl_multi, easy_handles[i].easy_handle); 63 | } 64 | 65 | curl_multi_perform(curl_multi, &running_status); 66 | 67 | do 68 | { 69 | int curl_multi_fds; 70 | CURLMcode rc = curl_multi_perform(curl_multi, &running_status); 71 | if (rc == CURLM_OK) 72 | { 73 | rc = curl_multi_wait(curl_multi, nullptr, 0, 1000, &curl_multi_fds); 74 | } 75 | 76 | if (rc != CURLM_OK) 77 | { 78 | std::cerr << "curl_multi failed, code " << rc << std::endl; 79 | break; 80 | } 81 | } while (running_status); 82 | 83 | for (CurlEasyHandle& handle : easy_handles) 84 | { 85 | std::string filename = handle.url.substr(11, handle.url.find_last_of(".") - handle.url.find_first_of(".") - 1) + ".html"; 86 | std::ofstream file(filename); 87 | if (file.is_open()) 88 | { 89 | file << handle.data; 90 | file.close(); 91 | std::cout << "Data written to " << filename << std::endl; 92 | } 93 | 94 | curl_multi_remove_handle(curl_multi, handle.easy_handle); 95 | curl_easy_cleanup(handle.easy_handle); 96 | } 97 | 98 | curl_multi_cleanup(curl_multi); 99 | curl_global_cleanup(); 100 | 101 | return 0; 102 | } -------------------------------------------------------------------------------- /01_networking_libraries/libcurl/src/curl_multithreaded.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | using namespace std; 9 | 10 | struct ProgressData 11 | { 12 | std::string url; 13 | double lastProgress; 14 | }; 15 | 16 | std::size_t perform_callback(char* ptr, std::size_t size, std::size_t nmemb, void* userdata) 17 | { 18 | std::string* str = static_cast(userdata); 19 | std::size_t total_size = size * nmemb; 20 | str->append(ptr, total_size); 21 | return total_size; 22 | } 23 | 24 | int perform_progress(void* ptr, double download_size, double downloaded, double upoad_size, double uploaded) 25 | { 26 | ProgressData* progData = (ProgressData*)ptr; 27 | 28 | if (downloaded - progData->lastProgress >= 1024.0) 29 | { 30 | std::cout << "Download " << progData->url << ": " << downloaded << " bytes" << std::endl; 31 | progData->lastProgress = downloaded; 32 | } 33 | 34 | return 0; 35 | } 36 | 37 | void perform_request(const std::string& url) 38 | { 39 | CURL* curl; 40 | CURLcode res; 41 | 42 | curl = curl_easy_init(); 43 | if (curl != nullptr) 44 | { 45 | std::string data; 46 | ProgressData progData; 47 | progData.url = url; 48 | progData.lastProgress = 0.0; 49 | 50 | curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); 51 | curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); 52 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, perform_callback); 53 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); 54 | curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); 55 | curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, perform_progress); 56 | curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &progData); 57 | 58 | res = curl_easy_perform(curl); 59 | if (res == CURLE_OK) 60 | { 61 | std::string filename = url.substr(11, url.find_last_of(".") - url.find_first_of(".") - 1) + ".html"; 62 | std::ofstream file(filename); 63 | if (file.is_open()) 64 | { 65 | file << data; 66 | file.close(); 67 | std::cout << "Data written to " << filename << std::endl; 68 | } 69 | } 70 | else 71 | { 72 | std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl; 73 | } 74 | 75 | } 76 | } 77 | 78 | int main() 79 | { 80 | curl_global_init(CURL_GLOBAL_ALL); 81 | 82 | std::vector threads; 83 | std::vector urls = { 84 | "http://www.example.com", 85 | "http://www.google.com", 86 | "http://www.bing.com", 87 | "http://www.speedtest.net", 88 | }; 89 | 90 | for (const std::string& url : urls) 91 | { 92 | threads.push_back(std::thread(perform_request, url)); 93 | } 94 | 95 | for (std::thread& t : threads) 96 | { 97 | t.join(); 98 | } 99 | 100 | curl_global_cleanup(); 101 | 102 | return 0; 103 | } -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11...3.20) 2 | project(HTTPServer) 3 | 4 | set(CMAKE_BUILD_TYPE Debug) 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | 8 | find_package(Threads) 9 | find_package(CURL REQUIRED) 10 | 11 | file(GLOB SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") 12 | 13 | add_executable(HTTPServer main.cpp ${SOURCES}) 14 | 15 | target_link_libraries(HTTPServer 16 | PRIVATE 17 | Threads::Threads 18 | ) 19 | 20 | add_custom_target(deploy_http_root ALL 21 | COMMAND ${CMAKE_COMMAND} -E copy_directory 22 | ${CMAKE_SOURCE_DIR}/http_root 23 | ${CMAKE_BINARY_DIR}/http_root 24 | COMMENT "Deploying HTTP root folder to build directory" 25 | ) 26 | 27 | add_dependencies(HTTPServer deploy_http_root) 28 | 29 | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.1) 30 | target_link_libraries(HTTPServer PRIVATE stdc++fs) 31 | endif() -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/README.md: -------------------------------------------------------------------------------- 1 | ![HTTP Server class diagram](http_server_design.png) 2 | 3 | ```plantuml 4 | @startuml 5 | 6 | title HTTP Server Class Diagram 7 | 8 | class HTTPServer { 9 | - sock_server : int 10 | + start() : void 11 | - setup_socket(int port) : bool 12 | } 13 | 14 | class HTTPConnectionHandler { 15 | - m_parser : HTTPParser 16 | - m_router : HTTPRouter 17 | + handle_client(int sock_client) : ClientActivity 18 | } 19 | 20 | class HTTPParser { 21 | + parse(const std::string& raw_request) : HTTPRequest 22 | } 23 | 24 | class HTTPRouter { 25 | + route(const HTTPRequest& request) : HTTPResponse 26 | } 27 | 28 | class HTTPRequest { 29 | + m_method : std::string 30 | + m_path : std::string 31 | + m_body : std::string 32 | + m_headers : std::unordered_map 33 | } 34 | 35 | class HTTPResponse { 36 | - m_status : int 37 | - m_headers : std::unordered_map 38 | - m_body : std::string 39 | + set_status(int status) : void 40 | + set_body(const std::string& body) : void 41 | + set_header(const std::string& key, const std::string& val) : void 42 | + to_string() : std::string 43 | - code_to_message(int code) : std::string 44 | } 45 | 46 | HTTPServer --> HTTPConnectionHandler : manages 47 | HTTPConnectionHandler --> HTTPParser : uses 48 | HTTPConnectionHandler --> HTTPRouter : uses 49 | HTTPParser --> HTTPRequest : creates 50 | HTTPRouter --> HTTPRequest : uses 51 | HTTPRouter --> HTTPResponse : creates 52 | HTTPConnectionHandler --> HTTPResponse : sends 53 | 54 | @enduml 55 | ``` 56 | -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/defs.h: -------------------------------------------------------------------------------- 1 | #ifndef DEFS_H 2 | #define DEFS_H 3 | 4 | #define STR_HTTP_ROOT_PATH "http_root" 5 | #define STR_HTTP_MAIN_PAGE "index.html" 6 | #define STR_LOCALHOST "localhost" 7 | #define STR_LOCALHOST_IP "127.0.0.1" 8 | #define STR_TCP_PROTOCOL "tcp" 9 | #define MAX_CONNECTION (2) 10 | #define MESSAGE_SIZE (1024) 11 | 12 | enum ClientActivity 13 | { 14 | UNKNOWN = -1, 15 | WAITING, 16 | DISCONNECT, 17 | COMPLETED, 18 | }; 19 | 20 | enum HttpStatus 21 | { 22 | HTTP_200 = 200, 23 | HTTP_400 = 400, 24 | HTTP_403 = 403, 25 | HTTP_404 = 404, 26 | HTTP_500 = 500 27 | }; 28 | 29 | #endif // DEFS_H -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_connection_handler.cpp: -------------------------------------------------------------------------------- 1 | #include "http_connection_handler.h" 2 | #include "logging.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | HTTPConnectionHandler::HTTPConnectionHandler() 15 | { 16 | 17 | } 18 | 19 | ClientActivity HTTPConnectionHandler::handle_client(int sock_client) 20 | { 21 | char buffer[MESSAGE_SIZE]; 22 | std::memset(buffer, 0, MESSAGE_SIZE); 23 | int rc_recv = recv(sock_client, buffer, MESSAGE_SIZE, 0); 24 | if (rc_recv <= 0) 25 | { 26 | LOGI("Receive zero data from client"); 27 | return ClientActivity::DISCONNECT; 28 | } 29 | 30 | HTTPRequest client_request = m_parser.parse(buffer); 31 | HTTPResponse server_response = m_router.route(client_request); 32 | 33 | std::string s_server_response = server_response.to_string(); 34 | int rc_send = send(sock_client, s_server_response.c_str(), s_server_response.length(), 0); 35 | if (rc_send <= 0) 36 | { 37 | LOGE("Server is failed to respond"); 38 | return ClientActivity::DISCONNECT; 39 | } 40 | 41 | return ClientActivity::COMPLETED; 42 | } -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_connection_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_CONNECTION_HANDLER_H 2 | #define HTTP_CONNECTION_HANDLER_H 3 | 4 | #include "defs.h" 5 | #include "http_parser.h" 6 | #include "http_router.h" 7 | 8 | class HTTPConnectionHandler 9 | { 10 | public: 11 | HTTPConnectionHandler(); 12 | ClientActivity handle_client(int sock_client); 13 | 14 | private: 15 | HTTPParser m_parser; 16 | HTTPRouter m_router; 17 | }; 18 | 19 | #endif // HTTP_CONNECTION_HANDLER_H -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_parser.cpp: -------------------------------------------------------------------------------- 1 | #include "http_parser.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | HTTPRequest HTTPParser::parse(const std::string& raw_request) 8 | { 9 | HTTPRequest request; 10 | 11 | std::istringstream iss(raw_request); 12 | std::string line; 13 | 14 | // First line: method and path 15 | if (std::getline(iss, line) && line != "\r") 16 | { 17 | std::istringstream iss_first_line(line); 18 | iss_first_line >> request.m_method >> request.m_path; 19 | } 20 | 21 | while (std::getline(iss, line) && line != "\r") 22 | { 23 | if (!line.empty() && line.back() == '\r') 24 | { 25 | line.pop_back(); 26 | } 27 | 28 | std::size_t pivot = line.find(':'); 29 | if (pivot != std::string::npos) 30 | { 31 | std::string key = line.substr(0, pivot); 32 | std::string val = line.substr(pivot + 1); 33 | 34 | if (!val.empty() && val[0] == ' ') 35 | { 36 | val.erase(0, 1); 37 | } 38 | request.m_headers[key] = val; 39 | } 40 | } 41 | 42 | if (request.m_headers.count("Content-Length") > 0) 43 | { 44 | std::size_t body_length = std::stoi(request.m_headers["Content-Length"]); 45 | request.m_body.resize(body_length); 46 | iss.read(&request.m_body[0], body_length); 47 | } 48 | 49 | return request; 50 | } -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_PARSER_H 2 | #define HTTP_PARSER_H 3 | 4 | #include "http_request.h" 5 | #include 6 | 7 | class HTTPParser 8 | { 9 | public: 10 | HTTPRequest parse(const std::string& raw_request); 11 | }; 12 | 13 | #endif // HTTP_PARSER_H -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_request.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_REQUEST_H 2 | #define HTTP_REQUEST_H 3 | 4 | #include 5 | #include 6 | 7 | class HTTPRequest 8 | { 9 | public: 10 | HTTPRequest() = default; 11 | 12 | public: 13 | std::string m_method; 14 | std::string m_path; 15 | std::unordered_map m_headers; 16 | std::string m_body; 17 | }; 18 | 19 | #endif // HTTP_REQUEST_H -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_response.cpp: -------------------------------------------------------------------------------- 1 | #include "http_response.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | HTTPResponse::HTTPResponse(int code, const std::string& body) 8 | { 9 | 10 | } 11 | 12 | void HTTPResponse::set_status(int status) 13 | { 14 | m_status = status; 15 | } 16 | 17 | void HTTPResponse::set_body(const std::string& body) 18 | { 19 | m_body = body; 20 | set_header("Content-Length", std::to_string(m_body.length())); 21 | } 22 | 23 | void HTTPResponse::set_header(const std::string& key, const std::string& val) 24 | { 25 | m_headers[key] = val; 26 | } 27 | 28 | std::string HTTPResponse::to_string() 29 | { 30 | std::ostringstream oss; 31 | oss << "HTTP/1.1 " << m_status << " " << code_to_message(m_status) << "\r\n"; 32 | for (const auto& header : m_headers) 33 | { 34 | oss << header.first << ":" << header.second << "\r\n"; 35 | } 36 | oss << "Content-Length: " << m_body.length() << "\r\n"; 37 | oss << "\r\n"; 38 | oss << m_body; 39 | return oss.str(); 40 | } 41 | 42 | std::string HTTPResponse::code_to_message(int code) 43 | { 44 | switch (code) 45 | { 46 | case 200: return "OK"; 47 | case 404: return "Not Found"; 48 | case 500: return "Internal Server Error"; 49 | default: return "Unknown"; 50 | } 51 | } -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_response.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_RESPONSE_H 2 | #define HTTP_RESPONSE_H 3 | 4 | #include 5 | #include 6 | 7 | class HTTPResponse 8 | { 9 | public: 10 | HTTPResponse(int code = 404, const std::string& body = "404 Not Found"); 11 | void set_status(int status); 12 | void set_body(const std::string& body); 13 | void set_header(const std::string& key, const std::string& val); 14 | std::string to_string(); 15 | 16 | private: 17 | std::string code_to_message(int code); 18 | 19 | private: 20 | int m_status; 21 | std::unordered_map m_headers; 22 | std::string m_body; 23 | }; 24 | 25 | #endif // HTTP_RESPONSE_H -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_root/200/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 200 OK 7 | 36 | 37 | 38 |
39 |

200 OK

40 |

The request has succeeded. Everything is working as expected.

41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_root/400/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 400 Bad Request 7 | 50 | 51 | 52 |
53 |

400 Bad Request

54 |

The server could not understand your request due to invalid syntax.

55 | Return to Homepage 56 |
57 | 58 | 59 | -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_root/403/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 403 Forbidden 7 | 50 | 51 | 52 |
53 |

403 Forbidden

54 |

You don’t have permission to access this resource.

55 | Return to Homepage 56 |
57 | 58 | 59 | -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_root/404/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 404 Not Found 7 | 54 | 55 | 56 |
57 |
🚧
58 |

404 Not Found

59 |

Oops! The page you’re looking for doesn’t exist or has been moved.

60 | Return to Homepage 61 |
62 | 63 | 64 | -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_root/500/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 500 Internal Server Error 7 | 54 | 55 | 56 |
57 |
💥
58 |

500 Error

59 |

Something went wrong on our server. Please try again later.

60 | Return to Homepage 61 |
62 | 63 | 64 | -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_root/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Main Page 7 | 46 | 47 | 48 |

Main Page

49 |

Select a folder to view its contents:

50 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_router.cpp: -------------------------------------------------------------------------------- 1 | #include "http_router.h" 2 | #include "logging.h" 3 | #include "defs.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #if __has_include() 11 | #include 12 | namespace fs = std::filesystem; 13 | #elif __has_include() 14 | #include 15 | namespace fs = std::experimental::filesystem; 16 | #else 17 | #error "No filesystem support available!" 18 | #endif 19 | 20 | HTTPResponse HTTPRouter::route(const HTTPRequest& request) 21 | { 22 | HTTPResponse response; 23 | 24 | std::string path = fs::current_path().string() + "/" + std::string(STR_HTTP_ROOT_PATH) + request.m_path; 25 | LOGI(path); 26 | if (!fs::exists(path) || !fs::is_directory(path)) 27 | { 28 | LOGE("Path is not found"); 29 | path = fs::current_path().string() + "/" + std::string(STR_HTTP_ROOT_PATH) + std::string("/404"); 30 | response.set_status(HTTP_404); 31 | } 32 | else 33 | { 34 | response.set_status(HTTP_200); 35 | } 36 | 37 | 38 | std::string filename = path + "/" + STR_HTTP_MAIN_PAGE; 39 | std::fstream file(filename, std::ios::in); 40 | if (file.is_open()) 41 | { 42 | file.seekg(0, std::ios::end); 43 | std::streampos file_size = file.tellg(); 44 | file.seekg(0, std::ios::beg); 45 | 46 | std::string html_content; 47 | html_content.resize(file_size); 48 | 49 | file.read(&html_content[0], file_size); 50 | 51 | if (file) 52 | { 53 | response.set_body(html_content); 54 | } 55 | else 56 | { 57 | LOGE("Error reading the file"); 58 | } 59 | 60 | file.close(); 61 | } 62 | 63 | return response; 64 | } 65 | -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_router.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_ROUTER_H 2 | #define HTTP_ROUTER_H 3 | 4 | #include "http_request.h" 5 | #include "http_response.h" 6 | 7 | class HTTPRouter 8 | { 9 | public: 10 | HTTPRouter() = default; 11 | HTTPResponse route(const HTTPRequest& request); 12 | }; 13 | 14 | #endif // HTTP_ROUTER_H -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_server.cpp: -------------------------------------------------------------------------------- 1 | #include "http_server.h" 2 | #include "logging.h" 3 | #include "defs.h" 4 | #include "utils.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | HTTPServer::HTTPServer(int port) 19 | : sock_server(-1) 20 | { 21 | if (this->setup_socket(port) == false) 22 | { 23 | LOGE("Server HTTP service is not ready"); 24 | } 25 | } 26 | 27 | void HTTPServer::start() 28 | { 29 | if (this->sock_server < 0) 30 | { 31 | return; 32 | } 33 | 34 | pollfd fds[MAX_CONNECTION]; 35 | int nfds = 1; 36 | fds[0].fd = this->sock_server; 37 | fds[0].events = POLLIN; 38 | 39 | // Server Loop 40 | while (true) 41 | { 42 | LOGI("Server starts new poll()"); 43 | int rc_poll = poll(fds, nfds, -1); 44 | if (rc_poll < 0) 45 | { 46 | LOGE("Server poll() failed"); 47 | break; 48 | } 49 | 50 | for (int i = 0; i < nfds; i++) 51 | { 52 | if (fds[i].revents & POLLIN) 53 | { 54 | if (fds[i].fd == this->sock_server) 55 | { 56 | sockaddr addr_client; 57 | socklen_t addr_client_len = sizeof(sockaddr); 58 | int sock_client = accept(this->sock_server, &addr_client, &addr_client_len); 59 | if (sock_client < 0) 60 | { 61 | LOGE("Server accept() failed"); 62 | } 63 | else 64 | { 65 | LOGI("A client is connected"); 66 | print_sockaddr_info(&addr_client); 67 | 68 | if (nfds < MAX_CONNECTION) 69 | { 70 | fds[nfds].fd = sock_client; 71 | fds[nfds].events = POLLIN; 72 | nfds++; 73 | } 74 | } 75 | } 76 | } 77 | else 78 | { 79 | HTTPConnectionHandler handler; 80 | ClientActivity activity = handler.handle_client(fds[i].fd); 81 | if (activity != ClientActivity::WAITING) 82 | { 83 | LOGI("A client is disconnected"); 84 | close(fds[i].fd); 85 | fds[i].fd = fds[nfds - 1].fd; 86 | fds[i].events = fds[nfds - 1].events; 87 | nfds--; 88 | } 89 | } 90 | } 91 | } 92 | } 93 | 94 | bool HTTPServer::setup_socket(int port) 95 | { 96 | protoent* tcp_proto = getprotobyname(STR_TCP_PROTOCOL); 97 | if (tcp_proto == nullptr) 98 | { 99 | return false; 100 | } 101 | 102 | std::string s_port = std::to_string(port); 103 | 104 | addrinfo hints; 105 | std::memset(&hints, 0, sizeof(hints)); 106 | hints.ai_family = AF_INET; 107 | hints.ai_socktype = SOCK_STREAM; 108 | hints.ai_protocol = tcp_proto->p_proto; 109 | hints.ai_flags = AI_PASSIVE; 110 | addrinfo* addr_server; 111 | if (getaddrinfo(STR_LOCALHOST, s_port.c_str(), &hints, &addr_server) != 0) 112 | { 113 | LOGE("Server getaddrinfo() failed"); 114 | return false; 115 | } 116 | 117 | this->sock_server = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 118 | if (this->sock_server < 0) 119 | { 120 | LOGE("Server socket() failed"); 121 | freeaddrinfo(addr_server); 122 | this->sock_server = -1; 123 | return false; 124 | } 125 | 126 | set_socket_nonblocking(this->sock_server); 127 | 128 | int rc_bind = 0; 129 | for (addrinfo* p = addr_server; p != nullptr; p = p->ai_next) 130 | { 131 | print_sockaddr_info(p->ai_addr); 132 | rc_bind = bind(this->sock_server, p->ai_addr, p->ai_addrlen); 133 | if (rc_bind == 0) 134 | { 135 | break; 136 | } 137 | } 138 | 139 | if (rc_bind != 0) 140 | { 141 | LOGE("Server bind() failed"); 142 | freeaddrinfo(addr_server); 143 | close(this->sock_server); 144 | this->sock_server = -1; 145 | return false; 146 | } 147 | 148 | if (listen(this->sock_server, MAX_CONNECTION) != 0) 149 | { 150 | LOGE("Server listen() failed"); 151 | freeaddrinfo(addr_server); 152 | close(this->sock_server); 153 | this->sock_server = -1; 154 | return false; 155 | } 156 | 157 | freeaddrinfo(addr_server); 158 | return true; 159 | } 160 | -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_server.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_SERVER_H 2 | #define HTTP_SERVER_H 3 | 4 | #include "http_connection_handler.h" 5 | 6 | class HTTPServer 7 | { 8 | public: 9 | HTTPServer(int port); 10 | void start(); 11 | 12 | private: 13 | bool setup_socket(int port); 14 | 15 | private: 16 | int sock_server; 17 | }; 18 | 19 | #endif // HTTP_SERVER_H -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/http_server_design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nguyenchiemminhvu/LinuxNetworkProgramming/45d335c2f390f1e2d84482cdc66a1d402fe688b8/01_networking_libraries/my_http_server/http_server_design.png -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/logging.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | enum class LogLevel 7 | { 8 | Inform, 9 | Debug, 10 | Error 11 | }; 12 | 13 | class Logger 14 | { 15 | public: 16 | // Singleton instance accessor 17 | static Logger& getInstance() 18 | { 19 | static Logger instance; 20 | return instance; 21 | } 22 | 23 | void setLogLevel(LogLevel level) 24 | { 25 | std::lock_guard lock(m_mutex); 26 | m_logLevel = level; 27 | } 28 | 29 | void inform(const std::string& message) 30 | { 31 | log(LogLevel::Inform, "[INFO] ", message); 32 | } 33 | 34 | void debug(const std::string& message) 35 | { 36 | log(LogLevel::Debug, "[DEBUG] ", message); 37 | } 38 | 39 | void error(const std::string& message) 40 | { 41 | log(LogLevel::Error, "[ERROR] ", message); 42 | } 43 | 44 | private: 45 | LogLevel m_logLevel = LogLevel::Inform; 46 | std::mutex m_mutex; 47 | 48 | Logger() = default; 49 | Logger(const Logger&) = delete; 50 | Logger& operator=(const Logger&) = delete; 51 | 52 | void log(LogLevel level, const std::string& prefix, const std::string& message) 53 | { 54 | std::lock_guard lock(m_mutex); 55 | if (level >= m_logLevel) 56 | { 57 | std::cout << "[" << time(nullptr) << "] " << prefix << message << std::endl; 58 | } 59 | } 60 | }; 61 | 62 | #define LOGI(message) Logger::getInstance().inform(message) 63 | #define LOGD(message) Logger::getInstance().debug(message) 64 | #define LOGE(message) Logger::getInstance().error(message) 65 | -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/main.cpp: -------------------------------------------------------------------------------- 1 | #include "http_server.h" 2 | #include "logging.h" 3 | #include 4 | #include 5 | #include 6 | 7 | void print_usage(const char *program_name) 8 | { 9 | fprintf(stderr, "Usage: %s \n", program_name); 10 | } 11 | 12 | int main(int argc, char** argv) 13 | { 14 | if (argc != 2) 15 | { 16 | print_usage(argv[0]); 17 | return -1; 18 | } 19 | 20 | try 21 | { 22 | int port = std::stoi(argv[1]); 23 | HTTPServer server(port); 24 | server.start(); 25 | } 26 | catch(const std::exception& e) 27 | { 28 | LOGE(e.what()); 29 | print_usage(argv[0]); 30 | } 31 | 32 | return 0; 33 | } -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include "logging.h" 3 | #include 4 | #include 5 | 6 | void print_sockaddr_info(sockaddr* sa) 7 | { 8 | char ip[INET6_ADDRSTRLEN]; 9 | std::memset(ip, 0, INET6_ADDRSTRLEN); 10 | 11 | void *addr; 12 | int port; 13 | 14 | if (sa->sa_family == AF_INET) 15 | { 16 | struct sockaddr_in *sin = (struct sockaddr_in *)sa; 17 | addr = &(sin->sin_addr); 18 | port = ntohs(sin->sin_port); 19 | } 20 | else if (sa->sa_family == AF_INET6) 21 | { 22 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; 23 | addr = &(sin6->sin6_addr); 24 | port = ntohs(sin6->sin6_port); 25 | } 26 | else 27 | { 28 | LOGE("Unknown address family"); 29 | return; 30 | } 31 | 32 | if (inet_ntop(sa->sa_family, addr, ip, sizeof(ip)) == NULL) 33 | { 34 | LOGE("inet_ntop() failed"); 35 | return; 36 | } 37 | 38 | std::string ip_port = std::string(ip) + ":" + std::to_string(port); 39 | LOGI(ip_port); 40 | } 41 | 42 | void set_socket_nonblocking(int sock) 43 | { 44 | int flags = fcntl(sock, F_GETFL, 0); 45 | if (flags == -1) 46 | { 47 | LOGE("fcntl(F_GETFL) failed"); 48 | return; 49 | } 50 | 51 | if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) 52 | { 53 | LOGE("fcntl(F_SETFL, O_NONBLOCK) failed"); 54 | } 55 | } -------------------------------------------------------------------------------- /01_networking_libraries/my_http_server/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | void print_sockaddr_info(sockaddr *sa); 10 | void set_socket_nonblocking(int sock); 11 | 12 | #endif // UTILS_H -------------------------------------------------------------------------------- /01_networking_libraries/openssl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11...3.20) 2 | project(OpenSSLExamples) 3 | 4 | set(CMAKE_BUILD_TYPE Debug) 5 | 6 | find_package(Threads) 7 | find_package(CURL REQUIRED) 8 | 9 | function(compile_executables target_name file_path) 10 | add_executable(${target_name} ${file_path}) 11 | target_link_libraries(${target_name} 12 | PRIVATE 13 | CURL::libcurl 14 | Threads::Threads 15 | ssl 16 | crypto 17 | ) 18 | endfunction() 19 | 20 | add_custom_target(copy_certificates ALL 21 | COMMAND ${CMAKE_COMMAND} -E copy_directory 22 | ${CMAKE_SOURCE_DIR}/certificate 23 | ${CMAKE_BINARY_DIR}/ 24 | COMMENT "Copying certificate files to build directory" 25 | ) 26 | 27 | compile_executables(https_client ${CMAKE_CURRENT_SOURCE_DIR}/src/https_client.c) 28 | 29 | compile_executables(ssl_client_server ${CMAKE_CURRENT_SOURCE_DIR}/src/ssl_client_server.c) 30 | add_dependencies(ssl_client_server copy_certificates) -------------------------------------------------------------------------------- /01_networking_libraries/openssl/certificate/make_cert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | openssl genrsa -out server.key 2048 4 | openssl req -new -key server.key -out server.csr 5 | openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt 6 | -------------------------------------------------------------------------------- /01_networking_libraries/openssl/certificate/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDETCCAfkCFCC3kQ+ei5B1iC575b2MYbV+22p0MA0GCSqGSIb3DQEBCwUAMEUx 3 | CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl 4 | cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjQxMjEzMTQ0MzMyWhcNMjUxMjEzMTQ0 5 | MzMyWjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE 6 | CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOC 7 | AQ8AMIIBCgKCAQEAhle/VBTMT2pWDm7kwORCEYcIxjE0liXs5eUx2dIj2rrpt/wF 8 | NLK9AEgT65rzJ7+SWeXAeBuYQ8EwFJSYRPArnG69797WHGDmJV78b3alCwRzDNM7 9 | AxerG8NFHGRx7S9EQezMagVrOuTYDdyAipj4AIxmu8oGtpTQjyEa7Q42T1MAr0/H 10 | MNqmZUYaaXOLXFFvmUK8gI9ukL5BKhuWeSC7wcQkOPKbXqksyMxi8lV0HHKBFFnl 11 | fubTtDI+1JJYZBQZnMP4l+jdWNiMtksKpHYMC58DaRsW5q4vSm7mqQm4Or017wqw 12 | OruWCE4A/SGfx65pxD+0zhtWawG5Onu58x/WmwIDAQABMA0GCSqGSIb3DQEBCwUA 13 | A4IBAQBJxGgfHS926xjhcGNb3cBISzodLjSuxQyEUfvEPAToz2W8014tkpY8aDJQ 14 | ncf9NTsDMVcqZ3wbYnOIGGOyN+WCiRuy4c0rfJ8Pd9aISKi46xuDHX96+QHUZ5z1 15 | +Rs4uVmVY9lYRzEpfdMow8hq132o/fb+D3zAKWWJo/O2VyhOz/NsQblOx3XWrTgL 16 | voWuPrm1+F5+hL81HovqDt4VEmEf77/V8d8283EJfXtyyQFXKOgIKOc0RHMoj4a8 17 | LmKreedJxfS9WDujVJKS8vgJw70rGtbB707MBvhvej5pLzua9tCbchaAg2O0aEOa 18 | ITS9IZX8rBQbXyjtnAcMOss+3Zkl 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /01_networking_libraries/openssl/certificate/server.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx 3 | ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN 4 | AQEBBQADggEPADCCAQoCggEBAIZXv1QUzE9qVg5u5MDkQhGHCMYxNJYl7OXlMdnS 5 | I9q66bf8BTSyvQBIE+ua8ye/klnlwHgbmEPBMBSUmETwK5xuve/e1hxg5iVe/G92 6 | pQsEcwzTOwMXqxvDRRxkce0vREHszGoFazrk2A3cgIqY+ACMZrvKBraU0I8hGu0O 7 | Nk9TAK9PxzDapmVGGmlzi1xRb5lCvICPbpC+QSoblnkgu8HEJDjym16pLMjMYvJV 8 | dBxygRRZ5X7m07QyPtSSWGQUGZzD+Jfo3VjYjLZLCqR2DAufA2kbFuauL0pu5qkJ 9 | uDq9Ne8KsDq7lghOAP0hn8euacQ/tM4bVmsBuTp7ufMf1psCAwEAAaAAMA0GCSqG 10 | SIb3DQEBCwUAA4IBAQB4zpgQUdnMJnmk06Rgnd8vQh6k41/NOoIX/WQAHOQjvsF+ 11 | 6+Ca7YP+Qi0CBQr7ySuP9AgBLly9AjiV/Os5SpdUfG1L4e76NPIH2GwsAF6GW1Jt 12 | WobwcqXWhofQ6vjS8tDheqc3POFhfOx6AiFVKG6b+A4dEnwhN6iY6RKYnQXHWpZp 13 | WN5+WQ3GfcgcRDWy5Sc4PQoqyRAANS6lIyPcM+1fC9zuMFo/w6aMxN97PROjFhBG 14 | vXT/7klgmdhaasz9u2tk4FmO/RtxtysLiUJVFmcxrtUrh9c17BZY8vFibSHZwDFW 15 | MX3H8DBwIlsR0WZXSZD8fNpwSyMkE0Owl/Hxv8K8 16 | -----END CERTIFICATE REQUEST----- 17 | -------------------------------------------------------------------------------- /01_networking_libraries/openssl/certificate/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCGV79UFMxPalYO 3 | buTA5EIRhwjGMTSWJezl5THZ0iPauum3/AU0sr0ASBPrmvMnv5JZ5cB4G5hDwTAU 4 | lJhE8Cucbr3v3tYcYOYlXvxvdqULBHMM0zsDF6sbw0UcZHHtL0RB7MxqBWs65NgN 5 | 3ICKmPgAjGa7yga2lNCPIRrtDjZPUwCvT8cw2qZlRhppc4tcUW+ZQryAj26QvkEq 6 | G5Z5ILvBxCQ48pteqSzIzGLyVXQccoEUWeV+5tO0Mj7UklhkFBmcw/iX6N1Y2Iy2 7 | SwqkdgwLnwNpGxbmri9KbuapCbg6vTXvCrA6u5YITgD9IZ/HrmnEP7TOG1ZrAbk6 8 | e7nzH9abAgMBAAECggEAA7LkYhMO62jVsbpv5eri8f4yMAxbkrHv0dL7o/zbK9yI 9 | yGrR7qxOwKehx/mq+pjI4lzAoQ3xchW3/MXfY2NRcBJeFf3TdOvEdzO95ru97SdU 10 | NTHKDPQleLNppbYTEbq2Of893MI4osh5aMUXbOyrqo6Zc+leDsAOKSkTx4JKqhub 11 | hEJMjMO58GeDor7zVUYcOeLOSCElFrvWrrWtdDiez5u4LjDb/AzyF8IEeRxn9U2W 12 | oQOTH4u7oG/dICiXk1KT5KuoJbZiJ/n47EVyAvDNZLDTgFKQklCPE4jG1fyZLXkI 13 | WEtQgHrppTOnxXBrkaMoRS+P/mgzf4kKSIZSH08mkQKBgQC9xTCD/kfS/sT/5Dsn 14 | 3ZvqmjtFaW5xbTf9YhfsKI7BS638qlU0ZocWWJRgHpVRhhjrZVt9ZTg2RTsl4KSf 15 | g+NwXedIXuF1PYEDq6FkR/TCi1JMQHDriVeP0XkfHHceGADQVNAfP428ysCbSdIl 16 | XE0H1kMw+HjNMaVJ/CDwNYTj7wKBgQC1OnMjUsS5DL8Rr+09f0x9ONMeJMYEBdN4 17 | mDyBeA7LuTQXgm+xhf12pMD5Ig31SQHNRSA11LkBEAmo+0oXqR7lZNXDJz9hh4vY 18 | SX1YN+YywnBw2kiLtxH8Id6SqcQJWx2iwtp9JdMk30apeaAIrxgEhuUjBebR5tOi 19 | uJDHd2UcFQKBgA5QJCOgdyxb2OHwBeIw3cSpE1Vd6V2WvkVXM7S1Lmc33QyUkE6e 20 | eQvfbHl/dQWsFo8Smv7vNnkpaZemxOPfH7Vm773OojSkiemL9WVJ5BUUSwXDSoEJ 21 | X9hSFU+aZwWXC+CqtPPWaEtYA4Ty4W2983Vm3LSB2SKo3u2gnZ9gegBzAoGBAJgf 22 | rC5md8Nts6VBK7+Hb8ldPbn73EvcKBr+jyuOf7z6hYOGdgNqGczRlzqJH3ZaBi0s 23 | bVFCctk1gSZbaWJbEBg2gEkEVOulP/oaFiFLKuxI0uGoGWVOEsa2F4K9BlVkKohJ 24 | T+XlLkuxMDtqYK9papvCL+0qMwrW26enjcAhtLnRAoGAbIbZSJcA1OGWdjYbShW1 25 | cFzMBXoNfncFbWGaoUKfo4HS35pScIAz4/KE76UAUxA7WgOLgoqw9TSWoDXwCXrH 26 | owNYZMWbpY2FNtv4ZBIlO1PWysq/0Mwdt455Uhg4+8nK+d+pBoh1EAD2FvuLun0t 27 | Qiel9hinZsysf7TeabAhrEg= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /01_networking_libraries/openssl/src/https_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #define TCP_PROTOCOL_NAME "tcp" 19 | #define REQUEST_SIZE 1024 20 | #define RESPONSE_SIZE 4096 21 | 22 | void print_usage(const char *program_name) 23 | { 24 | fprintf(stderr, "Usage: %s \n", program_name); 25 | } 26 | 27 | void report_error(const char* message) 28 | { 29 | fprintf(stderr, "Error: %s\n", message); 30 | } 31 | 32 | void print_sockaddr_info(struct sockaddr *sa) 33 | { 34 | char ip[INET6_ADDRSTRLEN]; 35 | memset(ip, 0, INET6_ADDRSTRLEN); 36 | 37 | void *addr; 38 | int port; 39 | 40 | if (sa->sa_family == AF_INET) 41 | { 42 | struct sockaddr_in *sin = (struct sockaddr_in *)sa; 43 | addr = &(sin->sin_addr); 44 | port = ntohs(sin->sin_port); 45 | } 46 | else if (sa->sa_family == AF_INET6) 47 | { 48 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; 49 | addr = &(sin6->sin6_addr); 50 | port = ntohs(sin6->sin6_port); 51 | } 52 | else 53 | { 54 | report_error("Unknown address family"); 55 | return; 56 | } 57 | 58 | if (inet_ntop(sa->sa_family, addr, ip, sizeof(ip)) == NULL) 59 | { 60 | report_error("inet_ntop() failed"); 61 | return; 62 | } 63 | 64 | printf("%s:%d\n", ip, port); 65 | } 66 | 67 | void perform_https_request(char* hostname, char* port) 68 | { 69 | int rc; 70 | 71 | struct protoent* tcp_proto = getprotobyname(TCP_PROTOCOL_NAME); 72 | if (tcp_proto == NULL) 73 | { 74 | report_error("TCP protocol is not supported"); 75 | return; 76 | } 77 | 78 | struct addrinfo hints; 79 | memset(&hints, 0, sizeof(hints)); 80 | hints.ai_family = AF_INET; 81 | hints.ai_socktype = SOCK_STREAM; 82 | hints.ai_protocol = tcp_proto->p_proto; 83 | struct addrinfo* addr_server; 84 | rc = getaddrinfo(hostname, port, &hints, &addr_server); 85 | if (rc != 0) 86 | { 87 | report_error("Resolve address failed"); 88 | return; 89 | } 90 | 91 | int sock_client = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 92 | if (sock_client < 0) 93 | { 94 | report_error("socket() failed"); 95 | return; 96 | } 97 | 98 | for (struct addrinfo* p_server = addr_server; p_server != NULL; p_server = p_server->ai_next) 99 | { 100 | print_sockaddr_info(p_server->ai_addr); 101 | rc = connect(sock_client, p_server->ai_addr, p_server->ai_addrlen); 102 | if (rc == 0) 103 | { 104 | break; 105 | } 106 | } 107 | 108 | if (rc != 0) 109 | { 110 | report_error("connect() failed"); 111 | return; 112 | } 113 | 114 | SSL_load_error_strings(); 115 | SSL_library_init(); 116 | 117 | const SSL_METHOD* ssl_method = TLS_client_method(); 118 | SSL_CTX* ssl_context = SSL_CTX_new(ssl_method); 119 | if (ssl_context == NULL) 120 | { 121 | report_error("Unable to create SSL context"); 122 | freeaddrinfo(addr_server); 123 | close(sock_client); 124 | return; 125 | } 126 | 127 | SSL* ssl = SSL_new(ssl_context); 128 | if (ssl == NULL) 129 | { 130 | report_error("SSL_new() failed"); 131 | freeaddrinfo(addr_server); 132 | close(sock_client); 133 | SSL_CTX_free(ssl_context); 134 | return; 135 | } 136 | 137 | SSL_set_fd(ssl, sock_client); 138 | rc = SSL_connect(ssl); 139 | if (rc <= 0) 140 | { 141 | report_error("SSL_connect() failed"); 142 | ERR_print_errors_fp(stderr); 143 | freeaddrinfo(addr_server); 144 | close(sock_client); 145 | SSL_CTX_free(ssl_context); 146 | SSL_shutdown(ssl); 147 | SSL_free(ssl); 148 | return; 149 | } 150 | 151 | printf("SSL connection is done with cipher suite %s\n", SSL_get_cipher(ssl)); 152 | 153 | char http_request[REQUEST_SIZE]; 154 | memset(http_request, 0, REQUEST_SIZE); 155 | sprintf(http_request, "GET / HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", hostname); 156 | 157 | rc = SSL_write(ssl, http_request, strlen(http_request)); 158 | if (rc <= 0) 159 | { 160 | report_error("SSL_write() failed"); 161 | ERR_print_errors_fp(stderr); 162 | freeaddrinfo(addr_server); 163 | close(sock_client); 164 | SSL_CTX_free(ssl_context); 165 | SSL_shutdown(ssl); 166 | SSL_free(ssl); 167 | return; 168 | } 169 | 170 | char http_response[RESPONSE_SIZE + 1]; 171 | memset(http_response, 0, RESPONSE_SIZE); 172 | int received_bytes = 0; 173 | int total_received_bytes = 0; 174 | do 175 | { 176 | received_bytes = SSL_read(ssl, http_response + total_received_bytes, RESPONSE_SIZE - total_received_bytes); 177 | if (received_bytes > 0) 178 | { 179 | printf("Received %d bytes\n", received_bytes); 180 | total_received_bytes += received_bytes; 181 | } 182 | } while (received_bytes > 0); 183 | 184 | http_response[total_received_bytes] = 0; 185 | printf("%s\n", http_response); 186 | 187 | freeaddrinfo(addr_server); 188 | close(sock_client); 189 | SSL_CTX_free(ssl_context); 190 | SSL_shutdown(ssl); 191 | SSL_free(ssl); 192 | } 193 | 194 | int main(int argc, char** argv) 195 | { 196 | if (argc != 3) 197 | { 198 | print_usage(argv[0]); 199 | return -1; 200 | } 201 | 202 | perform_https_request(argv[1], argv[2]); 203 | 204 | return 0; 205 | } -------------------------------------------------------------------------------- /01_networking_libraries/openssl/src/ssl_client_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #define PROTOCOL "tcp" 19 | #define TCP_PORT 4443 20 | #define MESSAGE_SIZE 1024 21 | #define HOST_NAME "localhost" 22 | #define MAX_CONNECTION 100 23 | 24 | #define SERVER_CERT_FILE "server.crt" 25 | #define SERVER_KEY_FILE "server.key" 26 | 27 | void print_usage(const char *program_name) 28 | { 29 | fprintf(stderr, "Usage: %s \n", program_name); 30 | } 31 | 32 | void report_error(const char* message) 33 | { 34 | fprintf(stderr, "%ld: Error: %s\n", time(NULL), message); 35 | } 36 | 37 | void print_sockaddr_info(struct sockaddr *sa) 38 | { 39 | char ip[INET6_ADDRSTRLEN]; 40 | memset(ip, 0, INET6_ADDRSTRLEN); 41 | 42 | void *addr; 43 | int port; 44 | 45 | if (sa->sa_family == AF_INET) 46 | { 47 | struct sockaddr_in *sin = (struct sockaddr_in *)sa; 48 | addr = &(sin->sin_addr); 49 | port = ntohs(sin->sin_port); 50 | } 51 | else if (sa->sa_family == AF_INET6) 52 | { 53 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; 54 | addr = &(sin6->sin6_addr); 55 | port = ntohs(sin6->sin6_port); 56 | } 57 | else 58 | { 59 | report_error("Unknown address family"); 60 | return; 61 | } 62 | 63 | if (inet_ntop(sa->sa_family, addr, ip, sizeof(ip)) == NULL) 64 | { 65 | report_error("inet_ntop() failed"); 66 | return; 67 | } 68 | 69 | printf("%s:%d\n", ip, port); 70 | } 71 | 72 | void set_non_blocking(int socket) 73 | { 74 | int flags = fcntl(socket, F_GETFL, 0); 75 | if (flags == -1) 76 | { 77 | report_error("fcntl(F_GETFL) failed"); 78 | return; 79 | } 80 | 81 | if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) 82 | { 83 | report_error("fcntl(F_SETFL) failed"); 84 | } 85 | } 86 | 87 | void run_server() 88 | { 89 | int rc; 90 | 91 | struct protoent* tcp_proto = getprotobyname(PROTOCOL); 92 | if (tcp_proto == NULL) 93 | { 94 | report_error("TCP protocol is not supported"); 95 | return; 96 | } 97 | 98 | char port_server[6]; 99 | memset(port_server, 0, 6); 100 | sprintf(port_server, "%d", TCP_PORT); 101 | 102 | struct addrinfo hints; 103 | memset(&hints, 0, sizeof(hints)); 104 | hints.ai_family = AF_INET; 105 | hints.ai_socktype = SOCK_STREAM; 106 | hints.ai_protocol = tcp_proto->p_proto; 107 | hints.ai_flags = AI_PASSIVE; 108 | struct addrinfo* addr_server; 109 | rc = getaddrinfo(HOST_NAME, port_server, &hints, &addr_server); 110 | if (rc != 0) 111 | { 112 | report_error("Server getaddrinfo() failed"); 113 | return; 114 | } 115 | 116 | int sock_server = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 117 | if (sock_server < 0) 118 | { 119 | report_error("Server socket() failed"); 120 | freeaddrinfo(addr_server); 121 | return; 122 | } 123 | 124 | set_non_blocking(sock_server); 125 | 126 | int optval = 1; 127 | (void)setsockopt(sock_server, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); 128 | 129 | for (struct addrinfo* p = addr_server; p != NULL; p = p->ai_next) 130 | { 131 | print_sockaddr_info(p->ai_addr); 132 | rc = bind(sock_server, p->ai_addr, p->ai_addrlen); 133 | if (rc == 0) 134 | { 135 | break; 136 | } 137 | } 138 | 139 | if (rc != 0) 140 | { 141 | report_error("Server bind() failed"); 142 | freeaddrinfo(addr_server); 143 | close(sock_server); 144 | return; 145 | } 146 | 147 | rc = listen(sock_server, MAX_CONNECTION); 148 | if (rc != 0) 149 | { 150 | report_error("Server listen() failed"); 151 | freeaddrinfo(addr_server); 152 | close(sock_server); 153 | return; 154 | } 155 | 156 | SSL_load_error_strings(); 157 | SSL_library_init(); 158 | 159 | const SSL_METHOD* p_ssl_method = TLS_server_method(); 160 | SSL_CTX* p_ssl_context = SSL_CTX_new(p_ssl_method); 161 | if (p_ssl_context == NULL) 162 | { 163 | report_error("Server is unable to create SSL context"); 164 | ERR_print_errors_fp(stderr); 165 | freeaddrinfo(addr_server); 166 | close(sock_server); 167 | return; 168 | } 169 | 170 | rc = SSL_CTX_use_certificate_file(p_ssl_context, SERVER_CERT_FILE, SSL_FILETYPE_PEM); 171 | if (rc <= 0) 172 | { 173 | report_error("Server SSL_CTX_use_certificate_file() failed"); 174 | ERR_print_errors_fp(stderr); 175 | SSL_CTX_free(p_ssl_context); 176 | freeaddrinfo(addr_server); 177 | close(sock_server); 178 | return; 179 | } 180 | 181 | rc = SSL_CTX_use_PrivateKey_file(p_ssl_context, SERVER_KEY_FILE, SSL_FILETYPE_PEM); 182 | if (rc <= 0) 183 | { 184 | report_error("Server SSL_CTX_use_PrivateKey_file() failed"); 185 | ERR_print_errors_fp(stderr); 186 | SSL_CTX_free(p_ssl_context); 187 | freeaddrinfo(addr_server); 188 | close(sock_server); 189 | return; 190 | } 191 | 192 | SSL* arr_ssl[MAX_CONNECTION]; 193 | struct pollfd fds[MAX_CONNECTION]; 194 | memset(arr_ssl, NULL, sizeof(SSL*) * MAX_CONNECTION); 195 | memset(&fds, 0, sizeof(struct pollfd) * MAX_CONNECTION); 196 | fds[0].fd = sock_server; 197 | fds[0].events = POLLIN; 198 | arr_ssl[0] = SSL_new(p_ssl_context); 199 | SSL_set_fd(arr_ssl[0], fds[0].fd); 200 | 201 | for (int i = 1; i < MAX_CONNECTION; i++) 202 | { 203 | fds[i].fd = -1; 204 | arr_ssl[i] = NULL; 205 | } 206 | 207 | int nfds = 1; 208 | 209 | // Server Loop 210 | char request_buffer[MESSAGE_SIZE]; 211 | char response_buffer[MESSAGE_SIZE]; 212 | while (1) 213 | { 214 | int activity = poll(fds, MAX_CONNECTION, -1); 215 | if (activity <= 0) 216 | { 217 | report_error("Server poll() failed"); 218 | break; 219 | } 220 | 221 | for (int i = 0; i < nfds; i++) 222 | { 223 | if (fds[i].revents & POLLIN) 224 | { 225 | if (fds[i].fd == sock_server) 226 | { 227 | struct sockaddr addr_client; 228 | socklen_t addr_client_len = sizeof(struct sockaddr); 229 | int sock_client = accept(sock_server, &addr_client, &addr_client_len); 230 | if (sock_client > 0) 231 | { 232 | char ip_client[NI_MAXHOST]; 233 | char port_client[NI_MAXSERV]; 234 | rc = getnameinfo(&addr_client, addr_client_len, ip_client, NI_MAXHOST, port_client, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV); 235 | if (rc == 0) 236 | { 237 | printf("Client %d is connected %s:%s\n", sock_client, ip_client, port_client); 238 | } 239 | 240 | if (nfds < MAX_CONNECTION) 241 | { 242 | fds[nfds].fd = sock_client; 243 | fds[nfds].events = POLLIN; 244 | arr_ssl[nfds] = SSL_new(p_ssl_context); 245 | SSL_set_fd(arr_ssl[nfds], fds[nfds].fd); 246 | 247 | rc = SSL_accept(arr_ssl[nfds]); 248 | if (rc <= 0) 249 | { 250 | report_error("Server SSL_accept() failed"); 251 | ERR_print_errors_fp(stderr); 252 | 253 | close(fds[nfds].fd); 254 | fds[nfds].fd = -1; 255 | 256 | SSL_free(arr_ssl[nfds]); 257 | arr_ssl[nfds] = NULL; 258 | } 259 | else 260 | { 261 | set_non_blocking(fds[nfds].fd); 262 | nfds++; 263 | } 264 | } 265 | } 266 | } 267 | else 268 | { 269 | int should_disconnect = 0; 270 | memset(request_buffer, 0, MESSAGE_SIZE); 271 | int received_bytes = SSL_read(arr_ssl[i], request_buffer, MESSAGE_SIZE); 272 | if (received_bytes <= 0) 273 | { 274 | should_disconnect = 1; 275 | } 276 | 277 | if (strcmp(request_buffer, "quit") == 0 || strcmp(request_buffer, "exit") == 0) 278 | { 279 | should_disconnect = 1; 280 | } 281 | 282 | if (should_disconnect) 283 | { 284 | printf("Client %d is disconnected\n", fds[i].fd); 285 | close(fds[i].fd); 286 | SSL_free(arr_ssl[i]); 287 | fds[i].fd = fds[nfds - 1].fd; 288 | arr_ssl[i] = arr_ssl[nfds - 1]; 289 | fds[nfds - 1].fd = -1; 290 | arr_ssl[nfds - 1] = NULL; 291 | nfds--; 292 | } 293 | else 294 | { 295 | request_buffer[received_bytes] = 0; 296 | printf("Server received %d request: %s\n", fds[i].fd, request_buffer); 297 | 298 | memset(response_buffer, 0, MESSAGE_SIZE); 299 | sprintf(response_buffer, "Server time: %ld", time(NULL)); 300 | 301 | int sent_bytes = SSL_write(arr_ssl[i], response_buffer, strlen(response_buffer)); 302 | if (sent_bytes <= 0) 303 | { 304 | report_error("Server SSL_write() failed"); 305 | } 306 | } 307 | } 308 | } 309 | } 310 | } 311 | 312 | freeaddrinfo(addr_server); 313 | close(sock_server); 314 | 315 | SSL_CTX_free(p_ssl_context); 316 | for (int i = 0; i < MAX_CONNECTION; i++) 317 | { 318 | if (fds[i].fd > 0) 319 | { 320 | close(fds[i].fd); 321 | } 322 | 323 | if (arr_ssl[i] != NULL) 324 | { 325 | SSL_shutdown(arr_ssl[i]); 326 | SSL_free(arr_ssl[i]); 327 | arr_ssl[i] = NULL; 328 | } 329 | } 330 | } 331 | 332 | void run_client() 333 | { 334 | int rc; 335 | 336 | struct protoent* tcp_proto = getprotobyname(PROTOCOL); 337 | if (tcp_proto == NULL) 338 | { 339 | report_error("TCP protocol is not supported"); 340 | return; 341 | } 342 | 343 | char port_server[6]; 344 | memset(port_server, 0, 6); 345 | sprintf(port_server, "%d", TCP_PORT); 346 | 347 | struct addrinfo hints; 348 | memset(&hints, 0, sizeof(hints)); 349 | hints.ai_family = AF_INET; 350 | hints.ai_socktype = SOCK_STREAM; 351 | hints.ai_protocol = tcp_proto->p_proto; 352 | struct addrinfo* addr_server; 353 | rc = getaddrinfo(HOST_NAME, port_server, &hints, &addr_server); 354 | if (rc != 0) 355 | { 356 | report_error("Clent getaddrinfo() failed"); 357 | return; 358 | } 359 | 360 | int sock_client = socket(addr_server->ai_family, addr_server->ai_socktype, addr_server->ai_protocol); 361 | if (sock_client < 0) 362 | { 363 | report_error("Client socket() failed"); 364 | freeaddrinfo(addr_server); 365 | return; 366 | } 367 | 368 | for (struct addrinfo* p = addr_server; p != NULL; p = p->ai_next) 369 | { 370 | print_sockaddr_info(p->ai_addr); 371 | rc = connect(sock_client, p->ai_addr, p->ai_addrlen); 372 | if (rc == 0) 373 | { 374 | break; 375 | } 376 | } 377 | 378 | if (rc != 0) 379 | { 380 | report_error("Client connect() failed"); 381 | return; 382 | } 383 | 384 | SSL_load_error_strings(); 385 | SSL_library_init(); 386 | 387 | const SSL_METHOD* p_ssl_method = TLS_client_method(); 388 | SSL_CTX* p_ssl_context = SSL_CTX_new(p_ssl_method); 389 | if (p_ssl_context == NULL) 390 | { 391 | report_error("Client is unable to create SSL context"); 392 | ERR_print_errors_fp(stderr); 393 | freeaddrinfo(addr_server); 394 | close(sock_client); 395 | return; 396 | } 397 | 398 | SSL* p_ssl = SSL_new(p_ssl_context); 399 | if (p_ssl == NULL) 400 | { 401 | report_error("Client SSL_new() failed"); 402 | ERR_print_errors_fp(stderr); 403 | SSL_CTX_free(p_ssl_context); 404 | freeaddrinfo(addr_server); 405 | close(sock_client); 406 | return; 407 | } 408 | 409 | SSL_set_fd(p_ssl, sock_client); 410 | rc = SSL_connect(p_ssl); 411 | if (rc <= 0) 412 | { 413 | report_error("Client SSL_connect() failed"); 414 | ERR_print_errors_fp(stderr); 415 | freeaddrinfo(addr_server); 416 | close(sock_client); 417 | SSL_CTX_free(p_ssl_context); 418 | SSL_shutdown(p_ssl); 419 | SSL_free(p_ssl); 420 | return; 421 | } 422 | 423 | // Client Loop 424 | char request_buffer[MESSAGE_SIZE]; 425 | char response_buffer[MESSAGE_SIZE]; 426 | while (1) 427 | { 428 | printf("Request: "); 429 | memset(request_buffer, 0, MESSAGE_SIZE); 430 | fgets(request_buffer, MESSAGE_SIZE, stdin); 431 | request_buffer[strcspn(request_buffer, "\r\n")] = 0; 432 | 433 | if (strcmp(request_buffer, "exit") == 0 434 | || strcmp(request_buffer, "quit") == 0 435 | || strcmp(request_buffer, "shutdown") == 0) 436 | { 437 | break; 438 | } 439 | 440 | int sent_bytes = SSL_write(p_ssl, request_buffer, strlen(request_buffer)); 441 | if (sent_bytes <= 0) 442 | { 443 | report_error("Client SSL_write() failed"); 444 | continue; 445 | } 446 | 447 | memset(response_buffer, 0, MESSAGE_SIZE); 448 | int received_bytes = SSL_read(p_ssl, response_buffer, MESSAGE_SIZE); 449 | if (received_bytes <= 0) 450 | { 451 | report_error("Client SSL_read() failed"); 452 | continue; 453 | } 454 | response_buffer[received_bytes] = 0; 455 | 456 | printf("Server response: %s\n", response_buffer); 457 | } 458 | 459 | freeaddrinfo(addr_server); 460 | close(sock_client); 461 | SSL_CTX_free(p_ssl_context); 462 | SSL_shutdown(p_ssl); 463 | SSL_free(p_ssl); 464 | } 465 | 466 | int main(int argc, char** argv) 467 | { 468 | if (argc != 2) 469 | { 470 | print_usage(argv[0]); 471 | return -1; 472 | } 473 | 474 | if (strcmp(argv[1], "server") == 0) 475 | { 476 | run_server(); 477 | } 478 | else if (strcmp(argv[1], "client") == 0) 479 | { 480 | run_client(); 481 | } 482 | else 483 | { 484 | print_usage(argv[0]); 485 | return -1; 486 | } 487 | 488 | return 0; 489 | } -------------------------------------------------------------------------------- /SSL_client_workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nguyenchiemminhvu/LinuxNetworkProgramming/45d335c2f390f1e2d84482cdc66a1d402fe688b8/SSL_client_workflow.png -------------------------------------------------------------------------------- /SSL_server_workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nguyenchiemminhvu/LinuxNetworkProgramming/45d335c2f390f1e2d84482cdc66a1d402fe688b8/SSL_server_workflow.png -------------------------------------------------------------------------------- /how_https_work.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nguyenchiemminhvu/LinuxNetworkProgramming/45d335c2f390f1e2d84482cdc66a1d402fe688b8/how_https_work.png -------------------------------------------------------------------------------- /http_connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nguyenchiemminhvu/LinuxNetworkProgramming/45d335c2f390f1e2d84482cdc66a1d402fe688b8/http_connection.png -------------------------------------------------------------------------------- /tcp_based_client_server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nguyenchiemminhvu/LinuxNetworkProgramming/45d335c2f390f1e2d84482cdc66a1d402fe688b8/tcp_based_client_server.png -------------------------------------------------------------------------------- /udp_based_client_server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nguyenchiemminhvu/LinuxNetworkProgramming/45d335c2f390f1e2d84482cdc66a1d402fe688b8/udp_based_client_server.png --------------------------------------------------------------------------------