← Chapter 61 Index | Part 1 of 3
Why Do We Need These APIs?
When you create a TCP socket and connect or accept, the operating system assigns addresses and port numbers. Sometimes your program needs to know what address it was given — especially when the OS auto-assigns an ephemeral port, or when a server wants to know which client connected.
Two system calls solve this problem:
- getsockname() — tells you the local (your own) address and port of a socket.
- getpeername() — tells you the remote (peer’s) address and port of a connected socket.
Think of it this way: if a socket is a phone, getsockname gives you your own phone number, and getpeername gives you the number of the person you are talking to.
Function Signatures
#include <sys/socket.h>
/* Returns your own (local) address */
int getsockname(int sockfd,
struct sockaddr *addr,
socklen_t *addrlen);
/* Returns the peer's (remote) address */
int getpeername(int sockfd,
struct sockaddr *addr,
socklen_t *addrlen);
Both return 0 on success and -1 on error.
Parameters Explained
| Parameter | Type | What it means |
|---|---|---|
sockfd |
int |
The socket file descriptor you want to query |
addr |
struct sockaddr * |
Buffer where the address will be written. Usually cast from sockaddr_in (IPv4) or sockaddr_in6 (IPv6) |
addrlen |
socklen_t * |
Pass in the size of your buffer. The kernel writes back the actual size used. |
When to Use Each Function
- After
bind()with port 0 — to find which port the OS picked - After
connect()— to find your own local port - Inside a server — to log which interface received a connection
- Inside
accept()-ed socket — to know what local address was used
- After
accept()— if you passed NULL for the address, call getpeername later - Inside a child process (after fork) — parent passed just the fd, not the address
- Logging which client connected
- Security checks — validating the remote IP
Simple Example: Checking Your Own Port After bind()
When you bind to port 0, the kernel picks a free port for you. Use getsockname() to find out which one was chosen.
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
int sockfd;
struct sockaddr_in addr;
socklen_t addrlen;
/* Create a TCP socket */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket");
return 1;
}
/* Bind to ANY address, port 0 (OS picks the port) */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(0); /* 0 = let OS choose */
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("bind");
return 1;
}
/* Now find out what port was assigned */
addrlen = sizeof(addr);
if (getsockname(sockfd, (struct sockaddr *)&addr, &addrlen) == -1) {
perror("getsockname");
return 1;
}
printf("OS assigned port: %d\n", ntohs(addr.sin_port));
printf("Local address: %s\n", inet_ntoa(addr.sin_addr));
return 0;
}
Compile and run:
gcc -o check_port check_port.c
./check_port
# Output: OS assigned port: 54321 (some ephemeral port)
Full Example from TLPI: Both getsockname and getpeername Together
This example creates a listening socket and a connecting socket on the same machine (loopback). It then calls both APIs to show all four address combinations.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
/* Helper: print a sockaddr_in as "IP:port" */
static void print_addr(const char *label,
struct sockaddr_in *addr,
socklen_t len)
{
(void)len;
printf("%s => %s : %d\n",
label,
inet_ntoa(addr->sin_addr),
ntohs(addr->sin_port));
}
int main(void)
{
int listenFd, connFd, acceptFd;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
/* ---- Step 1: Create listening socket ---- */
listenFd = socket(AF_INET, SOCK_STREAM, 0);
if (listenFd == -1) { perror("socket"); return 1; }
/* Allow address reuse so we can restart quickly */
int yes = 1;
setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(9090); /* fixed port for this demo */
if (bind(listenFd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("bind"); return 1;
}
if (listen(listenFd, 5) == -1) {
perror("listen"); return 1;
}
/* ---- Step 2: Create connecting socket (client side) ---- */
connFd = socket(AF_INET, SOCK_STREAM, 0);
if (connFd == -1) { perror("socket"); return 1; }
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9090);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);
if (connect(connFd, (struct sockaddr *)&server_addr,
sizeof(server_addr)) == -1) {
perror("connect"); return 1;
}
/* ---- Step 3: Server accepts ---- */
acceptFd = accept(listenFd, NULL, NULL);
if (acceptFd == -1) { perror("accept"); return 1; }
/* ---- Step 4: Query all four combinations ---- */
printf("\n--- getsockname (local address) ---\n");
addrlen = sizeof(addr);
getsockname(connFd, (struct sockaddr *)&addr, &addrlen);
print_addr("connFd local", &addr, addrlen);
addrlen = sizeof(addr);
getsockname(acceptFd, (struct sockaddr *)&addr, &addrlen);
print_addr("acceptFd local", &addr, addrlen);
printf("\n--- getpeername (remote address) ---\n");
addrlen = sizeof(addr);
getpeername(connFd, (struct sockaddr *)&addr, &addrlen);
print_addr("connFd peer ", &addr, addrlen);
addrlen = sizeof(addr);
getpeername(acceptFd, (struct sockaddr *)&addr, &addrlen);
print_addr("acceptFd peer ", &addr, addrlen);
close(connFd);
close(acceptFd);
close(listenFd);
return 0;
}
Expected output:
--- getsockname (local address) ---
connFd local => 127.0.0.1 : 51234 (ephemeral port chosen by OS)
acceptFd local => 127.0.0.1 : 9090 (server port)
--- getpeername (remote address) ---
connFd peer => 127.0.0.1 : 9090 (the server it connected to)
acceptFd peer => 127.0.0.1 : 51234 (the client that connected)
Visualising the Four Queries
| Function Call | Socket | Returns | Typical Value |
|---|---|---|---|
getsockname(connFd) |
Client socket | Client’s own IP + ephemeral port | 127.0.0.1:51234 |
getsockname(acceptFd) |
Accepted socket | Server’s IP + server port | 127.0.0.1:9090 |
getpeername(connFd) |
Client socket | Server’s IP + server port | 127.0.0.1:9090 |
getpeername(acceptFd) |
Accepted socket | Client’s IP + client ephemeral port | 127.0.0.1:51234 |
Notice how connFd and acceptFd are two ends of the same connection. What is “local” for one is “peer” for the other, and vice versa.
- Both functions work only on a socket that is already connected or bound.
- Pass in
sizeof(struct sockaddr_in)for IPv4,sizeof(struct sockaddr_in6)for IPv6. addrlenis a value-result parameter — the kernel writes back the actual size.getpeername()fails withENOTCONNif the socket is not connected.- These are read-only queries — they never change the socket state.
Common Mistakes
/* WRONG: forgetting to reset addrlen before each call */
socklen_t addrlen = sizeof(struct sockaddr_in);
getsockname(sockfd1, (struct sockaddr *)&addr, &addrlen);
/* addrlen is now modified by the kernel — may be smaller */
getsockname(sockfd2, (struct sockaddr *)&addr, &addrlen); /* BUG! */
/* CORRECT: reset addrlen before every call */
addrlen = sizeof(struct sockaddr_in);
getsockname(sockfd1, (struct sockaddr *)&addr, &addrlen);
addrlen = sizeof(struct sockaddr_in); /* reset */
getsockname(sockfd2, (struct sockaddr *)&addr, &addrlen);
Verify with netstat
While your program sleeps (add a sleep(30) before exit), you can verify the connection from a terminal using netstat or ss:
# Show all TCP connections
netstat -tn | grep 9090
# Or with newer ss tool
ss -tn | grep 9090
# Output will show both sides:
# 127.0.0.1:9090 127.0.0.1:51234 ESTABLISHED
# 127.0.0.1:51234 127.0.0.1:9090 ESTABLISHED
Interview Questions and Answers
bind() with port 0, the OS assigns a free ephemeral port automatically. You cannot know this port without calling getsockname(). This is very common in client programs that let the OS pick a port, and in test frameworks.connect(). Calling connect() on a UDP socket does not establish a real connection — it just stores the peer address so the kernel knows where to send datagrams. After that, getpeername() returns the stored peer address.addrlen before each call when reusing the same variable.errno to ENOTCONN. This is the standard “transport endpoint is not connected” error. You must have an established TCP connection (or a “connected” UDP socket) before calling getpeername().getpeername() on the accepted socket descriptor it inherited. The parent may not have passed the address struct down, but the socket itself carries all the connection state. getpeername() is the standard way to recover the peer address in such cases.