Sockets are entry-points for a network "pipe" that connects two or more network applications. Both sides need to open a socket to create the network "pipe". Sockets are bidirectional in nature and hence, both ends of the "pipe" can simultaneous send data to the other end and receive data from the other end. The two endpoints typically sit on different machines. However, there is nothing stopping us from making them sit on the same machine.
The following figure shows two sockets forming a network "pipe".
Technically speaking, sockets are a part of GNU C library (also known as glibc). We include them here since a lot of network applications are popularly written in C using glibc sockets.
A socket is identified by three main parameters: (a) IP address (IPv4 or IPv6) of the machine, (b) the port number on that machine, and (c) the transport protocol. A port number is a logical local identification point for network applications; each host can have as much as 65,536 ports. In terms of network stack, transport protocols (e.g. TCP or UDP) belong to a layer that sits on top of the IP layer but beneath the application layer. Sockets on both sides need to use the same transport protocol.
Depending upon the transport protocol, sockets can communicate with each other in two ways: connection-oriented or connection-less. In addition to transport sockets, we also have sockets that allow us to send and receive raw packets. We do not cover those sockets here.
For connection-oriented sockets, the main thing is that the two sockets must establish an explicit connection (hand-shake, if you will) before they can send or receive data. The primary transport protocol for these types of sockets is Transmission Control Protocol (TCP).
To setup a connection, we need to create a server socket that can wait for connection requests from (remote) clients. Then, we need to create a client socket that can send a request. Once the server receives a request from a client, it creates a new connection. To be more precise, with each request, the server socket creates a new local socket and associates it with the remote client socket. This way, the two sockets -- the client that initiated the connection request and the one that was created locally by the server socket -- become connected.
Once a connection is established, the parent server socket does not call it quits. Instead, it detaches itself from the new connection and starts waiting for the next incoming request. And the cycle continues.
The following figure shows connection establishment in two steps. In the first step, the server socket (socketA0) accepts an incoming connection from a client socket (socketB0). In the second step, the server socket creates a new local socket (socketA1) and associates it with the the client socket. With that, socketA1 and socketB0 now become connected.
For connectionless sockets, there is no requirement for an explicit connection setup. A client socket can send messages directly to a server socket. The primary transport protocol for these types of sockets is User Datagram Protocol (UDP). Because such sockets are connectionless, one socket can send messages to multiple server sockets at the same time -- something that is not possible with the connection-oriented sockets.
The following figure shows a connectionless client socket (socketB0) sending data to three different server sockets (socketA0, socketA1, and socketA2), all at the same time.
In the next few pages, we describe both connection-oriented and connectionless sockets. In addition, we also cover advanced socket concepts like socket select and socket options.