Why Do We Need These Calls?
When you create and use a socket, you might not always know:
When the kernel assigns you an ephemeral port (implicit bind), you need getsockname() to find out which port you got.
If your server was exec()d by inetd, you inherit the socket but the accept() return value (which includes the peer address) is not accessible. getpeername() retrieves it.
These two system calls are the answer:
#include <sys/socket.h>
/* Returns the local address the socket is bound to */
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/* Returns the address of the remote peer the socket is connected to */
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/* Both return 0 on success, -1 on error */
| Parameter | Type | Meaning |
|---|---|---|
| sockfd | int | The socket file descriptor you want address info for |
| addr | struct sockaddr * | Buffer where the address is written by the call (output) |
| addrlen | socklen_t * | Value-result: pass in the buffer size; receives actual address length on return |
getsockname() – When and Why to Use It
getsockname() returns the address that the local socket is bound to. There are three key situations where this is useful:
| Situation | Why getsockname() is needed |
|---|---|
| Socket bound by another program (inetd) | Your server was exec()d by inetd. The socket file descriptor is inherited but you do not know what port it is on. getsockname() tells you. |
| Implicit bind (port 0 or no bind before connect) | If you call connect() on a TCP socket without calling bind() first, the kernel picks a port for you. getsockname() reveals which ephemeral port was chosen. |
| bind() with port 0 | You explicitly bind with sin_port=0 to let the kernel pick any free port. getsockname() reads back the assigned port number. |
What triggers an implicit bind?
The kernel performs an implicit bind (assigns a local address automatically) in these cases:
| Operation | Socket Type | What the kernel assigns |
|---|---|---|
| connect() or listen() | TCP socket not previously bound | Wildcard IP + ephemeral port from OS port range |
| First sendto() | UDP socket not previously bound | Wildcard IP + ephemeral port |
| bind() with port 0 | Any | Specified IP + kernel-chosen ephemeral port |
getpeername() – When and Why to Use It
getpeername() returns the address of the remote end of the connection. It is most useful for TCP servers that want to log or verify the client’s IP address and port.
Normally a server gets the peer address from the return value of accept(). But if the server was exec()d after accept() (for example, started by inetd), the accept() return value is no longer accessible. getpeername() recovers that information from the socket itself.
Code Examples
Example 1 – Finding the ephemeral port after connect()
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(void)
{
int sockfd;
struct sockaddr_in server_addr, local_addr;
socklen_t addrlen;
/* Create a TCP socket */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) { perror("socket"); return 1; }
/* Connect to a server (no explicit bind – kernel assigns ephemeral port) */
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(80);
inet_pton(AF_INET, "93.184.216.34", &server_addr.sin_addr); /* example.com */
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("connect");
close(sockfd);
return 1;
}
/*
* At this point the kernel has assigned us an ephemeral local port.
* We did not call bind(), so we do not know what port we got.
* getsockname() reveals it.
*/
addrlen = sizeof(local_addr);
if (getsockname(sockfd, (struct sockaddr *)&local_addr, &addrlen) == -1) {
perror("getsockname");
close(sockfd);
return 1;
}
printf("Connected from local port: %d\n", ntohs(local_addr.sin_port));
printf("Local IP: %s\n",
inet_ntoa(local_addr.sin_addr));
close(sockfd);
return 0;
}
Example 2 – Server logging client address with getpeername()
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
/*
* log_peer_address - called by a server to log the client's IP and port
* Useful when the server was exec()d and lost the accept() return value.
*/
void log_peer_address(int conn_fd)
{
struct sockaddr_in peer;
socklen_t peerlen = sizeof(peer);
char ip_str[INET_ADDRSTRLEN];
if (getpeername(conn_fd, (struct sockaddr *)&peer, &peerlen) == -1) {
perror("getpeername");
return;
}
inet_ntop(AF_INET, &peer.sin_addr, ip_str, sizeof(ip_str));
printf("Client connected from %s:%d\n", ip_str, ntohs(peer.sin_port));
}
/*
* log_local_address - print what local address/port we are listening on
*/
void log_local_address(int sockfd)
{
struct sockaddr_in local;
socklen_t loclen = sizeof(local);
char ip_str[INET_ADDRSTRLEN];
if (getsockname(sockfd, (struct sockaddr *)&local, &loclen) == -1) {
perror("getsockname");
return;
}
inet_ntop(AF_INET, &local.sin_addr, ip_str, sizeof(ip_str));
printf("Server socket bound to %s:%d\n", ip_str, ntohs(local.sin_port));
}
Example 3 – Both calls together, like the TLPI listing
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
/*
* Demonstration program:
* - Creates a listening socket on a given port
* - Connects to it from another socket
* - Calls getsockname() and getpeername() on both ends
* - Prints address info like the socknames example in TLPI
*/
void print_addr(const char *label, int fd)
{
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
char buf[INET_ADDRSTRLEN];
if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) == 0)
printf("%s getsockname: %s:%d\n", label,
inet_ntop(AF_INET, &addr.sin_addr, buf, sizeof(buf)),
ntohs(addr.sin_port));
if (getpeername(fd, (struct sockaddr *)&addr, &addrlen) == 0)
printf("%s getpeername: %s:%d\n", label,
inet_ntop(AF_INET, &addr.sin_addr, buf, sizeof(buf)),
ntohs(addr.sin_port));
}
int main(int argc, char *argv[])
{
int listen_fd, conn_fd, accept_fd;
struct sockaddr_in srv_addr, cli_addr;
socklen_t cli_len;
int port;
if (argc != 2) { fprintf(stderr, "Usage: %s port\n", argv[0]); return 1; }
port = atoi(argv[1]);
/* Step 1: Create listening socket */
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
memset(&srv_addr, 0, sizeof(srv_addr));
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr.s_addr = INADDR_ANY;
srv_addr.sin_port = htons(port);
bind(listen_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
listen(listen_fd, 5);
/* Step 2: Create connecting socket (client side) */
conn_fd = socket(AF_INET, SOCK_STREAM, 0);
srv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* 127.0.0.1 */
connect(conn_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
/* Step 3: Accept the connection */
cli_len = sizeof(cli_addr);
accept_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_len);
/* Step 4: Print addresses for both connected sockets */
print_addr("conn_fd ", conn_fd);
print_addr("accept_fd", accept_fd);
/*
* Expected output (port 55555 example):
* conn_fd getsockname: 127.0.0.1:32835 (ephemeral port)
* conn_fd getpeername: 127.0.0.1:55555
* accept_fd getsockname: 127.0.0.1:55555
* accept_fd getpeername: 127.0.0.1:32835
*
* This matches what netstat -a shows for the two ESTABLISHED connections.
*/
sleep(5); /* time to run: netstat -a | grep 55555 */
close(accept_fd);
close(conn_fd);
close(listen_fd);
return 0;
}
Example 4 – Using bind() with port 0 and reading back the assigned port
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/*
* Bind to port 0: kernel picks any free port.
* Use getsockname() to find out which port was chosen.
* Common in tests and dynamic port allocation scenarios.
*/
int main(void)
{
int sockfd;
struct sockaddr_in addr;
socklen_t addrlen;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(0); /* let kernel pick a port */
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("bind");
return 1;
}
/* Read back the port the kernel assigned */
addrlen = sizeof(addr);
if (getsockname(sockfd, (struct sockaddr *)&addr, &addrlen) == -1) {
perror("getsockname");
return 1;
}
printf("Kernel assigned port: %d\n", ntohs(addr.sin_port));
/* Now you can tell clients which port to connect to */
close(sockfd);
return 0;
}
Verifying with netstat
You can verify the socket address information returned by getsockname() and getpeername() using the netstat command. Run the example program and then in another terminal:
/* Run the example program in background */
$ ./socknames 55555 &
/* netstat output format:
Proto Recv-Q Send-Q Local Address Foreign Address State
*/
$ netstat -a | egrep '(Address|55555)'
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 *:55555 *:* LISTEN
tcp 0 0 localhost:32835 localhost:55555 ESTABLISHED
tcp 0 0 localhost:55555 localhost:32835 ESTABLISHED
Reading the netstat output:
| Row | Socket | Local Address | Foreign Address | State |
|---|---|---|---|---|
| 1 | listen_fd | *:55555 | *:* | LISTEN |
| 2 | conn_fd (client) | localhost:32835 (ephemeral) | localhost:55555 | ESTABLISHED |
| 3 | accept_fd (server side) | localhost:55555 | localhost:32835 | ESTABLISHED |
Notice that rows 2 and 3 are mirror images of each other – the local and foreign addresses swap between the two ends of the connection. This is exactly what getsockname() and getpeername() return on each socket.
ss -tn (socket statistics) which is faster than netstat and shows the same information.Using getsockname() with IPv6
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/*
* Generic helper that works with both IPv4 and IPv6.
* Uses struct sockaddr_storage which is large enough for any address family.
*/
void print_socket_info(int sockfd, const char *label)
{
struct sockaddr_storage local_ss, peer_ss;
socklen_t len;
char addr_buf[INET6_ADDRSTRLEN];
int port;
/* ---- Local address ---- */
len = sizeof(local_ss);
if (getsockname(sockfd, (struct sockaddr *)&local_ss, &len) == 0) {
if (local_ss.ss_family == AF_INET) {
struct sockaddr_in *s = (struct sockaddr_in *)&local_ss;
inet_ntop(AF_INET, &s->sin_addr, addr_buf, sizeof(addr_buf));
port = ntohs(s->sin_port);
} else { /* AF_INET6 */
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&local_ss;
inet_ntop(AF_INET6, &s->sin6_addr, addr_buf, sizeof(addr_buf));
port = ntohs(s->sin6_port);
}
printf("[%s] Local: %s:%d\n", label, addr_buf, port);
}
/* ---- Peer address ---- */
len = sizeof(peer_ss);
if (getpeername(sockfd, (struct sockaddr *)&peer_ss, &len) == 0) {
if (peer_ss.ss_family == AF_INET) {
struct sockaddr_in *s = (struct sockaddr_in *)&peer_ss;
inet_ntop(AF_INET, &s->sin_addr, addr_buf, sizeof(addr_buf));
port = ntohs(s->sin_port);
} else {
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&peer_ss;
inet_ntop(AF_INET6, &s->sin6_addr, addr_buf, sizeof(addr_buf));
port = ntohs(s->sin6_port);
}
printf("[%s] Remote: %s:%d\n", label, addr_buf, port);
}
}
struct sockaddr_storage as your address buffer. It is large enough to hold any socket address (IPv4, IPv6, UNIX domain). This avoids buffer overflows when the socket domain changes.Interview Questions & Answers
getsockname() returns the local address that the socket is bound to – that is, your own IP address and port number. getpeername() returns the address of the remote peer – the IP address and port of the machine on the other end of the connection. For a server, getsockname() gives the port it is listening on and getpeername() gives the connecting client’s address.
When a server is exec()d by another process such as inetd, it inherits the connected socket file descriptor but does not have access to the client address that accept() returned in the parent. getpeername() lets the exec()d server retrieve the peer address directly from the socket.
An ephemeral port is a temporary, automatically assigned port number chosen by the kernel from a range (typically 32768–60999 on Linux). The kernel assigns one when you call connect() on a TCP socket that was not explicitly bound, on the first sendto() for an unbound UDP socket, or when you bind() with port 0. You discover the assigned port by calling getsockname() after the implicit bind occurs.
Before calling getsockname() or getpeername(), you initialize *addrlen to the size of the address buffer you have allocated. This tells the kernel how much space is available. After the call returns, the kernel writes the actual number of bytes it filled into the address structure back into *addrlen. If the actual address is smaller than your buffer, only that many bytes are written. If it is larger (meaning your buffer was too small), the address is silently truncated to fit.
netstat -a shows each socket as a row with local address and foreign address columns. For a connected pair, the two ESTABLISHED entries are mirror images: the local address of one entry is the foreign address of the other. This matches exactly what getsockname() and getpeername() return on each end of the connection.
struct sockaddr_in is only large enough for IPv4 addresses. If you later use an IPv6 socket, sizeof(struct sockaddr_in6) is larger and would overflow a sockaddr_in buffer. struct sockaddr_storage is defined to be large enough to hold any socket address family, making it the safe choice for code that may work with both IPv4 and IPv6.
No. A listening socket is not connected to any peer. Calling getpeername() on a listening socket returns ENOTCONN (socket is not connected). getpeername() only works on a connected socket – one that has completed a connect() as a client or has been returned by accept() as a server.
Chapter 61 Complete!
You have covered sendfile(), TCP_CORK, MSG_MORE, and socket address retrieval. These are essential tools for writing efficient, production-quality Linux network applications.
