{"id":24624568,"url":"https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming","last_synced_at":"2025-10-06T16:31:57.053Z","repository":{"id":265284136,"uuid":"859389597","full_name":"nguyenchiemminhvu/LinuxNetworkProgramming","owner":"nguyenchiemminhvu","description":"A comprehensive guide for Linux Network (Socket) programming","archived":false,"fork":false,"pushed_at":"2025-01-19T12:17:00.000Z","size":751,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-19T13:28:44.372Z","etag":null,"topics":["c","cpp","network-programming","socket-programming"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nguyenchiemminhvu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-18T15:22:18.000Z","updated_at":"2025-01-19T12:17:02.000Z","dependencies_parsed_at":"2024-11-28T18:28:39.775Z","dependency_job_id":"2f310eeb-ff72-40bd-b05c-c7541c44a57a","html_url":"https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming","commit_stats":null,"previous_names":["nguyenchiemminhvu/linuxnetworkprogramming"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nguyenchiemminhvu%2FLinuxNetworkProgramming","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nguyenchiemminhvu%2FLinuxNetworkProgramming/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nguyenchiemminhvu%2FLinuxNetworkProgramming/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nguyenchiemminhvu%2FLinuxNetworkProgramming/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nguyenchiemminhvu","download_url":"https://codeload.github.com/nguyenchiemminhvu/LinuxNetworkProgramming/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235534366,"owners_count":19005470,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","cpp","network-programming","socket-programming"],"created_at":"2025-01-25T04:01:29.093Z","updated_at":"2025-10-06T16:31:51.493Z","avatar_url":"https://github.com/nguyenchiemminhvu.png","language":"C","funding_links":[],"categories":["C","Computer Networks and Network Programming"],"sub_categories":["WebGPU"],"readme":"- [Introduction](#introduction)\n- [Sockets](#sockets)\n  - [Type of sockets](#type-of-sockets)\n  - [Addressing sockets](#addressing-sockets)\n  - [Socket APIs](#socket-apis)\n    - [getprotobyname()](#getprotobyname)\n    - [getservbyname()](#getservbyname)\n    - [getaddrinfo()](#getaddrinfo)\n    - [htonl(), htons(), ntohl(), ntohs()](#htonl-htons-ntohl-ntohs)\n    - [socket()](#socket)\n    - [setsockopt()](#setsockopt)\n    - [bind()](#bind)\n    - [listen()](#listen)\n    - [accept()](#accept)\n    - [connect()](#connect)\n    - [recv()](#recv)\n    - [send()](#send)\n    - [close()](#close)\n- [Programming Client-Server Models](#programming-client-server-models)\n  - [Client-Server Architecture](#client-server-architecture)\n  - [Simple HTTP Client](#simple-http-client)\n    - [Include necessary headers](#include-necessary-headers)\n    - [Define some constants](#define-some-constants)\n    - [Define Helper functions](#define-helper-functions)\n    - [Get TCP Protocol](#get-tcp-protocol)\n    - [Get Port of HTTP service](#get-port-of-http-service)\n    - [Convert Port to Network Byte Order](#convert-port-to-network-byte-order)\n    - [Resolve Host Name](#resolve-host-name)\n    - [Create A Socket](#create-a-socket)\n    - [Connect to HTTP server](#connect-to-http-server)\n    - [Send HTTP Request](#send-http-request)\n    - [Receive HTTP Response](#receive-http-response)\n    - [Clean Up](#clean-up)\n  - [Simple TCP-Based Client-Server](#simple-tcp-based-client-server)\n    - [Necessary Headers And Macros](#necessary-headers-and-macros)\n    - [Utility Functions](#utility-functions)\n    - [Setup Server](#setup-server)\n    - [Setup Client](#setup-client)\n    - [Main Function](#main-function)\n  - [Multithread TCP-Based Client-Server](#multithread-tcp-based-client-server)\n    - [Setup Server with multithreading](#setup-server-with-multithreading)\n  - [Simple UDP-Based Client-Server](#simple-udp-based-client-server)\n    - [Necessary Headers And Macros](#necessary-headers-and-macros-1)\n    - [Setup Server](#setup-server-1)\n    - [Setup Client](#setup-client-1)\n  - [Advanced Techniques](#advanced-techniques)\n    - [Non-blocking sockets](#non-blocking-sockets)\n    - [Synchronous I/O Multiplexing with select()](#synchronous-io-multiplexing-with-select)\n    - [Synchronous I/O Multiplexing with poll()](#synchronous-io-multiplexing-with-poll)\n    - [Broadcasting Messages](#broadcasting-messages)\n  - [Create A Simple HTTP Server](#create-a-simple-http-server)\n    - [Demo](#demo)\n- [Networking Libraries](#networking-libraries)\n  - [Using libcurl](#using-libcurl)\n    - [curl command-line examples](#curl-command-line-examples)\n    - [Basic Curl](#basic-curl)\n    - [Curl Multiple Handles](#curl-multiple-handles)\n    - [Curl Multithreading](#curl-multithreading)\n  - [Secure Networking with OpenSSL](#secure-networking-with-openssl)\n    - [A HTTPS Client](#a-https-client)\n    - [Secure Client-Server](#secure-client-server)\n- [Conclusion](#conclusion)\n\n\n# Introduction\n\nIn my opinion, Linux network programming, especially socket programming, isn’t that difficult. However, learning this topic on your own can be challenging because many online resources are unclear, and sample codes often only cover the basics. You might find yourself unsure of what to do next. That's why I created this tutorial. It aims to give you clear guidelines and plenty of examples to help you understand better.\n\nLinux network programming deals with the interaction between processes using network interfaces. It enables interprocess communication (```IPC```), allowing data exchange between processes running on the same machine or on different machines connected over a network.\n\nThe foundation of Linux network programming lies in the use of sockets, a universal API designed for interprocess communication. Sockets originated from BSD Unix in 1983 and were later standardized by POSIX, making them a cornerstone of modern networking.\n\n# Sockets\n\nA socket is an endpoint for communication. Think of it as a door through which data flows in and out of a process. Processes use sockets to send and receive messages, enabling seamless ```IPC```.\n\nSockets were initially designed to support two domains:\n\n**Unix Domain (Unix)**: Used for communication between processes within the same operating system.\n\n**Internet Domain (INET)**: Used for communication between processes on different systems connected via a TCP/IP network.\n\nUnix domain sockets are used for ```IPC``` within the same operating system. They are faster than ```INET``` sockets because they don't require network protocol overhead. Instead of IP addresses, Unix domain sockets use file system paths for addressing.\n\n```INET``` domain sockets are used for communication between processes on different systems connected over a network. These sockets rely on the ```TCP/IP``` protocol stack, which ensures data integrity and delivery.\n\nTwo common protocols used with ```INET``` domain sockets are:\n\n**TCP (Transmission Control Protocol)**: Provides reliable, ordered, and error-checked delivery of data.\n\n**UDP (User Datagram Protocol)**: Provides fast, connectionless data transmission without guarantees of delivery.\n\n## Type of sockets\n\nThe BSD socket API supports several types of sockets, which determine how data is transmitted between processes:\n\n**Stream Sockets (SOCK_STREAM)**: These provide a reliable, connection-oriented communication protocol. Data is sent and received as a continuous stream of bytes. Typically used with ```TCP``` (Transmission Control Protocol).\n\n**Datagram Sockets (SOCK_DGRAM)**: These provide a connectionless communication protocol. Data is sent in discrete packets, and delivery isn't guaranteed. Typically used with ```UDP``` (User Datagram Protocol).\n\n**Raw Sockets (SOCK_RAW)**: These allow processes to access lower-level network protocols directly, bypassing the standard ```TCP``` or ```UDP``` layers. Useful for custom protocol implementations or network monitoring tools.\n\n## Addressing sockets\n\nIn the INET domain, sockets are identified by two components:\n\n**IP Address**: A 32-bit number (```IPv4```) or a 128-bit number (```IPv6```) that uniquely identifies a device on a network. IPv4 addresses are often represented in dotted decimal notation, such as ```192.168.1.1```.\n\n**Port Number**: A 16-bit number that identifies a specific service or application on the device. For example, web servers typically use port 80 (```HTTP```) or 443 (```HTTPS```).\n\nCheck some of well-known services in Linux system via ```/etc/services``` file. Ports under 1024 are often considered special, and usually require special OS privileges to use.\n\n```\nworker@7e4a84e41875:~/study_workspace/LinuxNetworkProgramming$ cat /etc/services\ntcpmux          1/tcp                           # TCP port service multiplexer\necho            7/tcp\necho            7/udp\n...\nftp             21/tcp\nfsp             21/udp          fspd\nssh             22/tcp                          # SSH Remote Login Protocol\ntelnet          23/tcp\nsmtp            25/tcp          mail\n...\nhttp            80/tcp          www             # WorldWideWeb HTTP\n...\n```\n\n## Socket APIs\n\n### getprotobyname()\n\n```\n#include \u003cnetdb.h\u003e\n\nstruct protoent *getprotobyname(const char *name);\n```\n\nSample usage:\n\n```\nstruct protoent *proto;\nproto = getprotobyname(\"tcp\");\nif (proto)\n{\n    printf(\"Protocol number for TCP: %d\\n\", proto-\u003ep_proto);\n}\n```\n\n**Description**: ```getprotobyname()``` returns a ```protoent``` structure for the given protocol name, which contains information about the protocol.\n\n### getservbyname()\n\n```\n#include \u003cnetdb.h\u003e\n\nstruct servent *getservbyname(const char *name, const char *proto);\n```\n\nSample usage:\n\n```\nstruct servent *serv;\nserv = getservbyname(\"http\", \"tcp\");\nif (serv)\n{\n    printf(\"Port number for HTTP: %d\\n\", ntohs(serv-\u003es_port));\n}\n```\n\n**Description**: ```getservbyname()``` returns a ```servent``` structure for the given service name and protocol, which contains information about the service.\n\n### getaddrinfo()\n\n```\n#include \u003csys/types.h\u003e\n#include \u003csys/socket.h\u003e\n#include \u003cnetdb.h\u003e\n\nstruct addrinfo {\n    int              ai_flags;     // AI_PASSIVE, AI_CANONNAME, etc.\n    int              ai_family;    // AF_INET, AF_INET6, AF_UNSPEC\n    int              ai_socktype;  // SOCK_STREAM, SOCK_DGRAM\n    int              ai_protocol;  // use 0 for \"any\"\n    size_t           ai_addrlen;   // size of ai_addr in bytes\n    struct sockaddr *ai_addr;      // struct sockaddr_in or _in6\n    char            *ai_canonname; // full canonical hostname\n\n    struct addrinfo *ai_next;      // linked list, next node\n};\n \nint getaddrinfo(const char *node,     // e.g. \"www.example.com\" or IP\n                const char *service,  // e.g. \"http\" or port number\n                const struct addrinfo *hints,\n                struct addrinfo **res);\n```\n\nStruct ```addrinfo``` has the pointer to struct ```sockaddr``` which is used in many socket functions.\n\nSample usage:\n\n```\nint status;\nstruct addrinfo hints;\nstruct addrinfo *servinfo;  // will point to the results\n \nmemset(\u0026hints, 0, sizeof(hints)); // make sure the struct is empty\nhints.ai_family = AF_UNSPEC;     // don't care IPv4 or IPv6\nhints.ai_socktype = SOCK_STREAM; // TCP stream sockets\nhints.ai_flags = AI_PASSIVE;     // fill in my IP for me\n \nif ((status = getaddrinfo(NULL, \"3490\", \u0026hints, \u0026servinfo)) != 0)\n{\n    fprintf(stderr, \"getaddrinfo error: %s\\n\", gai_strerror(status));\n    exit(1);\n}\n \n// servinfo now points to a linked list of 1 or more struct addrinfos\n \n// ... do everything until you don't need servinfo anymore ....\n \nfreeaddrinfo(servinfo); // free the linked-list\n```\n\n**Description**: ```getaddrinfo()``` is used to get a list of address structures for the specified node and service, which can be used to create and connect sockets.\n\n### htonl(), htons(), ntohl(), ntohs()\n\n```\n#include \u003carpa/inet.h\u003e\n\nuint32_t htonl(uint32_t hostlong);\nuint16_t htons(uint16_t hostshort);\nuint32_t ntohl(uint32_t netlong);\nuint16_t ntohs(uint16_t netshort);\n```\n\nSample usage:\n\n```\nuint32_t host_port = 8080;\nuint32_t net_port = htonl(host_port);\nprintf(\"Network byte order: 0x%x\\n\", net_port);\n```\n\n**Description**: These functions convert values between host and network byte order. ```htonl()``` and ```htons()``` convert from host to network byte order, while ```ntohl()``` and ```ntohs()``` convert from network to host byte order.\n\n```htons(uint16_t hostshort)```: Converts a 16-bit number from host byte order to network byte order.\n\n```htonl(uint32_t hostlong)```: Converts a 32-bit number from host byte order to network byte order.\n\n```ntohs(uint16_t netshort)```: Converts a 16-bit number from network byte order to host byte order.\n\n```ntohl(uint32_t netlong)```: Converts a 32-bit number from network byte order to host byte order.\n\n**What is Network Byte Order?**\n\nNetwork byte order is a standardized way of arranging the bytes of multi-byte data types (like integers) in network communication. Different CPU architecture may process data in different orders, we called it **\"endianness\"**.\n\n\u003e Big-endian (BE): Stores the most significant byte (the “big end”) first. This means that the first byte (at the lowest memory address) is the largest, which makes the most sense to people who read left to right\n \n\u003e Little-endian (LE): Stores the least significant byte (the “little end”) first. This means that the first byte (at the lowest memory address) is the smallest, which makes the most sense to people who read right to left.\n\nData transferred in network is always Big-endian order.\n\nData sending from host machine is called Host-byte order, could be big or little endian. Using the functions above ensure proper communication between systems.\n\n### socket()\n\n```\n#include \u003csys/types.h\u003e\n#include \u003csys/socket.h\u003e\n \nint socket(int domain, int type, int protocol);\n```\n\nSample usage:\n\n```\nint s;\nstruct addrinfo hints, *res;\n \ngetaddrinfo(\"www.example.com\", \"http\", \u0026hints, \u0026res);\n \ns = socket(res-\u003eai_family, res-\u003eai_socktype, res-\u003eai_protocol);\nif (s == -1)\n{\n    perror(\"socket\");\n    exit(1);\n}\n```\n\n**Description**: ```socket()``` creates a new socket and returns a file descriptor for it.\n\n### setsockopt()\n\n```\n#include \u003csys/types.h\u003e\n#include \u003csys/socket.h\u003e\n\nint setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);\n```\n\nSample usage:\n\n```\nint sockfd; // Assume sockfd is a valid socket descriptor\nint optval = 1;\nif (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, \u0026optval, sizeof(optval)) == -1)\n{\n    perror(\"setsockopt\");\n    exit(1);\n}\n```\n\n**Description**: ```setsockopt()``` sets options on a socket, such as enabling the reuse of local addresses.\n\n### bind()\n\n```\n#include \u003csys/types.h\u003e\n#include \u003csys/socket.h\u003e\n#include \u003cnetinet/in.h\u003e\n\nint bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);\n```\n\nSample usage:\n\n```\nint sockfd; // Assume sockfd is a valid socket descriptor\nstruct sockaddr_in my_addr;\nmy_addr.sin_family = AF_INET;\nmy_addr.sin_port = htons(3490);\nmy_addr.sin_addr.s_addr = INADDR_ANY;\n\nif (bind(sockfd, (struct sockaddr *)\u0026my_addr, sizeof(my_addr)) == -1)\n{\n    perror(\"bind\");\n    exit(1);\n}\n```\n\n**Description**: ```bind()``` assigns a local address to a socket.\n\n### listen()\n\n```\n#include \u003csys/types.h\u003e\n#include \u003csys/socket.h\u003e\n\nint listen(int sockfd, int backlog);\n```\n\nSample usage:\n\n```\nint sockfd; // Assume sockfd is a valid socket descriptor\nif (listen(sockfd, 10) == -1)\n{\n    perror(\"listen\");\n    exit(1);\n}\n```\n\n**Description**: ```listen()``` marks a socket as a passive socket that will be used to accept incoming connection requests.\n\n### accept()\n\n```\n#include \u003csys/types.h\u003e\n#include \u003csys/socket.h\u003e\n\nint accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);\n```\n\nSample usage:\n\n```\nint sockfd; // Assume sockfd is a valid socket descriptor\nstruct sockaddr_storage their_addr;\nsocklen_t addr_size = sizeof(their_addr);\nint new_fd = accept(sockfd, (struct sockaddr *)\u0026their_addr, \u0026addr_size);\nif (new_fd == -1)\n{\n    perror(\"accept\");\n    exit(1);\n}\n```\n\n**Description**: ```accept()``` accepts a connection on a socket. If the server wants to send responding data back to client, it will send data to the returned fd.\n\n### connect()\n\n```\n#include \u003csys/types.h\u003e\n#include \u003csys/socket.h\u003e\n\nint connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);\n```\n\nSample usage:\n\n```\nint sockfd; // Assume sockfd is a valid socket descriptor\nstruct addrinfo hints, *res;\n\nmemset(\u0026hints, 0, sizeof(hints));\nhints.ai_family = AF_UNSPEC;\nhints.ai_socktype = SOCK_STREAM;\n\nif (getaddrinfo(\"www.example.com\", \"http\", \u0026hints, \u0026res) != 0)\n{\n    fprintf(stderr, \"getaddrinfo error\\n\");\n    exit(1);\n}\n\nif (connect(sockfd, res-\u003eai_addr, res-\u003eai_addrlen) == -1)\n{\n    perror(\"connect\");\n    exit(1);\n}\n```\n\n**Description**: ```connect()``` initiates a connection on a socket.\n\n### recv()\n\n```\n#include \u003csys/types.h\u003e\n#include \u003csys/socket.h\u003e\n\nssize_t recv(int sockfd, void *buf, size_t len, int flags);\n```\n\nSample usage:\n\n```\nint sockfd; // Assume sockfd is a valid socket descriptor\nchar buf[100];\nssize_t bytes_received = recv(sockfd, buf, sizeof(buf), 0);\nif (bytes_received == -1)\n{\n    perror(\"recv\");\n    exit(1);\n}\nelse\n{\n    printf(\"Received %zd bytes\\n\", bytes_received);\n}\n```\n\n**Description**: ```recv()``` receives data from a socket.\n\n### send()\n\n```\n#include \u003csys/types.h\u003e\n#include \u003csys/socket.h\u003e\n\nssize_t send(int sockfd, const void *buf, size_t len, int flags);\n```\n\nSample usage:\n\n```\nint sockfd; // Assume sockfd is a valid socket descriptor\nchar *msg = \"Hello, World!\";\nssize_t bytes_sent = send(sockfd, msg, strlen(msg), 0);\nif (bytes_sent == -1)\n{\n    perror(\"send\");\n    exit(1);\n}\nelse\n{\n    printf(\"Sent %zd bytes\\n\", bytes_sent);\n}\n```\n\n**Description**: ```send()``` sends data to a socket.\n\n### close()\n\n```\n#include \u003cunistd.h\u003e\n\nint close(int fd);\n```\n\nSample usage:\n\n```\nclose(sockfd);\n```\n\n**Description**: ```close()``` closes a file descriptor, so that it no longer refers to any file and may be reused.\n\n# Programming Client-Server Models\n\n## Client-Server Architecture\n\nThe client-server model is a way of organizing networked computers where one computer (the client) requests services or resources from another computer (the server). The server provides these services or resources to the client.\n\n## Simple HTTP Client\n\nHere are our goals:\n\n- we want to write a program which gets the address of a WWW site (e.g. ```httpstat.us```) as the argument and fetches the document.\n- the program outputs the document to stdout;\n- the program uses TCP to connect to the ```HTTP``` server.\n\nClick [HERE](https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming/blob/main/00_tutorials/07_http_redirection.c) for a complete source code.\n\n![HTTP Connection](https://raw.githubusercontent.com/nguyenchiemminhvu/LinuxNetworkProgramming/refs/heads/main/http_connection.png)\n\n### Include necessary headers\n\n```\n#include \u003cunistd.h\u003e\n#include \u003cstdio.h\u003e\n#include \u003cstring.h\u003e\n#include \u003cstdlib.h\u003e\n#include \u003csys/socket.h\u003e\n#include \u003cnetdb.h\u003e\n#include \u003carpa/inet.h\u003e\n```\n\n```unistd.h```: Provides access to the POSIX operating system API, including file descriptors and the close() function.\n\n```stdio.h```: Standard I/O library for input/output operations (e.g., printf, fprintf).\n\n```stdlib.h```: Standard library for memory management (malloc, free) and program control (exit).\n\n```string.h```: Provides string manipulation functions like memset, strlen, etc.\n\n```arpa/inet.h```: Functions for manipulating IP addresses, such as ntohs and inet_ntoa.\n\n```netdb.h```: Functions for network database operations, such as getaddrinfo, getprotobyname, and getservbyname.\n\n```sys/socket.h```: Defines socket-related functions like socket, connect, send, and recv.\n\n### Define some constants\n\n```\nconst char* CPP_HOSTNAME = \"httpstat.us\";\nconst int MESSAGE_SIZE = 1024;\n```\n\n```CPP_HOSTNAME```: The hostname of the remote server the program will connect to.\n\n```MESSAGE_SIZE```: The size of the buffer used for sending and receiving data (1 KB in this case).\n\n### Define Helper functions\n\n```\nvoid on_func_failure(const char* message)\n{\n    fprintf(stderr, \"Error: %s\\n\", message);\n    exit(EXIT_FAILURE);\n}\n```\n\nA helper function to handle errors. When a function fails, this function is called with an error message.\n\n```fprintf(stderr, ...)```: Prints the error message to the standard error output.\n\n```exit(EXIT_FAILURE)```: Terminates the program with a failure status code.\n\n### Get TCP Protocol\n\n```\nstruct protoent* p_proto_ent = getprotobyname(\"tcp\");\nif (p_proto_ent == NULL)\n{\n    on_func_failure(\"TCP protocol is not available\");\n}\n```\n\n```getprotobyname(\"tcp\")```: Retrieves the protocol entry for \"tcp\" (Transmission Control Protocol). The function returns a protoent structure that contains protocol information.\n\nIf it returns NULL, the program exits using ```on_func_failure``` because TCP is required for the connection.\n\n### Get Port of HTTP service\n\n```\nservent* p_service_ent = getservbyname(\"http\", p_proto_ent-\u003ep_name);\nif (p_service_ent == NULL)\n{\n    on_func_failure(\"HTTP service is not available\");\n}\n```\n\n```getservbyname(\"http\", p_proto_ent-\u003ep_name)```: Retrieves information about the \"http\" service, including the port number (usually 80 for HTTP).\n\nIf getservbyname fails, the program exits. This function ensures the port number for HTTP is available.\n\n### Convert Port to Network Byte Order\n\n```\nchar port_buffer[6];\nmemset(port_buffer, 0, sizeof(port_buffer));\nsprintf(port_buffer, \"%d\", ntohs(p_service_ent-\u003es_port));\n```\n\nPort Conversion: The port number from ```getservbyname``` is in network byte order (big-endian). ```ntohs``` converts it to host byte order (little-endian, on most systems).\n\n```sprintf```: Converts the port number to a string (stored in port_buffer), which is required by getaddrinfo.\n\n### Resolve Host Name\n\n```\nstruct addrinfo hints;\nmemset(\u0026hints, 0, sizeof(hints));\nhints.ai_family = AF_INET;\nhints.ai_protocol = p_proto_ent-\u003ep_proto;\nhints.ai_socktype = SOCK_STREAM;\n\nstruct addrinfo* server_addr;\nint rc = getaddrinfo(CPP_HOSTNAME, port_buffer, \u0026hints, \u0026server_addr);\nif (rc != 0)\n{\n    on_func_failure(\"Failed to resolve hostname\");\n}\n```\n\n```addrinfo```: A structure that holds information for socket creation.\n\n```ai_family = AF_INET```: Specifies IPv4 addresses.\n\n```ai_socktype = SOCK_STREAM```: Specifies a TCP connection.\n\n```ai_protocol = p_proto_ent-\u003ep_proto```: Ensures the protocol is TCP.\n\n```getaddrinfo```: Resolves the hostname (cppinstitute.org) and port (80) into an address that can be used for connecting.\n\nIf getaddrinfo fails, the program exits.\n\n### Create A Socket\n\n```\nint sock_fd = socket(server_addr-\u003eai_family, server_addr-\u003eai_socktype, server_addr-\u003eai_protocol);\nif (sock_fd \u003c 0)\n{\n    freeaddrinfo(server_addr);\n    on_func_failure(\"socket() failed\");\n}\n```\n\n```socket()```: Creates a new socket for communication.\n\nThe arguments specify the address family, socket type, and protocol (IPv4, TCP).\n\nIf the socket creation fails, the program exits.\n\n### Connect to HTTP server\n\n```\nrc = connect(sock_fd, server_addr-\u003eai_addr, sizeof(struct sockaddr));\nif (rc != 0)\n{\n    freeaddrinfo(server_addr);\n    on_func_failure(\"connect() failed\");\n}\n```\n\n```connect()```: Initiates a connection to the remote server using the socket.\n\nIf connect fails, the program cleans up allocated resources and exits.\n\n### Send HTTP Request\n\n```\nchar http_request[MESSAGE_SIZE];\nmemset(http_request, 0, MESSAGE_SIZE);\nsprintf(http_request, \"GET / HTTP/1.1\\r\\nHost: %s\\r\\nConnection: close\\r\\n\\r\\n\", CPP_HOSTNAME);\n\nint http_request_len = strlen(http_request);\nint sent_bytes = 0;\nwhile (sent_bytes \u003c http_request_len)\n{\n    int sent_rc = send(sock_fd, http_request + sent_bytes, http_request_len - sent_bytes, 0);\n    printf(\"sent %d bytes\\n\", sent_rc);\n    sent_bytes += sent_rc;\n}\n```\n\nA conversation with the HTTP server consists of requests (sent by the client) and responses (sent by the server).\n\nTo get a root document from a site named www.site.com, the client should send the request to the server:\n\n```\nGET / HTTP / 1.1 \\ r \\ n Host: www.site.com \\ r \\ n Connection:close \\ r \\ n \\ r \\ n\n```\n\nThe request consists of:\n\n- a line containing a request name (```GET```) followed by the name of the resource the client wants to receive; the root documents is specified as a single slash (```/```); the line must also include the HTTP protocol version (```HTTP/1.1```), and must end with the ```\\r\\n``` characters; note: all the lines must be ended in the same way;\n- a line containing the name of the site (```www.site.com```) preceded by the parameter name (Host:)\n- a line containing the parameter named Connection: along with its value, close forces the server to close the connection after the first request is served; it will simplify our client’s code;\n- an empty line is the request’s terminator.\n\nIf the request is correct, the server’s response will begin with a more or less similar header.\n\n```\nHTTP/1.1 200 OK\nContent-Type: text/plain\nDate: Thu, 05 Dec 2024 07:07:58 GMT\nServer: Kestrel\nSet-Cookie: ARRAffinity=b3b03edd65273a52d0e5a4a4995ddf09acfbb7f67adccaf277d300c0a375ea34;Path=/;HttpOnly;Domain=httpstat.us\nRequest-Context: appId=cid-v1:3548b0f5-7f75-492f-82bb-b6eb0e864e53\nX-RBT-CLI: Name=LGEVN-Hanoi-ACC-5080M-A; Ver=9.14.2b;\nConnection: close\nContent-Length: 6\n\n200 OK\n```\n\n### Receive HTTP Response\n\n```\nchar http_response[MESSAGE_SIZE];\nmemset(http_response, 0, MESSAGE_SIZE);\nint received_bytes = 0;\nwhile (1 == 1)\n{\n    int received_rc = recv(sock_fd, http_response + received_bytes, MESSAGE_SIZE - received_bytes, 0);\n    printf(\"Received %d bytes\\n\", received_rc);\n    received_bytes += received_rc;\n}\n```\n\n```recv()```: Receives the server's response in chunks and appends it to the http_response buffer.\n\nWhen ```recv()``` returns 0 or a negative value, it indicates the server has closed the connection or an error occurred.\n\n### Clean Up\n\n```\nclose(sock_fd);\nfreeaddrinfo(server_addr);\n```\n\n```close()```: Closes the socket, releasing system resources.\n\n```freeaddrinfo()```: Frees the memory allocated by getaddrinfo.\n\n## Simple TCP-Based Client-Server\n\nClick [HERE](https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming/blob/main/00_tutorials/08_simple_client_server.c) for a complete source code.\n\n![TCP-Based Client-Server](https://raw.githubusercontent.com/nguyenchiemminhvu/LinuxNetworkProgramming/refs/heads/main/tcp_based_client_server.png)\n\n### Necessary Headers And Macros\n\n```\n#include \u003cunistd.h\u003e\n#include \u003cstdio.h\u003e\n#include \u003ctime.h\u003e\n#include \u003cstring.h\u003e\n#include \u003cstdlib.h\u003e\n#include \u003carpa/inet.h\u003e\n#include \u003cnetdb.h\u003e\n#include \u003csys/socket.h\u003e\n#include \u003csys/stat.h\u003e\n\n#define PROTOCOL \"tcp\"\n#define TCP_PORT 45123\n#define MESSAGE_SIZE 1024\n#define HOST_NAME \"localhost\"\n```\n\n**Headers:**\n\n```unistd.h```: Provides POSIX API functions, e.g., close.\n\n```stdio.h```: For input/output operations, e.g., printf, fprintf.\n\n```time.h```: To get the current time using time().\n\n```string.h```: For string operations, e.g., strcmp, memset.\n\n```stdlib.h```: For memory allocation and process control.\n\n```arpa/inet.h```: For socket-related functions and data structures.\n\n```netdb.h```: To resolve hostnames using getaddrinfo and gethostbyname.\n\n```sys/socket.h```: Core socket programming functions, e.g., socket, connect, bind.\n\n```sys/stat.h```: For file and directory operations.\n\n**Macros:**\n\n```PROTOCOL```: Defines the protocol as tcp.\n\n```TCP_PORT```: The port number the server and client use for communication.\n\n```MESSAGE_SIZE```: Maximum size for messages sent/received.\n\n```HOST_NAME```: Default hostname, set to localhost.\n\n### Utility Functions\n\n```\nvoid print_usage(const char *program_name)\n{\n    fprintf(stderr, \"Usage: %s \u003cclient|server\u003e\\n\", program_name);\n}\n```\n\nPrints a usage guide, showing how to execute the program. Example usage:\n\n```./program_name client```\n\nor\n\n```./program_name server.```\n\n```\nvoid report_error(const char* message)\n{\n    fprintf(stderr, \"Error: %s\\n\", message);\n}\n```\n\nPrints error messages to stderr.\n\n### Setup Server\n\n**Choose protocol and resolve server address**\n\n```\nstruct protoent* tcp_proto = getprotobyname(PROTOCOL);\n```\n\nRetrieves the protocol structure for the ```\"tcp\"``` protocol using ```getprotobyname()```.\n\n```\nchar server_port[6];\nmemset(server_port, 0, 6);\nsprintf(server_port, \"%d\", htons(TCP_PORT));\n\nstruct addrinfo addr_hints;\nmemset(\u0026addr_hints, 0, sizeof(addr_hints));\naddr_hints.ai_family = AF_INET;\naddr_hints.ai_socktype = SOCK_STREAM;\naddr_hints.ai_protocol = tcp_proto-\u003ep_proto;\n\nstruct addrinfo* addr_server;\nrc = getaddrinfo(NULL, server_port, \u0026addr_hints, \u0026addr_server);\n```\n\nConverts the TCP port into network byte order using ```htons()```.\n\nInitializes an addrinfo structure to specify connection parameters:\n- ai_family = AF_INET: IPv4.\n- ai_socktype = SOCK_STREAM: TCP socket.\n\nResolves the server's address information using ```getaddrinfo()```.\n\n**Create server socket**\n\n```\nint sock_server = socket(addr_server-\u003eai_family, addr_server-\u003eai_socktype, addr_server-\u003eai_protocol);\n```\n\nCreates a socket using the ```socket()``` function.\n\n\n```\nint sock_server_opt = 1;\nrc = setsockopt(sock_server, SOL_SOCKET, SO_REUSEADDR | SO_KEEPALIVE, \u0026sock_server_opt, sizeof(sock_server_opt));\n```\n\nConfigures socket options:\n\n```SO_REUSEADDR```: Allows the server to reuse the same port.\n\n```SO_KEEPALIVE```: Keeps the connection alive.\n\n**Bind socket to address and start to listen**\n\n```\nfor (addrinfo* p_server = addr_server; p_server != NULL; p_server = p_server-\u003eai_next)\n{\n    rc = bind(sock_server, p_server-\u003eai_addr, p_server-\u003eai_addrlen);\n    if (rc == 0)\n    {\n        break;\n    }\n}\n```\n\nBinds the socket to the resolved address using ```bind()``` function.\n\nIterates over potential addresses (```addrinfo``` list) until successful.\n\n```\nrc = listen(sock_server, 3);\n```\n\nStarts listening for incoming client connections with a backlog of 3.\n\n**Server Loop - Accept incoming client connection**\n\n```\nstruct sockaddr addr_client;\nsocklen_t addr_len = sizeof(addr_client);\nsock_client = accept(sock_server, (struct sockaddr*)\u0026addr_client, \u0026addr_len);\n```\n\nAccepts incoming client connections using ```accept()``` function.\n\n**Server Loop - Receiving requests**\n\n```\nint received_bytes = recv(sock_client, request_buffer, MESSAGE_SIZE, 0);\n```\n\nReads data from the client using ```recv()``` function.\n\n**Server Loop - Processing requests**\n\n```\nif (strcmp(request_buffer, \"exit\") == 0\n|| strcmp(request_buffer, \"quit\") == 0\n|| strcmp(request_buffer, \"shutdown\") == 0)\n{\n    sprintf(response_buffer, \"OK\");\n    rc = send(sock_client, response_buffer, MESSAGE_SIZE, 0);\n    close(sock_client);\n    break;\n}\nelse if (strcmp(request_buffer, \"time\") == 0)\n{\n    sprintf(response_buffer, \"%d\", time(NULL));\n    rc = send(sock_client, response_buffer, MESSAGE_SIZE, 0);\n}\nelse\n{\n    sprintf(response_buffer, \"Unknown request\");\n    rc = send(sock_client, response_buffer, MESSAGE_SIZE, 0);\n}\n```\n\nHandles specific commands:\n\n```time```: Sends the current time.\n\n```exit, quit, shutdown```: Terminates the connection.\n\n```Other inputs```: Responds with \"Unknown request\".\n\n### Setup Client\n\n**Choose protocol and resolve server address**\n\n```\nstruct protoent* tcp_proto = getprotobyname(PROTOCOL);\n\nstruct addrinfo addr_hints;\nmemset(\u0026addr_hints, 0, sizeof(addr_hints));\naddr_hints.ai_family = AF_INET;\naddr_hints.ai_socktype = SOCK_STREAM;\naddr_hints.ai_protocol = tcp_proto-\u003ep_proto;\n\nstruct addrinfo* addr_server;\nrc = getaddrinfo(HOST_NAME, server_port, \u0026addr_hints, \u0026addr_server);\n```\n\nResolves the server's address.\n\n**Create client socket and connect to server**\n\n```\nint sock_client = socket(addr_server-\u003eai_family, addr_server-\u003eai_socktype, addr_server-\u003eai_protocol);\n\nfor (addrinfo* p_server = addr_server; p_server != NULL; p_server = p_server-\u003eai_next)\n{\n    rc = connect(sock_client, p_server-\u003eai_addr, p_server-\u003eai_addrlen);\n    if (rc == 0)\n    {\n        break;\n    }\n}\n```\n\nCreates a socket and connects to the server.\n\nIterates over potential addresses (```addrinfo``` list) until successful.\n\n**Client Loop - Send request and wait for response**\n\n```\nfgets(request_buffer, MESSAGE_SIZE, stdin);\nrequest_buffer[strcspn(request_buffer, \"\\n\")] = 0;\nsend(sock_client, request_buffer, strlen(request_buffer), 0);\nrecv(sock_client, response_buffer, MESSAGE_SIZE, 0);\n```\n\nSends user input to the server and waits for a response.\n\n### Main Function\n\n```\nif (strcmp(argv[1], \"server\") == 0)\n{\n    run_server();\n}\nelse if (strcmp(argv[1], \"client\") == 0)\n{\n    run_client();\n}\n```\n\nDetermines whether the program will run as a server or client based on the command-line argument.\n\n## Multithread TCP-Based Client-Server\n\nThe provided C program is a simple implementation of a TCP client-server application and works well for basic use cases. However, it has several limitations, particularly on the server-side: it can only handle one client connection at a time. While it processes a request from one client, other clients are left waiting.\n\n**Improvement:**\n\nUse multithreading to handle multiple clients concurrently. Each client connection can be assigned to a separate thread, allowing the server to process multiple requests simultaneously.\n\nClick [HERE](https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming/blob/main/00_tutorials/09_multithread_client_server.c) for a complete source code.\n\n### Setup Server with multithreading\n\nThe setup of Server socket and connection is the same as before. But in the Server Loop, each client connection will be handled in a detached thread.\n\n```\nint* p_sock_client = (int*)calloc(1, sizeof(int));\n*p_sock_client = sock_client;\npthread_t client_thread;\nrc = pthread_create(\u0026client_thread, NULL, server_handle_client, p_sock_client);\n\nrc = pthread_detach(client_thread);\n```\n\n**Thread Code for a client**\n\n```\nvoid* server_handle_client(void* arg)\n{\n    int* sock_client = ((int*)arg);\n    if (sock_client == NULL)\n    {\n        return NULL;\n    }\n\n    int rc;\n    char request_buffer[MESSAGE_SIZE];\n    char response_buffer[MESSAGE_SIZE];\n    while (true)\n    {\n        memset(request_buffer, 0, MESSAGE_SIZE);\n        memset(response_buffer, 0, MESSAGE_SIZE);\n\n        int received_bytes = recv(*sock_client, request_buffer, MESSAGE_SIZE, 0);\n        \n        rc = send(*sock_client, response_buffer, MESSAGE_SIZE, 0);\n\n        if (strcmp(request_buffer, \"exit\") == 0\n        || strcmp(request_buffer, \"quit\") == 0\n        || strcmp(request_buffer, \"shutdown\") == 0)\n        {\n            break;\n        }\n    }\n\n    if (sock_client != NULL)\n    {\n        free(sock_client);\n    }\n\n    return NULL;\n}\n```\n\n## Simple UDP-Based Client-Server\n\nOverall, the setup for UDP-Based client server application is similar with TCP-Based. I will show the different codes only.\n\nClick [HERE](https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming/blob/main/00_tutorials/10_peer_to_peer_client_server.c) for a complete source code.\n\n![UDP-Based Client-Server](https://raw.githubusercontent.com/nguyenchiemminhvu/LinuxNetworkProgramming/refs/heads/main/udp_based_client_server.png)\n\n### Necessary Headers And Macros\n\n```\n#define PROTOCOL \"udp\"\n#define UDP_PORT 45123\n#define MESSAGE_SIZE 1024\n#define HOST_NAME \"localhost\"\n```\n\nSimilar as previous explanation, but now the protocol is **UDP**.\n\n### Setup Server\n\n**Resolve Server Address**\n\n```\nstruct protoent* udp_protocol = getprotobyname(PROTOCOL);\n\nstruct addrinfo hints;\nmemset(\u0026hints, 0, sizeof(hints));\nhints.ai_family = AF_INET;\nhints.ai_socktype = SOCK_DGRAM;\nhints.ai_protocol = udp_protocol-\u003ep_proto;\nstruct addrinfo* addr_server;\nrc = getaddrinfo(NULL, port_server, \u0026hints, \u0026addr_server); // INADDR_ANY\n```\n\nSpecifies the socket type datagram for UDP connection.\n\n**Server Loop - Listen client request and response**\n\n```\nwhile (1)\n{\n    struct sockaddr addr_client;\n    socklen_t addr_client_len = sizeof(struct sockaddr);\n    int received_bytes = recvfrom(sock_server, request_buffer, MESSAGE_SIZE, 0, \u0026addr_client, \u0026addr_client_len);\n\n    sprintf(response_buffer, \"Server received request at %d\", time(NULL));\n    int response_buffer_len = strlen(response_buffer);\n    rc = sendto(sock_server, response_buffer, response_buffer_len, 0, \u0026addr_client, addr_client_len);\n}\n```\n\nThe ```recvfrom()``` and ```sendto()``` functions are the general format of ```recv()``` and ```send()``` functions, they are suitable to use in UDP packet transferring.\n\n### Setup Client\n\n```\nstruct protoent* udp_protocol = getprotobyname(PROTOCOL);\n\nstruct addrinfo hints;\nmemset(\u0026hints, 0, sizeof(hints));\nhints.ai_family = AF_INET;\nhints.ai_socktype = SOCK_DGRAM;\nhints.ai_protocol = udp_protocol-\u003ep_proto;\nstruct addrinfo* addr_server;\nrc = getaddrinfo(HOST_NAME, port_server, \u0026hints, \u0026addr_server);\n```\n\nSpecifies the socket type datagram for UDP connection.\n\n**Client Loop - Send request and wait for response**\n\n```\nchar request_buffer[MESSAGE_SIZE];\nchar response_buffer[MESSAGE_SIZE];\nwhile (1)\n{\n    printf(\"Enter command: \");\n    fgets(request_buffer, MESSAGE_SIZE, stdin);\n    request_buffer[strcspn(request_buffer, \"\\r\\n\")] = '\\0';\n\n    int request_buffer_len = strlen(request_buffer);\n    rc = sendto(sock_client, request_buffer, request_buffer_len, 0, addr_server-\u003eai_addr, addr_server-\u003eai_addrlen);\n\n    int received_bytes = recvfrom(sock_client, response_buffer, MESSAGE_SIZE, 0, addr_server-\u003eai_addr, \u0026addr_server-\u003eai_addrlen);\n}\n```\n\n## Advanced Techniques\n\n### Non-blocking sockets\n\nNon-blocking sockets support to build responsive applications or handle multiple connections without blocking the main thread.\n\nThe code [HERE](https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming/blob/main/00_tutorials/11_non_blocking_sockets.c) demonstrates the use of non-blocking sockets in a simple TCP-based application.\n\nIn this example, the ```fcntl()``` function is used to set the server and client sockets to non-blocking mode.\n\n```\n#include \u003cfcntl.h\u003e\n\nint fcntl(int fd, int cmd, ... /* arg */);\n```\n\n**Parameters:**\n\n```fd```: The file descriptor to operate on. It must already be open.\n\n```cmd```: The command to perform on the file descriptor. Common commands include:\n\n- ```F_DUPFD```: Duplicate a file descriptor.\n- ```F_GETFD```: Get the file descriptor flags.\n- ```F_SETFD```: Set the file descriptor flags.\n- ```F_GETFL```: Get the file status flags.\n- ```F_SETFL```: Set the file status flags.\n- ```F_SETLK```, ```F_SETLKW```, ```F_GETLK```: Manage file locks.\n\n```arg (optional)```: An argument whose type and meaning depend on the cmd. It's typically an integer or a pointer, depending on the command.\n\n**Utility Functions**\n\n```\nvoid set_non_blocking(int socket)\n{\n    int flags = fcntl(socket, F_GETFL, 0);\n    if (flags == -1)\n    {\n        report_error(\"fcntl(F_GETFL) failed\");\n        return;\n    }\n\n    if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1)\n    {\n        report_error(\"fcntl(F_SETFL) failed\");\n    }\n}\n```\n\nThe utility function ```set_non_blocking()``` is used to configure the file descriptor of client and server sockets to operate non-blocking mode.\n\n**Server socket initialization and binding**\n\n```\nstruct protoent* tcp_proto = getprotobyname(PROTOCOL);\nint sock_server = socket(addr_server-\u003eai_family, addr_server-\u003eai_socktype, addr_server-\u003eai_protocol);\nset_non_blocking(sock_server);\n```\n\nCreates a TCP socket and sets it to non-blocking mode.\n\nUsing ```set_non_blocking()``` ensures that the server does not block while waiting for connections.\n\n**Accept client connection**\n\n```\nsock_client = accept(sock_server, \u0026addr_client, \u0026addr_client_len);\nif (sock_client \u003c 0)\n{\n    if (errno != EAGAIN \u0026\u0026 errno != EWOULDBLOCK)\n    {\n        report_error(\"Server accept() failed\");\n        break;\n    }\n    else\n    {\n        printf(\"No client connection\\n\");\n    }\n}\nelse\n{\n    set_non_blocking(sock_client);\n}\n```\n\nBecause server socket is non-blocking, the ```accept()``` call returns immediately. If there is no connection available, errno is set to ```EAGAIN``` or ```EWOULDBLOCK```.\n\n**Receiving client data**\n\n```\nint received_bytes = recv(sock_client, buffer, MESSAGE_SIZE, 0);\nif (received_bytes \u003c 0)\n{\n    if (errno != EAGAIN \u0026\u0026 errno != EWOULDBLOCK)\n    {\n        report_error(\"Server recv() failed\");\n        break;\n    }\n}\n```\n\nSocket of client connection is set to non-blocking mode as well, ```recv()``` does not block when no data is available. If no data is received, errno is set to ```EAGAIN``` or ```EWOULDBLOCK```.\n\n### Synchronous I/O Multiplexing with select()\n\nApplying non-blocking file descriptor technique to network sockets allows the server to accept multiple client connection at a time without the need of using multithreading.\n\nHowever, there is a limitation in the [previous sample code](https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming/blob/main/00_tutorials/11_non_blocking_sockets.c).\n\nThe Server Loop continuously checks for connections and data, which can lead to high CPU usage.\n\nTo address this, we can use I/O Multiplexing mechanism with the help of ```select()``` function.\n\n```\n#include \u003csys/select.h\u003e\n#include \u003csys/time.h\u003e\n#include \u003cunistd.h\u003e\n\nint select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);\n```\n\n**Parameters:**\n\n```nfds```: Specifies the range of file descriptors to be checked. This is usually set to the highest file descriptor + 1.\n\n```readfds```: A pointer to a set of file descriptors to monitor for readability. Use FD_SET() to add descriptors and FD_ISSET() to check them.\n\n```writefds```: A pointer to a set of file descriptors to monitor for writability.\n\n```exceptfds```: A pointer to a set of file descriptors to monitor for exceptional conditions.\n\n```timeout```: A pointer to a struct timeval that specifies the maximum time to wait. It can be: ```NULL```: Wait indefinitely. ```Zero timeout```: Non-blocking mode, checks the status immediately. ```Specific value```: Blocks for the specified duration.\n\n**Return Value:**\n\n```\u003e 0```: Number of file descriptors ready for I/O.\n\n```0```: Timeout occurred, no file descriptors are ready.\n\n```-1```: An error occurred, and errno is set appropriately.\n\nThe ```select()``` function in C is used for monitoring multiple file descriptors to see if they are ready for I/O operations such as reading, writing, or if there’s an exceptional condition. It’s commonly used in network programming for managing multiple sockets without multithreading.\n\n```\n#include \u003csys/select.h\u003e\n#include \u003csys/time.h\u003e\n#include \u003cunistd.h\u003e\n\nint select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);\n```\n\n```nfds```: The highest-numbered file descriptor + 1.\n\n```readfds```: Set of FDs to check for readability.\n\n```writefds```: Set of FDs to check for writability.\n\n```exceptfds```: Set of FDs to check for exceptional conditions.\n\n```timeout```: Maximum time ```select()``` should block, or ```NULL``` for indefinite blocking.\n\nCheckout the completed sample code using I/O Multiplexing with ```select()``` function [HERE](https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming/blob/main/00_tutorials/12_multiplexing_select_client_server.c).\n\n**Initialization of fd_set**\n\n```\nfd_set read_set;\nfd_set master_set;\nFD_ZERO(\u0026master_set);\nFD_SET(sock_server, \u0026master_set);\nglobal_max_fd = MAX(global_max_fd, sock_server);\n```\n\n```master_set``` keeps track of all file descriptors to monitor.\n\n```read_set``` is a temporary copy used by select() to determine which descriptors are ready for I/O.\n\n```global_max_fd``` variable is updated to the highest descriptor value for use in ```select()```.\n\n**I/O Multiplexing with select()**\n\n```\nread_set = master_set;\n\nint activity = select(global_max_fd + 1, \u0026read_set, NULL, NULL, NULL);\nif (activity \u003c 0)\n{\n    report_error(\"Server select() failed\");\n}\n```\n\n```select()``` monitors the file descriptors in read_set for readability. It blocks until at least one descriptor is ready for reading.\n\n**Handling Ready Descriptors**\n\n```\nfor (int i = 0; i \u003c= global_max_fd; i++)\n{\n    if (FD_ISSET(i, \u0026read_set))\n    {\n        if (i == sock_server)\n        {\n            // Handle new incoming connections\n        }\n        else\n        {\n            // Handle client I/O\n        }\n    }\n}\n```\n\n```FD_ISSET(i, \u0026read_set)``` checks if descriptor i is ready for reading.\n\nIf I/O is ready on the server socket, it has a new connection to accept. Otherwise, the descriptor corresponds to a client socket, and data can be read from it.\n\n**Accepting Server I/O**\n\n```\nstruct sockaddr addr_client;\nsocklen_t addr_client_len = sizeof(struct sockaddr);\nint sock_client = accept(sock_server, \u0026addr_client, \u0026addr_client_len);\nFD_SET(sock_client, \u0026master_set);\nglobal_max_fd = MAX(global_max_fd, sock_client);\n```\n\nAdds the new client socket (sock_client) to ```master_set``` for monitoring.\n\nUpdates ```global_max_fd``` if the new socket's value is higher.\n\n**Handling Client I/O**\n\n```\nint received_bytes = recv(i, request_buffer, MESSAGE_SIZE, 0);\nif (received_bytes \u003c= 0)\n{\n    // Client disconnected or error occurred\n    close(i);\n    FD_CLR(i, \u0026master_set);\n}\nelse\n{\n    // Process received data and send a response\n    send(i, response_buffer, strlen(response_buffer), 0);\n}\n```\n\nIf received_bytes \u003c= 0, the client either disconnected or an error occurred, then removes the socket from ```master_set```.\n\n### Synchronous I/O Multiplexing with poll()\n\n\u003e WARNING: select() can monitor only file descriptors numbers that are less than FD_SETSIZE (1024)—an unreasonably low limit for many modern applications—and this limitation will not change. All modern applications should instead use poll(2) or epoll(7), which do not suffer this limitation.\n\nSimilar to ```select()```, the ```poll()``` function provides a way to monitor multiple file descriptors for readiness to perform I/O operations. However, ```poll()``` overcomes some limitations of ```select()```, such as the fixed size of the file descriptor set.\n\nWith ```poll()```, the server can efficiently handle multiple connections without needing multithreading, while addressing high CPU usage in the server loop.\n\n```\n#include \u003cpoll.h\u003e\n#include \u003cunistd.h\u003e\n\nint poll(struct pollfd *fds, nfds_t nfds, int timeout);\n```\n\n**Parameters:**\n\n```fds```: A pointer to an array of struct pollfd, which represents the file descriptors to monitor.\n\n```nfds```: The number of file descriptors in the fds array.\n\n```timeout```: Specifies the maximum time to wait (in milliseconds). It can be: ```-1```: Wait indefinitely. ```0```: Return immediately (non-blocking mode). ```Positive value```: Block for the specified time.\n\n**Return Value:**\n\n```\u003e 0```: The number of file descriptors with events.\n\n```0```: Timeout occurred, no events detected.\n\n```-1```: An error occurred, and errno is set appropriately.\n\nThe ```poll()``` function is more scalable than ```select()``` for monitoring a large number of file descriptors. It is commonly used in network programming to manage multiple connections, enabling efficient I/O multiplexing.\n\nCheck out the complete code for poll() I/O multiplexing [HERE](https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming/blob/main/00_tutorials/13_multiplexing_poll_client_server.c).\n\n**Initialization of pollfd array**\n\n```\nstruct pollfd fds[MAX_CONNECTION];\nmemset(\u0026fds, 0, sizeof(fds));\nfds[0].fd = sock_server;    // Monitor server socket\nfds[0].events = POLLIN;     // Monitor for incoming connections\nint nfds = 1;               // Start with one monitored socket\n```\n\nThe server socket is the first entry in the ```pollfd``` array, which is dynamically updated as clients connect or disconnect.\n\n**Server Loop with poll()**\n\n```\nint activity = poll(fds, nfds, -1);  // Wait indefinitely\nif (activity \u003c 0)\n{\n    report_error(\"Server poll() failed\");\n    break;\n}\n```\n\nThe loop continuously monitors the file descriptors and handles events as they occur.\n\n**Handle Server socket ready to read event**\n\n```\nif (fds[0].revents \u0026 POLLIN)\n{\n    struct sockaddr addr_client;\n    socklen_t addr_client_len = sizeof(struct sockaddr);\n    int sock_client = accept(fds[0].fd, \u0026addr_client, \u0026addr_client_len);\n    if (nfds \u003c MAX_CONNECTION)\n    {\n        fds[nfds].fd = sock_client;\n        fds[nfds].events = POLLIN;  // Monitor for incoming data\n        nfds++;\n    }\n}\n```\n\nEach new connection is added to the ```pollfd``` array, and the total monitored descriptors ```nfds``` is incremented.\n\n**Handle client I/O**\n\n```\nfor (int i = 1; i \u003e= 1 \u0026\u0026 i \u003c nfds; i++)\n{\n    if (fds[i].revents \u0026 POLLIN)\n    {\n        int received_bytes = recv(fds[i].fd, request_buffer, MESSAGE_SIZE, 0);\n        if (received_bytes \u003c= 0)\n        {\n            close(fds[i].fd);\n            fds[i].fd = fds[nfds - 1].fd;  // Replace with the last descriptor\n            nfds--;                        // Reduce the total count\n            i--;\n        }\n        else\n        {\n            sprintf(response_buffer, \"Server time: %ld\", time(NULL));\n            send(fds[i].fd, response_buffer, strlen(response_buffer), 0);\n        }\n    }\n}\n```\n\nReceives data from the client.\n\nSends a response or disconnects if necessary.\n\nCleans up the ```pollfd``` array after disconnections by replacing the closed descriptor with the last one and reducing the monitored count.\n\n### Broadcasting Messages\n\nBroadcasting is a method in networking where a message is sent from one computer (called the sender) to all computers (called receivers) within the same network. This is like one person shouting a message in a room so that everyone in the room hears it (including yourself). The most common address for broadcasting is ```255.255.255.255```.\n\nThe full source code that demonstrate broadcasting socket can be found [HERE](https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming/blob/main/00_tutorials/14_broadcasting.c).\n\n**Setup broadcast receiver socket**\n\n```\nint setup_broadcast_receiver(struct broadcast_t* receiver_info)\n{\n    int rc;\n\n    receiver_info-\u003efd = socket(AF_INET, SOCK_DGRAM, 0);\n    if (receiver_info-\u003efd \u003c 0)\n    {\n        report_error(\"socket() failed for receiver\");\n        return -1;\n    }\n\n    int optval = 1;\n    rc = setsockopt(receiver_info-\u003efd, SOL_SOCKET, SO_REUSEADDR, \u0026optval, sizeof(optval));\n    if (rc != 0)\n    {\n        report_error(\"setsockopt(SO_REUSEADDR) failed\");\n        return -1;\n    }\n\n    receiver_info-\u003eaddr_receiver.sin_family = AF_INET;\n    receiver_info-\u003eaddr_receiver.sin_port = htons(BROADCAST_PORT);\n    receiver_info-\u003eaddr_receiver.sin_addr.s_addr = htonl(INADDR_ANY);\n    receiver_info-\u003eaddr_receiver_len = sizeof(receiver_info-\u003eaddr_receiver);\n\n    rc = bind(receiver_info-\u003efd, (struct sockaddr *)\u0026receiver_info-\u003eaddr_receiver, receiver_info-\u003eaddr_receiver_len);\n    if (rc \u003c 0)\n    {\n        report_error(\"bind() failed for receiver\");\n        return -1;\n    }\n\n    return 0;\n}\n```\n\n```socket()```: Creates a UDP socket (```SOCK_DGRAM```) for communication.\n\n```setsockopt()```: Configures the socket with ```SO_REUSEADDR``` option to allow binding the socket to an address that is already in use.\n\n```bind()```: Binds the socket to a specific port (```BROADCAST_PORT```) on the local machine. It listens for messages sent to this port.\n\nThis receiver is set up to receive broadcast messages sent to the ```BROADCAST_PORT``` (defined as ```5555```).\n\n**Setup broadcast sender socket**\n\n```\nint setup_broadcast_sender(struct broadcast_t* sender_info)\n{\n    int rc;\n\n    sender_info-\u003efd = socket(AF_INET, SOCK_DGRAM, 0);\n    if (sender_info-\u003efd \u003c 0)\n    {\n        report_error(\"socket() failed for sender\");\n        return -1;\n    }\n\n    int optval = 1;\n    rc = setsockopt(sender_info-\u003efd, SOL_SOCKET, SO_BROADCAST, \u0026optval, sizeof(optval));\n    if (rc != 0)\n    {\n        report_error(\"setsockopt(SO_BROADCAST) failed\");\n        return -1;\n    }\n\n    sender_info-\u003eaddr_receiver.sin_family = AF_INET;\n    sender_info-\u003eaddr_receiver.sin_port = htons(BROADCAST_PORT);\n    inet_pton(AF_INET, BROADCAST_ADDR, \u0026sender_info-\u003eaddr_receiver.sin_addr);\n    sender_info-\u003eaddr_receiver_len = sizeof(sender_info-\u003eaddr_receiver);\n\n    return 0;\n}\n```\n\n```socket()```: Creates a UDP socket (```SOCK_DGRAM```) for broadcasting.\n\n```setsockopt()```: Configures the socket to allow broadcasting with the ```SO_BROADCAST``` option.\n\n```inet_pton()```: Converts the broadcast IP address (```255.255.255.255```) from text to binary format to be used in the socket.\n\nThe sender is set to send broadcast messages to the specified address and port.\n\n**Run receiver thread**\n\n```\nvoid* broadcast_receiver_thread_func(void* arg)\n{\n    struct broadcast_t* broadcast_receiver_info = (struct broadcast_t*)calloc(1, sizeof(struct broadcast_t));\n    if (setup_broadcast_receiver(broadcast_receiver_info) != 0)\n    {\n        report_error(\"setup_broadcast_receiver() failed\");\n        return NULL;\n    }\n\n    char buffer[MESSAGE_SIZE];\n\n    printf(\"Start to listen broadcast messages\\n\");\n    while (1)\n    {\n        memset(buffer, 0, MESSAGE_SIZE);\n        int received_bytes = recvfrom(broadcast_receiver_info-\u003efd, buffer, MESSAGE_SIZE, 0, (struct sockaddr*)\u0026broadcast_receiver_info-\u003eaddr_receiver, \u0026broadcast_receiver_info-\u003eaddr_receiver_len);\n        if (received_bytes \u003c= 0)\n        {\n            report_error(\"Broadcast receiver recvfrom() failed\");\n        }\n        else\n        {\n            printf(\"Received broadcast message: %s\\n\", buffer);\n        }\n    }\n\n    close(broadcast_receiver_info-\u003efd);\n    free(broadcast_receiver_info);\n\n    return NULL;\n}\n```\n\nThe function ```broadcast_receiver_thread_func``` runs in a separate thread.\n\nIt first calls ```setup_broadcast_receiver()``` to set up the receiver socket.\n\nThen, it listens for incoming messages using ```recvfrom()```. Each received message is printed to the console.\n\nThe ```recvfrom()``` function reads the broadcast message into the buffer and prints it. If there is an error or no data is received, it reports the issue.\n\nThe receiver thread will continue to listen until the program is terminated.\n\n**Run sender thread**\n\n```\nvoid* broadcast_sender_thread_func(void* arg)\n{\n    char* nick_name = (char*)arg;\n\n    struct broadcast_t* broadcast_sender_info = (struct broadcast_t*)calloc(1, sizeof(struct broadcast_t));\n    if (setup_broadcast_sender(broadcast_sender_info) != 0)\n    {\n        report_error(\"setup_broadcast_sender() failed\");\n        return NULL;\n    }\n\n    char broadcast_message[MESSAGE_SIZE];\n    while (1)\n    {\n        memset(broadcast_message, 0, MESSAGE_SIZE);\n        sprintf(broadcast_message, \"%s is active\", nick_name);\n        int sent_bytes = sendto(broadcast_sender_info-\u003efd, broadcast_message, MESSAGE_SIZE, 0, (struct sockaddr*)\u0026broadcast_sender_info-\u003eaddr_receiver, broadcast_sender_info-\u003eaddr_receiver_len);\n        if (sent_bytes \u003c= 0)\n        {\n            report_error(\"Send broadcast message failed\");\n        }\n        sleep(1);\n    }\n\n    close(broadcast_sender_info-\u003efd);\n    free(broadcast_sender_info);\n\n    return NULL;\n}\n```\n\nThe function ```broadcast_sender_thread_func``` is responsible for sending broadcast messages to the broadcast address (```255.255.255.255```).\n\nIt sets up the sender socket by calling ```setup_broadcast_sender()```.\n\nInside a loop, it creates a message string containing the user's nickname and sends it via the ```sendto()``` function to the broadcast address every second.\n\n## Create A Simple HTTP Server\n\n![HTTP Server class diagram](https://raw.githubusercontent.com/nguyenchiemminhvu/LinuxNetworkProgramming/main/01_networking_libraries/my_http_server/http_server_design.png)\n\nFull source code of my simple HTTP server is found [HERE](https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming/tree/main/01_networking_libraries/my_http_server).\n\nThis is a simple ```HTTP``` server written in ```C++``` using Linux socket programming. The server is designed to handle basic ```HTTP``` requests and responses. It listens for incoming connections, processes the requests, and sends back an appropriate response.\n\n### Demo\n\n**Server side:**\n\n```\nncmv@localhost:~/study_workspace/LinuxNetworkProgramming/01_networking_libraries/my_http_server/build$ cmake ..\n\nncmv@localhost:~/study_workspace/LinuxNetworkProgramming/01_networking_libraries/my_http_server/build$ make\n\nncmv@localhost:~/study_workspace/LinuxNetworkProgramming/01_networking_libraries/my_http_server/build$ ./HTTPServer 8080\n\n[1734346074] [INFO] 127.0.0.1:8080\n[1734346074] [INFO] Server starts new poll()\n[1734346086] [INFO] A client is connected\n[1734346086] [INFO] 127.0.0.1:48146\n[1734346086] [INFO] A client is disconnected\n[1734346086] [INFO] Server starts new poll()\n```\n\n**Client side:**\n\n```\nncmv@localhost:~/study_workspace/LinuxNetworkProgramming/01_networking_libraries/my_http_server/build$ curl -I http://localhost:8080\n\nHTTP/1.1 200 OK\nContent-Length:1544\nContent-Length: 1544\n```\n\n```\nncmv@localhost:~/study_workspace/LinuxNetworkProgramming/01_networking_libraries/my_http_server/build$ curl http://localhost:8080\n\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n    \u003cmeta charset=\"UTF-8\"\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\u003e\n    \u003ctitle\u003eMain Page\u003c/title\u003e\n    \u003cstyle\u003e\n        body {\n            font-family: Arial, sans-serif;\n            background-color: #f4f4f4;\n            color: #333;\n            margin: 0;\n            padding: 0;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            height: 100vh;\n        }\n        h1 {\n            font-size: 2.5em;\n            color: #333;\n            margin-bottom: 20px;\n        }\n        ul {\n            list-style-type: none;\n            padding: 0;\n        }\n        li {\n            margin: 10px 0;\n        }\n        a {\n            text-decoration: none;\n            font-size: 1.2em;\n            color: #007bff;\n            padding: 10px 15px;\n            border: 1px solid #007bff;\n            border-radius: 5px;\n            transition: background-color 0.3s, color 0.3s;\n        }\n        a:hover {\n            background-color: #007bff;\n            color: white;\n        }\n    \u003c/style\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n    \u003ch1\u003eMain Page\u003c/h1\u003e\n    \u003cp\u003eSelect a folder to view its contents:\u003c/p\u003e\n    \u003cul\u003e\n        \u003cli\u003e\u003ca href=\"./200\"\u003e200 - OK\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"./400\"\u003e400 - Bad Request\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"./403\"\u003e403 - Forbidden\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"./404\"\u003e404 - Not Found\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"./500\"\u003e500 - Internal Server Error\u003c/a\u003e\u003c/li\u003e\n    \u003c/ul\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n# Networking Libraries\n\n## Using libcurl\n\n[libcurl](https://curl.se/libcurl/) is a widely-used and powerful C library designed for transferring data over networks using a wide variety of protocols. It is the library behind the popular ```curl``` command-line tool and provides developers with a programmatic way to send and receive data through ```HTTP```, ```HTTPS```, ```FTP```, and other protocols.\n\nUsing libcurl is ideal for tasks that involve fetching web pages, uploading files to servers, interacting with ```REST APIs```, or sending emails... It saves time and effort because it eliminates the need to deal with low-level socket programming and protocol parsing. Instead of manually implementing low-level socket operations and parsing protocols, we can rely on libcurl to do the heavy lifting (creating network connections, handling requests, and managing data streams...).\n\n### curl command-line examples\n\nFetches the content of http://example.com and saves it into a file called ```temp.txt```.\n\n```\ncurl http://example.com \u003e temp.txt\n```\n\n----\n\nDownloads the content of http://example.com and saves it as ```index.html```.\n\n```\ncurl http://example.com -o index.html\n```\n\n----\n\nDownloads a file called ```file.zip``` from http://example.com\n\n```\ncurl -O http://example.com/file.zip\n```\n\n----\n\nSends a ```POST``` request to http://example.com with data \"name=ncmv\".\n\n```\ncurl -X POST -d \"name=ncmv\" http://example.com\n```\n\n----\n\nSends a ```POST``` request to http://example.com with ```JSON``` data ({\"name\":\"John\",\"age\":30}).\n\n```\ncurl -X POST -H \"Content-Type: application/json\" -d '{\"name\":\"John\",\"age\":30}' http://example.com\n```\n\n----\n\nFetches only the headers of the ```HTTP``` response from http://example.com\n\n```\ncurl -I http://example.com\n```\n\n----\n\nAccesses http://example.com using ```HTTP``` Basic Authentication with the username ```username``` and password ```password```.\n\n```\ncurl -u username:password http://example.com\n```\n\n----\n\nDownloads the file ```readme.txt``` from the ```FTP``` server ```test.rebex.net``` using the username ```demo``` and password ```password```.\n\n```\ncurl ftp://test.rebex.net/readme.txt --user demo:password\n```\n\n----\n\nUploads the local file temp to the ```FTP``` server ```test.rebex.net``` using the username ```demo``` and password ```password```.\n\n```\ncurl -T temp ftp://test.rebex.net/ --user demo:password\n```\n\n----\n\nUploads the local file temp to the ```SFTP``` server at ```localhost``` into the folder ```/home/ncmv/study_workspace/``` using the username ```demo``` and password ```password```.\n\n```\ncurl -u demo:passowrd -T temp sftp://localhost/home/ncmv/study_workspace/\n```\n\n----\n\nDownloads the file temp from the ```SFTP``` server ```localhost``` (in the folder ```/home/ncmv/study_workspace/```) using the username ```demo``` and password ```password```, and saves it locally as ````temp````.\n\n```\ncurl -u demo:password sftp://localhost/home/ncmv/study_workspace/temp -O temp\n```\n\n### Basic Curl\n\n**Include necessary headers**\n\n```\n#include \u003ciostream\u003e\n#include \u003ccurl/curl.h\u003e\n```\n\n**Callback function for receiving HTTP response**\n\n```\nsize_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)\n{\n    size_t total_size = size * nmemb;\n    ((std::string*)userp)-\u003eappend((char*)contents, total_size);\n    return total_size;\n}\n```\n\nPurpose: This function handles data received from the server during the HTTP request.\n\nParameters:\n\n- ```contents```: A pointer to the data received.\n- ```size``` and ```nmemb```: Together, they specify the size of the received data (in bytes).\n- ```userp```: A user-provided pointer to store the received data (in this case, a std::string).\n\nWhat it does:\n\n- Calculates the total size of the data: size * nmemb.\n- Appends the received data (converted to a string) to the ```std::string``` object passed in ```userp```.\n- Returns the total size of the data to let libcurl know how much data was processed.\n\n**Check libcurl version**\n\n```\ncurl_version_info_data* info = curl_version_info(CURLVERSION_NOW);\nif (info)\n{\n    std::cout \u003c\u003c \"libcurl version: \" \u003c\u003c info-\u003eversion \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"SSL version: \" \u003c\u003c info-\u003essl_version \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"Libz version: \" \u003c\u003c info-\u003elibz_version \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"Features: \" \u003c\u003c info-\u003efeatures \u003c\u003c std::endl;\n\n    const char *const *protocols = info-\u003eprotocols;\n    if (protocols)\n    {\n        std::cout \u003c\u003c \"Supported protocols: \";\n        for (int i = 0; protocols[i] != NULL; ++i)\n        {\n            std::cout \u003c\u003c protocols[i] \u003c\u003c \" \";\n        }\n        std::cout \u003c\u003c std::endl;\n    }\n}\n```\n\nPurpose: Displays the version information and features supported by ```libcurl```.\n\nHow it works:\n\n- Calls ```curl_version_info(CURLVERSION_NOW)``` to get information about the current version of ```libcurl```.\n- Prints the version, ```SSL``` support, compression library (```Libz```), and the supported protocols (```HTTP```, ```HTTPS```, ```FTP```, ...).\n\n**Initialize libcurl**\n\n```\nCURL *curl;\nCURLcode res;\nstd::string readBuffer;\n\ncurl = curl_easy_init();\n```\n\nDetails:\n\n- ```CURL *curl```: A handle to manage the HTTP session.\n- ```curl_easy_init()```: Initializes the handle. If successful, curl will not be NULL.\n\n**Set libcurl options**\n\n```\ncurl_easy_setopt(curl, CURLOPT_URL, \"http://httpstat.us/200\");\ncurl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);\ncurl_easy_setopt(curl, CURLOPT_WRITEDATA, \u0026readBuffer);\n```\n\nPurpose: Configures options for the HTTP request.\n\nOptions:\n\n- ```CURLOPT_URL```: Sets the URL to request.\n- ```CURLOPT_WRITEFUNCTION```: Specifies the callback function (```WriteCallback```) to handle the response data.\n- ```CURLOPT_WRITEDATA```: Provides the ```std::string``` object (```readBuffer```) where the response data will be stored.\n\n**Perform HTTP request**\n\n```\nres = curl_easy_perform(curl);\n```\n\n```curl_easy_perform(curl)```: Executes the HTTP request with the options set earlier.\n\n**Clean up**\n\n```\ncurl_easy_cleanup(curl);\n```\n\nFrees resources used by ```curl```. Always call this after finishing with ```curl```.\n\n**Result**:\n\n```\nncmv@localhost:~/study_workspace/LinuxNetworkProgramming/01_networking_libraries/libcurl/build$ ./basic_curl \n\nlibcurl version: 8.5.0\nSSL version: OpenSSL/3.0.13\nLibz version: 1.3\nFeatures: 1438599069\nSupported protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtmpe rtmps rtmpt rtmpte rtmpts rtsp scp sftp smb smbs smtp smtps telnet tftp \n\nResponse data: 200 OK\n```\n\nFull source code of basic curl example [HERE](https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming/blob/main/01_networking_libraries/libcurl/src/basic_curl.cpp).\n\n### Curl Multiple Handles\n\n**Include necessary headers**\n\n```\n#include \u003ciostream\u003e\n#include \u003cfstream\u003e\n#include \u003cstring\u003e\n#include \u003cvector\u003e\n#include \u003ccurl/curl.h\u003e\n```\n\n**Define a Easy Handle struct**\n\n```\nstruct CurlEasyHandle\n{\n    CURL* easy_handle;\n    std::string url;\n    std::string data;\n};\n```\n\nPurpose: Stores information for each HTTP request.\n\n- ```CURL* easy_handle```: A handle for making a single HTTP request.\n- ```std::string url```: The URL to fetch.\n- ```std::string data```: Stores the HTTP response data.\n\n**Callback function for receiving HTTP response**\n\n```\nstd::size_t perform_callback(char* ptr, std::size_t size, std::size_t nmemb, void* userdata)\n{\n    std::string* str = static_cast\u003cstd::string*\u003e(userdata);\n    std::size_t total_size = size * nmemb;\n    str-\u003eappend(ptr, total_size);\n    return total_size;\n}\n```\n\nPurpose: Handles the data received from the server.\n\nHow it works:\n\n- Calculates the total size of the received data: ```size * nmemb```.\n- Appends this data to the ```std::string``` object pointed to by ```userdata```.\n- Returns the total size of processed data to let ```libcurl``` know the data was handled.\n\n**Callback function for downloading progress**\n\n```\nint perform_progress(void* ptr, double download_size, double downloaded, double upload_size, double uploaded)\n{\n    CurlEasyHandle* progData = (CurlEasyHandle*)ptr;\n    std::cout \u003c\u003c \"Downloaded \" \u003c\u003c progData-\u003eurl \u003c\u003c \": \" \u003c\u003c downloaded \u003c\u003c \" bytes\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\nPurpose: Tracks the download progress for each URL.\n\nHow it works:\n\n- Prints the number of bytes downloaded for the URL.\n- Returning ```0``` signals ```libcurl``` to continue the download.\n- Returning non-zero would stop it.\n\n**Define a list of URLs**\n\n```\nconst std::vector\u003cstd::string\u003e urls = {\n    \"http://www.example.com\",\n    \"http://www.google.com\",\n    \"http://www.bing.com\",\n    \"http://www.speedtest.net\",\n};\n```\n\n**Initialize libcurl**\n\n```\nCURLM* curl_multi;\nint running_status;\n\ncurl_global_init(CURL_GLOBAL_DEFAULT);\ncurl_multi = curl_multi_init();\n```\n\nPurpose: Prepares libcurl for multi-handle operations.\n\nDetails:\n\n- ```curl_global_init()```: Initializes global resources for libcurl.\n- ```curl_multi_init()```: Creates a multi-handle for managing multiple simultaneous HTTP requests.\n\n**Create Easy Handles and add to Multi Handle**\n\n```\nstd::vector\u003cCurlEasyHandle\u003e easy_handles(urls.size());\nfor (int i = 0; i \u003c urls.size(); i++)\n{\n    easy_handles[i].easy_handle = curl_easy_init();\n    easy_handles[i].url = urls[i];\n\n    curl_easy_setopt(easy_handles[i].easy_handle, CURLOPT_URL, urls[i].c_str());\n    curl_easy_setopt(easy_handles[i].easy_handle, CURLOPT_WRITEFUNCTION, perform_callback);\n    curl_easy_setopt(easy_handles[i].easy_handle, CURLOPT_WRITEDATA, \u0026easy_handles[i].data);\n    curl_easy_setopt(easy_handles[i].easy_handle, CURLOPT_NOPROGRESS, 0L);\n    curl_easy_setopt(easy_handles[i].easy_handle, CURLOPT_PROGRESSFUNCTION, perform_progress);\n    curl_easy_setopt(easy_handles[i].easy_handle, CURLOPT_PROGRESSDATA, \u0026easy_handles[i]);\n\n    curl_multi_add_handle(curl_multi, easy_handles[i].easy_handle);\n}\n```\n\nPurpose: Creates and configures an easy handle for each URL, then adds it to the multi-handle.\n\nSteps:\n\n- Initialize a new easy handle using ```curl_easy_init()```.\n- Configure each handle with:\n  - The URL to fetch (```CURLOPT_URL```).\n  - A callback for handling response data (```CURLOPT_WRITEFUNCTION```).\n  - A pointer to the data storage (```CURLOPT_WRITEDATA```).\n  - Progress monitoring options (```CURLOPT_NOPROGRESS```, ```CURLOPT_PROGRESSFUNCTION```, ```CURLOPT_PROGRESSDATA```).\n- Add the easy handle to the multi-handle with ```curl_multi_add_handle()```.\n\n**Perform Multi Handle request**\n\n```\ncurl_multi_perform(curl_multi, \u0026running_status);\n\ndo\n{\n    int curl_multi_fds;\n    CURLMcode rc = curl_multi_perform(curl_multi, \u0026running_status);\n    if (rc == CURLM_OK)\n    {\n        rc = curl_multi_wait(curl_multi, nullptr, 0, 1000, \u0026curl_multi_fds);\n    }\n\n    if (rc != CURLM_OK)\n    {\n        std::cerr \u003c\u003c \"curl_multi failed, code \" \u003c\u003c rc \u003c\u003c std::endl;\n        break;\n    }\n} while (running_status);\n```\n\nPurpose: Executes all HTTP requests simultaneously.\n\nHow it works:\n\n- Starts the ```HTTP``` requests with ```curl_multi_perform()```.\n- Continuously calls ```curl_multi_perform()``` in a loop until all requests are complete (running_status becomes 0).\n- Uses ```curl_multi_wait()``` to wait for events (data availability) to avoid busy-waiting.\n\n**Save data and clean up**\n\n```\nfor (CurlEasyHandle\u0026 handle : easy_handles)\n{\n    std::string filename = handle.url.substr(11, handle.url.find_last_of(\".\") - handle.url.find_first_of(\".\") - 1) + \".html\";\n    std::ofstream file(filename);\n    if (file.is_open())\n    {\n        file \u003c\u003c handle.data;\n        file.close();\n        std::cout \u003c\u003c \"Data written to \" \u003c\u003c filename \u003c\u003c std::endl;\n    }\n\n    curl_multi_remove_handle(curl_multi, handle.easy_handle);\n    curl_easy_cleanup(handle.easy_handle);\n}\n\ncurl_multi_cleanup(curl_multi);\ncurl_global_cleanup();\n```\n\nPurpose: Saves the response data to files, then cleans up resources.\n\nSteps:\n\n- For each handle:\n  - Create a filename based on the ```URL```.\n  - Save the response data to the file.\n  - Remove the handle from the multi-handle (```curl_multi_remove_handle()```).\n- Clean up the handle (```curl_easy_cleanup()```).\n- Clean up the multi-handle (```curl_multi_cleanup()```) and global resources (```curl_global_cleanup()```).\n\n**Result**:\n\n```\nncmv@localhost:~/study_workspace/LinuxNetworkProgramming/01_networking_libraries/libcurl/build$ ./curl_multi_handle \n\n...\nDownloaded http://www.speedtest.net: 167 bytes\n...\nDownloaded http://www.bing.com: 53057 bytes\n...\nDownloaded http://www.google.com: 57709 bytes\n...\nDownloaded http://www.example.com: 1256 bytes\n...\nData written to example.html\nData written to google.html\nData written to bing.html\nData written to speedtest.html\n```\n\nFull source code of curl multiple handles example [HERE](https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming/blob/main/01_networking_libraries/libcurl/src/curl_multi_handle.cpp).\n\n### Curl Multithreading\n\n**Include necessary headers**\n\n```\n#include \u003ciostream\u003e\n#include \u003cthread\u003e\n#include \u003cvector\u003e\n#include \u003cfstream\u003e\n#include \u003ccurl/curl.h\u003e\n```\n\n**Define structures**\n\n```\nstruct ProgressData\n{\n    std::string url;\n    double lastProgress;\n};\n```\n\nPurpose: Stores progress information for each download.\n\nMembers:\n\n- ```std::string url```: The URL being downloaded.\n- ```double lastProgress```: The last recorded progress (in bytes) for this URL.\n\n**Callback function for receiving HTTP response**\n\n```\nstd::size_t perform_callback(char* ptr, std::size_t size, std::size_t nmemb, void* userdata)\n{\n    std::string* str = static_cast\u003cstd::string*\u003e(userdata);\n    std::size_t total_size = size * nmemb;\n    str-\u003eappend(ptr, total_size);\n    return total_size;\n}\n```\n\nPurpose: Handles data received from the server during an ```HTTP``` request.\n\nDetails:\n\n- Appends received data to a ```std::string``` provided as userdata.\n- Returns the size of the data to confirm successful processing.\n\n**Callback function for downloading progress**\n\n```\nint perform_progress(void* ptr, double download_size, double downloaded, double upload_size, double uploaded)\n{\n    ProgressData* progData = (ProgressData*)ptr;\n\n    if (downloaded - progData-\u003elastProgress \u003e= 1024.0)\n    {\n        std::cout \u003c\u003c \"Download \" \u003c\u003c progData-\u003eurl \u003c\u003c \": \" \u003c\u003c downloaded \u003c\u003c \" bytes\" \u003c\u003c std::endl;\n        progData-\u003elastProgress = downloaded;\n    }\n\n    return 0;\n}\n```\n\nPurpose: Tracks and displays download progress for a specific URL.\n\nDetails:\n\n- Checks if at least 1 KB (1024 bytes) of new data has been downloaded since the last update.\n- Prints the progress and updates lastProgress.\n\n**Function to perform HTTP request**\n\n```\nvoid perform_request(const std::string\u0026 url)\n{\n    CURL* curl;\n    CURLcode res;\n\n    curl = curl_easy_init();\n    if (curl != nullptr)\n    {\n        std::string data;\n        ProgressData progData;\n        progData.url = url;\n        progData.lastProgress = 0.0;\n\n        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());\n        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);\n        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, perform_callback);\n        curl_easy_setopt(curl, CURLOPT_WRITEDATA, \u0026data);\n        curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);\n        curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, perform_progress);\n        curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, \u0026progData);\n\n        res = curl_easy_perform(curl);\n        if (res == CURLE_OK)\n        {\n            std::string filename = url.substr(11, url.find_last_of(\".\") - url.find_first_of(\".\") - 1) + \".html\";\n            std::ofstream file(filename);\n            if (file.is_open())\n            {\n                file \u003c\u003c data;\n                file.close();\n                std::cout \u003c\u003c \"Data written to \" \u003c\u003c filename \u003c\u003c std::endl;\n            }\n        }\n    }\n}\n```\n\nPurpose: Performs an ```HTTP``` request to download the content of a given ```URL```.\n\nSteps:\n\n- Initializes a CURL easy handle.\n- Sets up callbacks for data writing (```perform_callback```) and progress tracking (```perform_progress```).\n- Executes the request using ```curl_easy_perform()```.\n\nOn success:\n\n- Saves the downloaded data to a file named after the ```URL```.\n\n**Set up multithreading HTTP perform**\n\n```\ncurl_global_init(CURL_GLOBAL_ALL);\n\nstd::vector\u003cstd::thread\u003e threads;\nstd::vector\u003cstd::string\u003e urls = {\n    \"http://www.example.com\",\n    \"http://www.google.com\",\n    \"http://www.bing.com\",\n    \"http://www.speedtest.net\",\n};\n\nfor (const std::string\u0026 url : urls)\n{\n    threads.push_back(std::thread(perform_request, url));\n}\n\nfor (std::thread\u0026 t : threads)\n{\n    t.join();\n}\n\ncurl_global_cleanup();\n```\n\nDetails:\n\n- ```curl_global_init(CURL_GLOBAL_ALL)```: Prepares libcurl for multi-threaded operations.\n- Creates a vector to store thread objects and another to store the list of URLs.\n- For each URL, creates a new thread to execute ```perform_request()```.\n- Ensures all threads complete before the program exits using ```join()``` method.\n- ```curl_global_cleanup()```: Releases resources allocated by ```libcurl```.\n\nFull source code of basic curl multithreading [HERE](https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming/blob/main/01_networking_libraries/libcurl/src/curl_multithreaded.cpp).\n\n## Secure Networking with OpenSSL\n\n```SSL``` (Secure Sockets Layer) is a cryptographic protocol originally designed to provide secure communication over a network, such as the internet. It ensures that the data transferred between a client (e.g., a web browser) and a server (e.g., a website) is encrypted, authenticated, and protected from being tampered with.\n\nModern systems use ```TLS``` (Transport Layer Security), which is an updated and more secure version of ```SSL```. When people say ```SSL```, they often mean ```SSL/TLS```.\n\nOne of well-known ```SSL/TLS``` application is HTTPs protocol. TLS encryption method is used to secure communication on the web, such as browsing, submit forms, online payments...\n\n**SSL Handshake:**\n\n- The client (e.g., a browser) connects to the server and says, \"I want to use SSL/TLS.\"\n- The server sends back its certificate, which contains its identity and a public key.\n- The client verifies the server's certificate to ensure it’s legitimate.\n- The client and server agree on a shared \"session key\" to encrypt the data during the session.\n\n![How HTTPs work](https://raw.githubusercontent.com/nguyenchiemminhvu/LinuxNetworkProgramming/refs/heads/main/how_https_work.png)\n\nTo work with ```SSL/TLS``` protocol in programming, ```OpenSSL``` is a typical choice.\n\n**Installation**\n\n```\nsudo apt-get install libssl-dev openssl\n```\n\n```libssl-dev```: Contains the development libraries for ```OpenSSL```, which are needed to compile programs using ```OpenSSL```.\n\n```openssl```: Installs the ```OpenSSL``` command-line tool, which can be used for generating keys and certificates or debugging ```SSL/TLS``` issues.\n\n**Initialize OpenSSL**\n\n```\n#include \u003copenssl/ssl.h\u003e\n#include \u003copenssl/err.h\u003e\n\nSSL_library_init();\nSSL_load_error_strings();\nOpenSSL_add_all_algorithms();\n```\n\nThese steps ensure that ```OpenSSL``` is ready to handle cryptography and provide meaningful error messages in case something goes wrong.\n\n**Create SSL context**\n\n```\nSSL_CTX *ctx = SSL_CTX_new(TLS_server_method());  // For server\n\nSSL_CTX *ctx = SSL_CTX_new(TLS_client_method());  // For client\n```\n\n```TLS_server_method()```: Configures the context for use in server mode.\n\n```TLS_client_method()```: Configures the context for use in client mode.\n\nThe ```SSL_CTX``` structure holds protocol settings, certificates, and other necessary configurations.\n\n**Load Certificates (only for server)**\n\n```\nSSL_CTX_use_certificate_file(ctx, \"server.crt\", SSL_FILETYPE_PEM);\n\nSSL_CTX_use_PrivateKey_file(ctx, \"server.key\", SSL_FILETYPE_PEM);\n```\n\n```server.crt```: The server's certificate file (proves the server's identity to the client).\n\n```server.key```: The private key file associated with the certificate.\n\nThis step ensures that the server can provide authentication during the ```SSL/TLS ```handshake.\n\n**Create and Bind socket**\n\nSet up a regular ```TCP``` socket as you would for normal network programming.\n\n**Wrap socket with SSL**\n\n```\nSSL *ssl = SSL_new(ctx);\nSSL_set_fd(ssl, socket_fd);\n```\n\nThe ```SSL``` object manages the encryption and decryption for the socket connection.\n\n**Perform Handshake**\n\n```\nSSL_accept(ssl); // For server\n\nSSL_connect(ssl); // For client\n```\n\nThe ```SSL/TLS``` handshake authenticates the server (and optionally the client) and establishes an encrypted communication channel.\n\n```SSL_accept()```: The server waits for the client to initiate the handshake.\n\n```SSL_connect()```: The client initiates the handshake with the server.\n\n**Send and receive encrypted data**\n\nAfter the handshake, the ```SSL``` connection is ready to send and receive encrypted data.\n\n```\nSSL_write(ssl, \"Hello, Secure World!\", strlen(\"Hello, Secure World!\"));\nchar buffer[1024];\n\nSSL_read(ssl, buffer, sizeof(buffer));\n```\n\n**Cleanup**\n\n```\nSSL_shutdown(ssl);\nSSL_free(ssl);\nSSL_CTX_free(ctx);\n```\n\nWith all these steps, It is enough to establish secure communication between a client and server using the ```SSL/TLS``` protocol with ```OpenSSL``` library.\n\n### A HTTPS Client\n\nFull source code of the example HTTPs Client is found [HERE](https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming/blob/main/01_networking_libraries/openssl/src/https_client.c).\n\n**Result**:\n\n```\nncmv@localhost:~/study_workspace/LinuxNetworkProgramming/01_networking_libraries/openssl/build$ ./https_client example.com 443\n\n93.184.215.14:443\nSSL connection is done with cipher suite TLS_AES_256_GCM_SHA384\n\nReceived 361 bytes\nReceived 1256 bytes\nHTTP/1.1 200 OK\nAge: 140532\nCache-Control: max-age=604800\nContent-Type: text/html; charset=UTF-8\nDate: Sat, 14 Dec 2024 09:44:47 GMT\nEtag: \"3147526947+gzip+ident\"\nExpires: Sat, 21 Dec 2024 09:44:47 GMT\nLast-Modified: Thu, 17 Oct 2019 07:18:26 GMT\nServer: ECAcc (sed/58B0)\nVary: Accept-Encoding\nX-Cache: HIT\nContent-Length: 1256\nConnection: close\n\n(The remaining is HTTP content of example.com website)\n```\n\n### Secure Client-Server\n\n| SSL Server Workflow             | SSL Client Workflow             |\n|----------------------------------|----------------------------------|\n| ![SSL Server Workflow](https://raw.githubusercontent.com/nguyenchiemminhvu/LinuxNetworkProgramming/refs/heads/main/SSL_server_workflow.png) | ![SSL Client Workflow](https://raw.githubusercontent.com/nguyenchiemminhvu/LinuxNetworkProgramming/refs/heads/main/SSL_client_workflow.png) |\n\nFull source code of the example SSL Client-Server is found [HERE](https://github.com/nguyenchiemminhvu/LinuxNetworkProgramming/blob/main/01_networking_libraries/openssl/src/ssl_client_server.c).\n\n# Conclusion\n\n**Reference**:\n\nhttps://www.linuxhowtos.org/C_C++/socket.htm\n\nhttps://www.tutorialspoint.com/unix_sockets/index.htm\n\nhttps://documentation.softwareag.com/adabas/wcp632mfr/wtc/wtc_prot.htm\n\nhttps://www.geeksforgeeks.org/little-and-big-endian-mystery/\n\nhttps://github.com/openssl/openssl/tree/691064c47fd6a7d11189df00a0d1b94d8051cbe0/demos/ssl\n\nhttps://beej.us/guide/bgnet/html/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnguyenchiemminhvu%2FLinuxNetworkProgramming","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnguyenchiemminhvu%2FLinuxNetworkProgramming","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnguyenchiemminhvu%2FLinuxNetworkProgramming/lists"}