Introduction
- socket address: For TCP/IP, the socket address is IP address and port number.
- a socket is the file descriptor;
- local_addr is the socket address on local computer.
- remote_addr is the socket address on remote computer.
Socket States
In this diagram there are a few different states a socket can be in. Socket conceptually contains different information and allows different oprations in each state.
- detached: a socket file discriptor is created through
socket(), but there is no socket address associated. This socket conceptually cannot do anything.- If you call sendto(remote_addr), socket implementation automatically binds it to a good local socket address (IP and a random port), and moves to bound state, before sending data.
- connect(remote_addr) moves directly to connected state.
- bound: the socket is associated with a local socket address (e.g. IP and port). For connection-less socket, you can now receive data from remote party because they can address to you. You can send data as long as you know the remote party’s socket address from a bound socket. Note that when bind() a socket, you can specify “any” IP (INADDR_ANY for IPv4, and in6addr_any for IPv6); when a message from client arrives, it can come though any network interface if your host has multiple interfaces (with different IPs). In bound state, you can choose to bind to a port together with a specific IP, or a floating “any” IP.
- listening: This is like a special bound state for a connection-oriented socket on server.
- connected: A connected socket has the full information about both local and remote addresses.
Connection-less Socket (like UDP)
- The server uses
socket()->bind(), then it can call recvfrom() to get (potentially blocking) for incoming messages; - The client uses socket() then it can use sendto(remote_addr) / sendmsg(remote_addr) to send a message to remote server. The socket implementation automatically binds the socket to a local socket address. You can also manually bind() it before using sendto(remote_addr) / sendmsg(remote_addr), but you have to choose IP / port carefully yourself.
- The receiver end can choose to obtain sender’s socket address when recvfrom() / recvmsg() is called. This address can be used to reply the message, if reply is needed.
- A connection-less socket can stay in bound state for ever, and it can be used to talk with different remote parties.
- It is however possible to move the connection-less socket to connected state, by calling connect(remote_addr). After that, you can send message with write()/send() without specifying remote_addr explicitly. You can call connect() again to let the socket point to a different remote socket address, or connect(null) to clear that association to return to bound state.
- Keep in mind, UDP does not guarantee reliable transfer, therefore a receiver may not receive all the datagrams the sender sent.
Connection-Oriented Socket (like TCP)
- The server uses socket() -> bind() -> listen() -> accept() sequence.
- The client uses socket() -> connect(remote_addr).
- The client may use socket() -> bind(local_addr) -> connect(remote_addr), but binding is automatically done if it uses socket() -> connect(remote_addr).
- The server’s listening socket is not for data communication. It serves as a mother socket, and the only purpose is to take in a new incoming connection from client. If that incoming connection request is accepted, a new server side socket is spawned in connected state for data communication. The listening server socket never moves into connected state.
- The new server socket uses the same port number as the listening socket, but since this is a connected socket, it has both local/server IP+port and client IP+port, so incoming data to the port from different clients can be properly distributed to their corresponding server sockets.
- The client side socket is also moved into connected state, associated with the socket address of the server side, appearing like to the same one it tries to
connect()to, althoug on server side it’s served by a different socket.
- Once the connection is established, both client and server now have a socket in connected state, and they can use read()/write()/send()/recv() to exchange data.
The State Machine Diagram

The state machine diagram is generated by Mermaid tool, see https://mermaid.js.org/syntax/stateDiagram.html. There is the Online Mermaid Editor you can try at https://mermaid.live/, paste the following in there:
%% Mermaid diagram https://mermaid.js.org/syntax/stateDiagram.html
stateDiagram-v2
detached: detached\n-----------\n(socket)
bound: bound\n--------------\n(socket)\n(local_addr)
listening: listening\n--------------\n(socket)\n(local_addr)
connected: connected\n-----------------\n(socket)\n(local_addr)\n(remote_addr)
all: all states
classDef VirtualState font-style:italic;
[*] --> detached : socket()
detached --> bound : bind(local_addr)
detached --> bound : sendto(remote_addr)
bound --> bound : sendto()/recvfrom()/sendmsg()/recvmsg()
detached --> connected : connect(remote_addr)
bound --> listening: listen()
listening --> connected: accept() spawns new
bound --> connected : connect(remote_addr)
connected --> connected: read()/write()/readv()/writev()/send()/recv()/\nsendto()/recvfrom()/sendmsg()/recvmsg()/\nconnect()
connected --> bound: connect(null)
all:::VirtualState --> [*] : close()
note left of [*]
Socket API At A Glance,
Connection-oriented: SOCK_STREAM (TCP) etc.
Connection-less: SOCK_DGRAM (UDP) etc.
https://en.wikipedia.org/wiki/Berkeley_sockets
end note
note left of bound
Connection-less sockets often stay here,
using sendto()/recvfrom()/sendmsg()/recvmsg()
getsockname() returns local socket address.
end note
note right of listening
Connection-oriented socket as "mother socket" at server.
client connecting to it results in a different connected socket.
end note
note right of connected
Most Connection-oriented sockets stay here, using read()/write()/readv()/writev()/send()/recv().
Connection-less sockets rarely here. If they are, they
- can still use sendto()/recvfrom()/sendmsg()/recvmsg();
- allows connect() to change remote_addr, or connect(null) to go to bound state.
getpeername() returns remote socket address.
end note
Some C++ wrapper code for socket is in C++ Wrapper for Socket API.