曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 3647|回复: 0
打印 上一主题 下一主题

getsockname()/getpeername() -- 返回本地协议/外地协议地址

[复制链接]

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
跳转到指定楼层
楼主
发表于 2012-11-6 11:52:36 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
getsockname() 和 getpeername() 的原型如下:
[C++] 纯文本查看 复制代码
#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

getsockname() 返回与某个套接字关联的本地协议地址,getpeername() 返回与某个套接字关联的外地协议地址。

下面用一段代码演示这两个函数的使用情况:
[C++] 纯文本查看 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>


//显示错误信息
static void display_error(const char *err_msg)
{
        perror (err_msg);
        exit (EXIT_FAILURE);
}
//获取本地协议地址或外地协议地址
char *sock_addr(int s, char *buf, size_t buf_size, int type)
{
        int z;
        struct sockaddr_in addr_inet;
        int len;


        len = sizeof(addr_inet);


        if (!type) {
                z = getsockname(s, (struct sockaddr *)&addr_inet, &len);
                if (z == -1) {
                        return NULL;    // 获取失败
                }


                bzero(buf, buf_size);
                snprintf(buf, buf_size, "%s:%u",           //将地址信息存储到缓冲区
                                inet_ntoa(addr_inet.sin_addr),
                                (unsigned)ntohs(addr_inet.sin_port));


                return buf;
        } else {
                z = getpeername(s, (struct sockaddr *)&addr_inet, &len);           
                if (z == -1) {
                        return NULL;    // Failed
                }


                bzero(buf, buf_size);
                snprintf(buf, buf_size, "%s:%u",           //将地址信息存储到缓冲区
                                inet_ntoa(addr_inet.sin_addr),
                                (unsigned)ntohs(addr_inet.sin_port));


                return buf;
        }                 
}


int main(int argc, char **argv, char **envp)
{
        int z;
        int sockfd;
        int len;
        char buf[64];
        struct sockaddr_in addr_inet, client_addr;
       
        //创建一个套接字
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if ( sockfd == -1 ) {
                display_error("socket()");
        }


        memset (&addr_inet, 0, sizeof(addr_inet));
        addr_inet.sin_family = AF_INET;
        addr_inet.sin_port = htons(2012);      //服务器绑定在 2012 端口
        inet_aton ("192.168.1.108", &addr_inet.sin_addr);        //服务器 IP 地址
        len = sizeof(addr_inet);


        //将地址绑定到套接字上
        z = bind(sockfd, (struct sockaddr *)&addr_inet, len);
        if ( z == -1) {
                display_error("bind()");
        }
        //获取服务器端 IP 地址
        if (!sock_addr(sockfd, buf, sizeof(buf), 0)) {
                display_error("sock_addr()");
        }


        printf("Server address is '%s'\n", buf);


        if (listen(sockfd, 5) == -1) {
                display_error("listen()");
        }


        int new_fd;


        if ((new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &len)) == -1) {
                display_error("accept()");
        }
        if (!sock_addr(new_fd, buf, sizeof(buf), 1)) {             //注意:如果应用 getsockname() 并通过 new_fd 来获取到地址仍然是服务器端地址
                display_error("sock_addr()");
        }
        printf ("Client address is '%s'\n", buf);


        close(new_fd);
        close(sockfd);


        return 0;
} 

运行程序:
beyes@beyes  :~/c/getsockname> ./sockname
Server address is '192.168.1.108:2012'
运行上面程序后,程序打印出了服务器端的地址及端口号,然后进入监听阶段。这时候,我们不必要再写一个客户端连接程序进行测试,而是利用 nc  这个程序进行连接。这里使用一台 IP 为 192.168.1.110 的 Windows 主机上的 nc.exe 进行连接:
C:\>nc.exe 192.168.1.108 2012
回车执行命令后,命令会马上退出,因为程序中的处理情况是,一旦连接发生,服务器也马上退出,此时可看到服务器端的输出:
Client address is '192.168.1.110:10269'
这表示,客户机的 IP 是 192.168.1.110 ,并且从 10269 这个端口发出连接。

服务器端通过调用 accept() 接受了客户端的连接,此时对返回的 new_fd 应用 getpeername() 时可以获得客户端的的身份信息。需要注意的是,不能用 getsockname() 从 new_fd 上获得客户端的信息,因为 getsockname() 用于返回的是“本地协议地址”;如果这么做的话,得到的信息仍然是“Server address is '192.168.1.108:2012'。

一个实际的例子如 xinetd 中的 telnet 服务。当 xinetd 调用 accept() 响应连接请求时,它返回两个值:一个是已连接的套接字描述符(函数返回值);另一个是客户的 IP 地址和端口号(存放在 accept() 第 2 个参数所指向的套接字地址结构中)。然后 xinetd 调用 fork() 派生出一个子进程,由于子进程是父进程内存镜像的一个副本,因此父进程中的那个套接字地址结构在子进程中也可用,那个已连接的套接字描述符也是如此(如上面的 new_fd,因为描述符父子进程之间是共享的)。接着,子进程会调用 exec 执行真正的服务器程序 Telnet 时,子进程的内存映像就会被新的 Telnet 程序所替代,此时包含对端地址的那个套接字地址结构就此消失了(如上面的那个 client_addr),然而那个已经连接了的套接字描述符(new_fd)却可以跨 exec 继续保持开放。最后在进入 Telnet 的领空之后,它首先要调用的就是 getpeername() 用于获取客户的 IP 地址和端口号。

从上所述,在程序中,可用 getpeername() 作用于 new_fd 这个套接字描述符来获取对端的 IP 地址和端口号;在没有调用 exec 之前,也可以直接从套接字地质结构 client_addr 中直接提取。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-4-28 04:03 , Processed in 0.081439 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表