getsockname函数用于获取与某个套接字关联的本地协议地址
getpeername函数用于获取与某个套接字关联的外地协议地址
对于这两个函数,如果函数调用成功,则返回0,如果调用出错,则返回-1。
使用这两个函数,我们可以通过套接字描述符来获取自己的IP地址和连接对端的IP地址,如在未调用bind函数的TCP客户端程序上,可以通过调用getsockname()函数获取由内核赋予该连接的本地IP地址和本地端口号,还可以在TCP的服务器端accept成功后,通过getpeername()函数来获取当前连接的客户端的IP地址和端口号。
例子
/*服务器端*/
#define MAXLINE 4096
#define PORT 6563
#define LISTENQ 1024
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
int main() {
int listenfd, connfd;
struct sockaddr_in servaddr;//服务器绑定的地址
struct sockaddr_in listendAddr, connectedAddr, peerAddr;//分别表示监听的地址,连接的本地地址,连接的对端地址
int listendAddrLen, connectedAddrLen, peerLen;
char ipAddr[INET_ADDRSTRLEN];//保存点分十进制的地址
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT);
bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));//服务器端绑定地址
listen(listenfd, LISTENQ);
listendAddrLen = sizeof(listendAddr);
getsockname(listenfd, (struct sockaddr *)&listendAddr, &listendAddrLen);//获取监听的地址和端口
printf("listen address = %s:%d\n", inet_ntoa(listendAddr.sin_addr), ntohs(listendAddr.sin_port));
while(1) {
connfd = accept(listenfd, (struct sockaddr *)NULL, NULL);
connectedAddrLen = sizeof(connectedAddr);
getsockname(connfd, (struct sockaddr *)&connectedAddr, &connectedAddrLen);//获取connfd表示的连接上的本地地址
printf("connected server address = %s:%d\n", inet_ntoa(connectedAddr.sin_addr), ntohs(connectedAddr.sin_port));
getpeername(connfd, (struct sockaddr *)&peerAddr, &peerLen); //获取connfd表示的连接上的对端地址
printf("connected peer address = %s:%d\n", inet_ntop(AF_INET, &peerAddr.sin_addr, ipAddr, sizeof(ipAddr)), ntohs(peerAddr.sin_port));
}
return 0;
}
上面的代码中,在调用listen函数之后就获取监听套接字描述符对应的本地地址,在accept()函数后,由于accept返回了一个套接字描述符connfd用于表示该连接,所以可以对这个connfd调用getsockname函数和getpeername函数,分别获取内核赋予该连接的本地IP地址和连接的对端地址。
/*客户端*/
#define PORT 6563
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
int main(int argc, char **argv) {
struct sockaddr_in servaddr;//服务器端地址
struct sockaddr_in clientAddr;//客户端地址
int sockfd;
int clientAddrLen = sizeof(clientAddr);
char ipAddress[INET_ADDRSTRLEN];//保存点分十进制的ip地址
if(argc < 2) {
printf("parameter error");
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {
printf("server address error\n");//地址参数不合法
}
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//向服务器端发起连接请求
getsockname(sockfd, (struct sockaddr*)&clientAddr, &clientAddrLen);//获取sockfd表示的连接上的本地地址
printf("client:client ddress = %s:%d\n", inet_ntop(AF_INET, &clientAddr.sin_addr, ipAddress, sizeof(ipAddress)), ntohs(clientAddr.sin_port));
return 0;
}
在客户端的代码中,调用connect函数后,即可调用getsockname来获连接上的本地地址。
代码的运行结果如下:
服务区端输出
客户端输出
封装
/**
* 取得套接字连接对方的网络地址, 地址格式为: IP:PORT
* @param fd {ACL_SOCKET} 网络套接字
* @param buf {char*} 存储地址的缓冲区,不能为空
* @param bsize {size_t} buf 空间大小
* @return {int} 0: ok; -1: error
*/
int acl_getpeername(ACL_SOCKET fd, char *buf, size_t size)
{
ACL_SOCKADDR addr;
struct sockaddr *sa = (struct sockaddr*) &addr;
socklen_t len = sizeof(addr);
if (fd == ACL_SOCKET_INVALID || buf == NULL || size <= 0) {
return -1;
}
memset(&addr, 0, sizeof(addr));
if (getpeername(fd, sa, &len) == -1) {
return -1;
}
if (sa->sa_family == AF_UNIX) {
memset(&addr, 0, sizeof(addr));
len = sizeof(addr);
if (getsockname(fd, sa, &len) == -1) {
return -1;
}
}
if (acl_inet_ntop(sa, buf, size) > 0) {
return 0;
} else {
return -1;
}
}
/**
* 取得套接字连接本地的网络地址, 地址格式为: IP:PORT
* @param fd {ACL_SOCKET} 网络套接字
* @param buf {char*} 存储地址的缓冲区,不能为空
* @param bsize {size_t} buf 空间大小
* @return {int} 0: ok; -1: error
*/
int acl_getsockname(ACL_SOCKET fd, char *buf, size_t size) {
ACL_SOCKADDR addr;
struct sockaddr *sa = (struct sockaddr *) &addr;
socklen_t len = sizeof(addr);
if (fd == ACL_SOCKET_INVALID || buf == NULL || size == 0){
return -1;
}
memset(&addr, 0, sizeof(addr));
if (getsockname(fd, sa, &len) == -1){
return -1;
}
if (acl_inet_ntop(sa, buf, size) > 0) {
return 0;
} else {
return -1;
}
}
/**
* 将 socket 地址转为字符串格式,同时支持 IPV4 与 IPV6 及 UNIX 域套接口
* @param sa {const struct sockaddr*}
* @param buf {char*} 存储转换结果
* @param size {size_t} buf 空间大小
* @return {size_t} 返回 sockaddr 地址所对应地址类型的实际长度,如对于 IPV4 则
* 对应 struct sockaddr_in 的结构体长度,对于 IPV6 则对应 struct sockaddr_in6
* 的结构体长度,返回值 0 表示转换出错
*/
size_t acl_inet_ntop(const struct sockaddr *sa, char *buf, size_t size){
if (sa->sa_family == AF_INET) {
int port;
char ip[IPLEN];
struct sockaddr_in *in = (struct sockaddr_in *) sa;
if (!inet_ntop(sa->sa_family, &in->sin_addr, ip, IPLEN)) {
return 0;
}
port = ntohs(in->sin_port);
if (port > 0) {
snprintf(buf, size, "%s:%d", ip, port);
} else {
snprintf(buf, size, "%s", ip);
}
return sizeof(struct sockaddr_in);
} else if (sa->sa_family == AF_INET6) {
acl_msg_fatal("%s(%d): not implement", __FUNCTION__, __LINE__);
return 0;
} else if (sa->sa_family == AF_UNIX) {
struct sockaddr_un *un = (struct sockaddr_un *) sa;
strncpy(buf, un->sun_path, size);
return sizeof(struct sockaddr_un);
} else {
acl_msg_error("%s(%d): invalid sa->sa_family=%d", __FUNCTION__, __LINE__, sa->sa_family);
return 0;
}
}