Why Would You Not Know Your Own Address?
It sounds strange at first — why would a program not know its own address? But there are many real situations where a socket’s address is assigned by the kernel rather than specified by the application, and you need to discover it at runtime:
When a client calls connect() without first calling bind(), the kernel assigns an ephemeral (temporary) port automatically. The client might need to know this port for logging or for a peer-to-peer protocol. When a server listens on port 0, the kernel picks a free port. When a server accepts a connection, it might not know which of its IP addresses the client connected to. getsockname() and getpeername() are the tools for discovering these addresses.
Function Signatures
#include <sys/socket.h>
/*
* getsockname - Get the LOCAL address bound to this socket.
* (My address, as seen by the network)
*/
int getsockname(int sockfd,
struct sockaddr *addr,
socklen_t *addrlen);
/*
* getpeername - Get the REMOTE address of the connected peer.
* (The other end of the connection)
*/
int getpeername(int sockfd,
struct sockaddr *addr,
socklen_t *addrlen);
/* Both return 0 on success, -1 on error */
addrlen is a value-result argument: you set it to the size of your buffer before the call, and the kernel updates it to the actual size of the address stored. Always pass the correct initial size or you will get truncated results.
port: 54321 (ephemeral)
getpeername() → 203.0.113.1:80
port: 80
getpeername() → 192.168.1.10:54321
Code Examples
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void show_local_address(int sockfd)
{
struct sockaddr_in local_addr;
socklen_t addrlen = sizeof(local_addr);
char ip_str[INET_ADDRSTRLEN];
/*
* After connect() is called without a prior bind(),
* the kernel assigns an ephemeral port. Use getsockname()
* to discover what port was chosen.
*/
if (getsockname(sockfd,
(struct sockaddr *)&local_addr,
&addrlen) == -1) {
perror("getsockname");
return;
}
inet_ntop(AF_INET, &local_addr.sin_addr,
ip_str, sizeof(ip_str));
printf("My local address: %s:%d\n",
ip_str, ntohs(local_addr.sin_port));
}
void show_peer_address(int sockfd)
{
struct sockaddr_in peer_addr;
socklen_t addrlen = sizeof(peer_addr);
char ip_str[INET_ADDRSTRLEN];
if (getpeername(sockfd,
(struct sockaddr *)&peer_addr,
&addrlen) == -1) {
perror("getpeername");
return;
}
inet_ntop(AF_INET, &peer_addr.sin_addr,
ip_str, sizeof(ip_str));
printf("Connected to: %s:%d\n",
ip_str, ntohs(peer_addr.sin_port));
}
int main(void)
{
int sfd;
struct sockaddr_in server_addr;
sfd = socket(AF_INET, SOCK_STREAM, 0);
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 */
/* No bind() — kernel picks ephemeral port for us */
if (connect(sfd, (struct sockaddr *)&server_addr,
sizeof(server_addr)) == 0) {
show_local_address(sfd); /* Shows: 192.168.x.y:<ephemeral> */
show_peer_address(sfd); /* Shows: 93.184.216.34:80 */
}
close(sfd);
return 0;
}
This demonstrates that calling listen() on a TCP socket without bind() causes the kernel to assign an ephemeral port. Use getsockname() to see what was assigned.
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
int sfd;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
char ip_str[INET_ADDRSTRLEN];
sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd == -1) { perror("socket"); return 1; }
/*
* No bind() call here — the kernel will assign an address
* and ephemeral port when we call listen().
*/
if (listen(sfd, 5) == -1) { perror("listen"); return 1; }
/* Discover what address/port the kernel chose */
if (getsockname(sfd,
(struct sockaddr *)&addr,
&addrlen) == -1) {
perror("getsockname");
return 1;
}
inet_ntop(AF_INET, &addr.sin_addr, ip_str, sizeof(ip_str));
printf("After listen() without bind():\n");
printf(" Address: %s\n", ip_str);
printf(" Port: %d (ephemeral)\n", ntohs(addr.sin_port));
/*
* Expected output:
* Address: 0.0.0.0
* Port: some_random_high_port (e.g. 36241)
*/
close(sfd);
return 0;
}
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void log_client(int conn_fd)
{
struct sockaddr_in peer;
socklen_t peerlen = sizeof(peer);
char ip[INET_ADDRSTRLEN];
/*
* getpeername() on the accepted connection gives us
* the client's IP address and port number.
* Useful for logging and access control.
*/
if (getpeername(conn_fd,
(struct sockaddr *)&peer,
&peerlen) == 0) {
inet_ntop(AF_INET, &peer.sin_addr, ip, sizeof(ip));
printf("[LOG] Connection from %s:%d\n",
ip, ntohs(peer.sin_port));
}
}
int main(void)
{
int lfd, cfd;
struct sockaddr_in server_addr;
int opt = 1;
lfd = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9090);
server_addr.sin_addr.s_addr = INADDR_ANY;
bind(lfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
listen(lfd, 10);
printf("Server ready on port 9090\n");
for (;;) {
cfd = accept(lfd, NULL, NULL); /* Note: not getting addr from accept */
if (cfd >= 0) {
log_client(cfd); /* Use getpeername() to discover client addr */
/* ... handle client ... */
close(cfd);
}
}
return 0;
}
Note that accept() can also return the peer address via its second and third arguments. getpeername() is useful when the peer address was not captured at accept() time, or when you need it later in a deeply nested function that only has the file descriptor.
Interview Questions
Answer: An ephemeral port is a temporary, automatically assigned port number chosen by the kernel from a range of high port numbers (typically 32768–60999 on Linux). The kernel assigns one when a client calls connect() without previously calling bind(). The client needs a port number for the TCP connection’s 4-tuple (src IP, src port, dst IP, dst port), and the kernel picks one that is currently free.
Answer: Before connect() (and without a bind()), getsockname() returns an all-zeros address and port 0 — the socket is unbound. After connect(), the kernel has assigned a local IP address (based on routing) and an ephemeral port, so getsockname() returns the actual assigned address and port.
Answer: accept() fills in the peer address at the time the connection is accepted, if you pass non-NULL second and third arguments. getpeername() can be called at any time later using only the connected file descriptor, without needing to have saved the address from accept(). Both give the same information; getpeername() is more flexible when the descriptor is passed deep into call chains.
Answer: It returns -1 with errno == ENOTCONN, indicating the socket is not connected. getpeername() is only meaningful on a connected socket (after connect() on a client, or on the socket returned by accept() on a server).
Answer: It calls getsockname() on the accepted connection socket. This returns the local IP address that the connection was established on, which tells the server which of its network interfaces the client used. The listening socket is typically bound to INADDR_ANY (0.0.0.0), which accepts connections on any interface, so the accepted connection socket is the only way to determine the actual interface used.
