Week 13.a CS3650 04/01 2024 https://naizhengtan.github.io/24spring/ 1. socket programming 2. select syscall ------ 1. socket programming * a toy example A client sends "hello world!" to a server. [see handout from last week] [server] [client] | | fd = socket(...) fd = socket(...) bind(fd,...) | listen(fd,...) | | | new_fd = accept(fd,...) +--connect(fd,...) | / | |<------------------+ | |------------------------->| | | new_fd <====================> fd * discuss topics in order: (1) sending and receiving data to and from an established socket (2a) establishing sockets for server (2b) establishing sockets for client (3) a toy client-server example [cont'ed from last time] * TCP sockets * Need to synchronize sender and receiver so that they agree where bytestream starts * Three new functions: listen(), connect(), accept() * Q: accept() returns a new socket? Why? [answer: Can't tie up original listening socket in data communication] * Socket programming interfaces: [see handout from last week] a) socket, send, and recv * int socket(int domain, int type, int protocol); socket() creates an endpoint for communication and returns a descriptor. * ssize_t send(int socket, const void *buffer, size_t length, int flags); send a message from a socket * ssize_t recv(int socket, void *buffer, size_t length, int flags); receive a message from a socket b) bind, listen, and accept (server side) * int bind(int socket, const struct sockaddr *address, socklen_t address_len); bind() assigns a name to an unnamed socket. * int listen(int socket, int backlog); listen for connections on a socket * int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len); accept a connection on a socket c) connect (client side) * int connect(int socket, const struct sockaddr *address, socklen_t address_len); initiate a connection on a socket 2. select syscall [Demo: a chat server] * Q: How to implement this? In particular, what if we want to receive from two sockets at once? ** recv() blocks ** can use non-blocking recv(), but this consumes too much CPU * An elegant solution: select(), multiplexing I/O ** the select() syscall: wait on multiple file descriptor at once. Wake up if any one has data. * Aside: select() is a great example of abstraction ** Designed to accommodate certain kinds of file descriptors ** But also works for sockets, because sockets are file descriptors at their core. * interface: int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict errorfds, struct timeval *restrict timeout); FD_SET(int fd, fd_set *set); Add fd to the set. FD_CLR(int fd, fd_set *set); Remove fd from the set. FD_ISSET(int fd, fd_set *set); Return true if fd is in the set. FD_ZERO(fd_set *set); Clear all entries from the set. * best practice: when reusing "fd_set", call "FD_ZERO"