CSE 124 Discussion Section Sockets Programming 10/10/17
Topics What s a socket? Creating a socket Connecting a socket Sending data Receiving data Resolving URLs to IPs Advanced socket options Live code examples!
What s a socket? Sockets are how programs communicate over the network. There are two types of sockets: Client sockets represent a one-to-one connection between two endpoints. Each client socket has a unique 5-tuple: (Protocol, Source IP, Dest IP, Source Port, Dest Port) Server sockets actively listen for new connections from clients on a specific port. When a new client connects, it creates a new client socket for that connection.
Creating a Socket Here s the socket function (from the socket man page): int sockfd = socket(int socket_family, int socket_type, int protocol); We ll go over each of these arguments in the following slides. The function returns a socket file descriptor ( sockfd ), which is used to other system calls that work with sockets.
Socket Family This is the Layer 3 protocol to use with the socket. Common socket families include: AF_UNIX, AF_LOCAL: Local communication (IPC sockets) AF_INET: IP protocol version 4 (IPv4) AF_INET6: IP protocl version 6 (IPv6) AF_PACKET: Low level packets, usually for raw sockets
Socket Type This is the Layer 4 protocol to use with the socket. For this course, there s only two you need to worry about: SOCK_STREAM: TCP connection. SOCK_DGRAM: UDP connection. The chosen protocol affects how other socket system calls work.
Socket Protocol This usually isn t used; for this course, always set this to 0. Some socket types support various protocols, but usually they only support one.
Connecting a Socket: Server Side First we need to set up the server socket. Clients connect to this. Bind the Socket to an address and port using bind() system call: int bind(int socket_fd, struct sockaddr *addr, socklen_t addrlen); Listen for connections using Listen() system call: void listen(int sockfd, int size_queue); Accept connections using accept system call: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
Binding a Socket This is used to set the IP and port the socket should operate on. The main argument is servaddr, where the program sets the IP + port. But wait: the IP depends on if we re using IPv4 or IPv6! Use struct sockaddr_in for IPv4, and struct sockaddr_in6 for IPv6. For this class, we only need to worry about IPv4. The argument addrlen should always be sizeof(struct sockaddr_in) for IPv4. If the function succeeds, it returns 0, Otherwise, it returns -1.
Creating a struct sockaddr_in struct sockaddr_in { short sin_family; // Always AF_INET unsigned short sin_port; // htons(port_number); struct in_addr sin_addr; // see struct in_addr, below char sin_zero[8]; // zero this if you want to }; struct in_addr { unsigned long s_addr; }; // load with inet_pton() inet_pton(af_inet, 192.168.1.25, &my_sockaddr_in.sin_addr.s_addr);
Listening on a Socket Now we need to tell the OS to allow clients to connect to it using the listen() call. size_queue is how many clients can be waiting for the server to accept their connection.
Accepting a Client Connection The server uses the accept() call to accept client connections. Here, the argument addr will get filled in by the function with the client s IP + port. The value returned will be -1 if an error occurred. Otherwise, the return value is a new socket fd that the server should use to communicate with the client via the send() and recv() calls.
Connecting a Socket: Client Side Clients only need to use one system call to connect to a server socket: int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); The addr argument is the same as in bind(), but in this case is the IP + port the client wants to connect to. This call will return 0 for a successful connection and -1 if it failed. Once a socket is connected, it must be closed later.
Closing a Socket This system call closes the socket/connection between client and server void close(int sockfd); Always close the socket before exiting the program. If the socket is not closed then the other side of the socket won t know that the connection has ended.
Sending data The send() call is used to send data over the network via a client socket. ssize_t send(int sockfd, const void *buffer, size_t len, int flags); This system call sends len bytes from buffer to the other end of the socket. flags can be used to set advanced features, but usually it can just be 0. The return value is the number of bytes that were sent to the network (-1 on error). If the socket has been closed (by either side), an error is returned.
Send: UDP vs TCP For TCP and UDP, send() acts differently. For UDP, send() will either consume all or none of the data. If data is consumed, that becomes a single UDP packet in the network. For TCP, send() may consume, all, some, or none of the data. Use sendall(): ssize_t sendall(int sockfd, const void *buffer, size_t len) { ssize_t ret, sent = 0; while(sent < len) { ret = send(sockfd, buffer + sent, len - sent); if (ret >= 0) sent += ret; } return sent; }
Receiving data The recv() call is used to receive data from the network. ssize_t recv(int sockfd, void *buffer, size_t len, int flags); This system call receives up to len bytes of data and it stores in buffer. flags can be used to set advanced features, but usually it can just be 0. The return value is the number of bytes that were received from the network (-1 on error).
Recv: UDP vs TCP Again, the way recv() works depends on the protocol used. In UDP, recv() retrieves the data in a single UDP packet. In TCP, recv() retrieves between 1 and len bytes. Use recvall() if needed. In both cases, recv() blocks until there is data. If there is no data left and the socket is closed, then recv() returns 0 and doesn t block.
Resolving URLs to IPs Sockets only connect to IPs. For URLs, we have to perform DNS resolution. int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); node: Website to query, but without protocol info, e.g. www.google.com service: Protocol to use, e.g. http hints: Hints to the DNS resolver about how to perform the DNS query res: Set by the function. A pointer to an array of IP + ports associated with the requested URL. This must be freed using freeaddrinfo()
Advanced Socket Options There s a number of advanced socket options that can be read/set using two similar system calls: int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); int optval = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval);
Advanced Socket Options SO_REUSEADDR: Allow reusing the same source address (if local address) SO_KEEPALIVE: Allow sending keepalive messages for certain protocols SO_BINDTODEVICE: Bind the socket to a specific hardware interface (L2 bind) SO_SNDBUF/SO_RCVBUF: Set the buffer size for sending/receiving SO_SNDTIMEO/SO_RCVTIMEO: Set the timeout for send/recv calls If one of these options is set on a server socket, all client sockets created by it will have the option set as well.
Live code examples!