Sections 56.3 & 56.4 โ bind() and struct sockaddr
After creating a socket with socket(), the socket has no address. It is like a telephone handset that is not yet connected to a phone number. The bind() system call assigns an address to the socket โ it gives it an identity that other processes can use to reach it.
The struct sockaddr structure is the generic address type that the sockets API uses so that a single set of system calls works across all socket domains.
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/* Returns: 0 on success, -1 on error */
| Argument | Type | Purpose |
|---|---|---|
sockfd |
int | File descriptor returned by socket() |
addr |
const struct sockaddr * | Pointer to a domain-specific address structure, cast to generic sockaddr * |
addrlen |
socklen_t | Size in bytes of the address structure pointed to by addr |
(phone with no number)
(phone number registered)
After bind(), the socket has a known address. Client processes can now use that address to connect or send data to this socket.
Typically a server calls bind() to register a well-known address (a fixed, publicly advertised address like port 80 for HTTP). A client usually does NOT call bind() โ the kernel automatically assigns an ephemeral (temporary) port to the client’s socket when it calls connect() or sends its first datagram.
- Fixed, advertised in advance
- Server calls
bind()explicitly - Clients know this address to connect
- Example: HTTP server always binds to port 80
- Temporary port assigned by the kernel
- Client usually skips
bind() - Kernel picks an unused port automatically
- Port is released when socket closes
A server can also skip bind() and call listen() directly. The kernel then assigns an ephemeral port to the server. The server must then use getsockname() to find out which port it got, and publish that information (e.g., register with a directory service like Sun RPC portmapper) so clients can find it.
Different socket domains use different address formats. For example:
AF_UNIXuses a filesystem pathname (e.g.,/tmp/mysocket)AF_INETuses a 4-byte IP address + 2-byte port numberAF_INET6uses a 16-byte IPv6 address + port number
Since system calls like bind() and connect() must work with all domains, the sockets API defines a single generic address type:
struct sockaddr {
sa_family_t sa_family; /* Address family: AF_UNIX, AF_INET, etc. */
char sa_data[14]; /* Address data (size varies by domain) */
};
This structure is just a template. You never fill it in directly. Instead, you fill in the domain-specific structure (e.g., struct sockaddr_in for AF_INET) and then cast its pointer to (struct sockaddr *) when passing to system calls.
| Generic Type | Domain-Specific Type | Domain | Key Fields |
|---|---|---|---|
struct sockaddr(cast target) |
struct sockaddr_un |
AF_UNIX |
sun_family, sun_path[108] |
struct sockaddr_in |
AF_INET |
sin_family, sin_port, sin_addr |
|
struct sockaddr_in6 |
AF_INET6 |
sin6_family, sin6_port, sin6_addr |
The first field in every domain-specific structure is always a family field (sun_family, sin_family, etc.) that matches the sa_family field of the generic struct sockaddr. The kernel reads this field to determine which type of address it is dealing with.
This is a common source of confusion for beginners. Here is the explanation:
The bind() function prototype takes a const struct sockaddr * as its second argument. But you fill in a domain-specific structure like struct sockaddr_in. Since C does not allow implicit pointer type conversion, you must explicitly cast:
struct sockaddr_in addr;
/* ... fill in addr ... */
bind(sockfd, (struct sockaddr *) &addr, sizeof(addr));
The cast is safe because all domain-specific address structures are designed to start with the same sa_family_t field at the same offset. The kernel uses that field to interpret the rest of the structure correctly.
Example 1: Binding a TCP server to port 8080
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h> /* struct sockaddr_in, htons(), INADDR_ANY */
int main(void)
{
int sockfd;
struct sockaddr_in server_addr;
/* Step 1: Create socket */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) { perror("socket"); exit(1); }
/* Step 2: Fill in address structure */
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; /* IPv4 */
server_addr.sin_port = htons(8080); /* port 8080, network byte order */
server_addr.sin_addr.s_addr = INADDR_ANY; /* accept on all interfaces */
/* Step 3: Bind โ note the cast to (struct sockaddr *) */
if (bind(sockfd,
(struct sockaddr *) &server_addr,
sizeof(server_addr)) == -1) {
perror("bind");
exit(1);
}
printf("Socket bound to port 8080\n");
close(sockfd);
return 0;
}
Example 2: Binding a UNIX domain socket
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h> /* struct sockaddr_un */
#include <unistd.h>
#define SOCKET_PATH "/tmp/my_ipc_socket"
int main(void)
{
int sockfd;
struct sockaddr_un addr;
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd == -1) { perror("socket"); exit(1); }
/* Remove any existing socket file */
unlink(SOCKET_PATH);
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);
if (bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
perror("bind");
exit(1);
}
printf("UNIX domain socket bound to %s\n", SOCKET_PATH);
close(sockfd);
unlink(SOCKET_PATH); /* cleanup */
return 0;
}
Example 3: Server skips bind() โ kernel assigns ephemeral port
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
int sockfd;
struct sockaddr_in assigned_addr;
socklen_t addr_len = sizeof(assigned_addr);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) { perror("socket"); exit(1); }
/*
* Skip bind() โ call listen() directly.
* Kernel assigns an ephemeral port.
*/
if (listen(sockfd, 5) == -1) { perror("listen"); exit(1); }
/* Use getsockname() to find out which port the kernel assigned */
if (getsockname(sockfd,
(struct sockaddr *) &assigned_addr,
&addr_len) == -1) {
perror("getsockname");
exit(1);
}
printf("Kernel assigned port: %d\n", ntohs(assigned_addr.sin_port));
close(sockfd);
return 0;
}
Learn the complete connection flow for stream sockets and the active/passive socket model.
