sockets · advanced
OS: POSIX Sockets — TCP Server/Client, epoll, UDP, TCP_NODELAY, TIME_WAIT
POSIX TCP sockets. Server: socket(AF_INET,SOCK_STREAM,0) → setsockopt(SO_REUSEADDR) → bind() → listen(fd,backlog) → accept() → read/write → close. Client: socket() → connect() → read/write → close. htons()/ntohs(): host↔network byte order (big-endian) conversion for port and address fields. Non-blocking: fcntl(fd, F_SETFL, O_NONBLOCK); read returns EAGAIN when no data. Multiplexers: select O(n) 1024-fd limit; poll O(n) no limit; epoll O(1)/event Linux-only. epoll: epoll_create1→epoll_ctl(ADD)→epoll_wait; only ready fds returned. UDP: SOCK_DGRAM; no connection; sendto/recvfrom per packet; multicast for market data. SO_REUSEADDR: reuse port in TIME_WAIT state. TCP_NODELAY: disable Nagle algorithm for low latency. SO_RCVBUF/SO_SNDBUF: tune kernel buffer sizes. TIME_WAIT: 2*MSL after close; prevents stale packet corruption.
TCP sockets: socket→bind→listen(backlog)→accept; SO_REUSEADDR; htons() for endian; epoll for O(1) multiplexed I/O; TCP_NODELAY for low latency (disables Nagle); UDP: SOCK_DGRAM, sendto/recvfrom, no connection state.
The code
// POSIX Sockets — TCP client/server in C++
// TCP Server (minimal):int fd = socket(AF_INET, SOCK_STREAM, 0); // create socketsetsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));struct sockaddr_in addr{};addr.sin_family = AF_INET;addr.sin_port = htons(8080); // host-to-network-short (endian)addr.sin_addr.s_addr = INADDR_ANY;bind(fd, (sockaddr*)&addr, sizeof(addr));listen(fd, 128); // 128 = backlog (max pending connections)int client = accept(fd, nullptr, nullptr); // blocks until connectionwrite(client, "hello", 5);close(client);close(fd);
// TCP Client:int fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in srv{};srv.sin_family = AF_INET;srv.sin_port = htons(8080);inet_pton(AF_INET, "127.0.0.1", &srv.sin_addr);connect(fd, (sockaddr*)&srv, sizeof(srv));char buf[256];read(fd, buf, sizeof(buf)); // receiveclose(fd);
// Non-blocking I/O with select / epoll:// select: O(n) per call; limited to FD_SETSIZE (1024) fds// poll: O(n) per call; no fd limit// epoll: O(1) per event; Linux only; production standardint epfd = epoll_create1(0);struct epoll_event ev;ev.events = EPOLLIN;ev.data.fd = fd;epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);struct epoll_event events[64];int n = epoll_wait(epfd, events, 64, -1); // block until eventsfor (int i = 0; i < n; i++) { if (events[i].data.fd == listen_fd) { /* new connection */ } else { /* data to read on events[i].data.fd */ }}
// UDP (connectionless):int fd = socket(AF_INET, SOCK_DGRAM, 0);// no connect/listen/accept — just sendto/recvfromsendto(fd, data, len, 0, (sockaddr*)&dst, sizeof(dst));recvfrom(fd, buf, sizeof(buf), 0, (sockaddr*)&src, &srclen);
// Key options:// SO_REUSEADDR: reuse port after crash (avoids TIME_WAIT)// TCP_NODELAY: disable Nagle algorithm (for low-latency)// SO_RCVBUF / SO_SNDBUF: tune kernel buffer sizes// O_NONBLOCK: non-blocking I/O (use with epoll)What this lesson walks through
- 01TCP 3-way handshake and socket lifecycle
- 02TCP server — minimal C++ implementation
- 03epoll — O(1) multiplexed I/O for production servers
- 04UDP and key socket options
- 05Non-blocking sockets and select/poll comparison
- 06Socket interview patterns — common questions
TCP connection: SYN → SYN-ACK → ACK (3-way handshake). Socket lifecycle: socket() creates a file descriptor. bind() assigns local address/port. listen() marks it passive (server). accept() blocks until client connects — returns a NEW fd for that connection. The original fd stays listening for more connections. connect() initiates connection (client side). read()/write() for data transfer. close() sends FIN.
See it animated — step by step, at your own pace
Unlock the full interactive walkthrough of OS: POSIX Sockets — TCP Server/Client, epoll, UDP, TCP_NODELAY, TIME_WAIT and 100+ animated C++ interview lessons.