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来获连接上的本地地址。

代码的运行结果如下:

服务区端输出

InetSocketAddress 获取客户端ip socket服务端获取客户端ip_unix


客户端输出

InetSocketAddress 获取客户端ip socket服务端获取客户端ip_套接字_02

封装

/**
     * 取得套接字连接对方的网络地址, 地址格式为: 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;
        }
    }