getsockname函数与getpeername函数的使用

getsockname和getpeername函数

getsockname函数用于获取与某个套接字关联的本地协议地址

getpeername函数用于获取与某个套接字关联的外地协议地址

定义如下:

#include<sys/socket.h>

int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);

int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);

对于这两个函数,如果函数调用成功,则返回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来获连接上的本地地址。

代码的运行结果如下:

服务区端输出

客户端输出

从上面的代码中可以看到,服务器端listenfd套接字描述符对应的地址即为绑定的通配IP地址和指定的端口,而connfd套接字描述符对应的连接的服务器端的地址为内核赋予的地址和用户指定的端口。

上面的客户端与服务器端的代码中使用了函数inet_ntoa,inet_pton对32位的地址进行转换,其中inet_ntoa是较老的函数,与它一起的还有函数inet_addr和inet_ntoa,这三个函数的定义如下:

#include<arpa/inet.h>

int inet_aton(const char *strptr, struct in_addr *addrptr);

in_addr_t inet_addr(const char *strptr);

char *inet_ntoa(struct in_addr inaddr);

inet_aton与inet_addr函数的功能类似,都是将点分十进制的字符串表示的IP地址转换成32位的网络字节序的IPv4地址。

inet_ntoa函数将32位的网络字节序的IPv4地址转换成点分十进制的字符串表示的IP地址,inet_addr函数已被废弃,并且这三个函数只针对IPv4地址有效,在点分十进制数串和32位的网络字节序二进制值间进行转换,如果要对于IPv4和IPv6都适用,那么使用下面两个函数:

#include<arpa/inet.h>

int inet_pton(int family, const char *strptr, void *addrptr);

const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);

函数中的p和n分别代表表达式(presentation)数值(numeric)

所以inet_pton函数将strptr指针所指的字符串转换为网络地址(IPv4和IPv6),再将地址保存到addrptr指向的结构体中,inet_ntop将网络地址转为字符串表示的地址,结果存放在strptr中,len参数是strptr的大小。

这两个函数支持IPv4和IPv6,所以需要通过参数family来指定,当前要转换的是IPv4地址还是IPv6地址。

Reference

《UNIX网络编程 卷1:套接字联网API(第3版)》

getsockname函数与getpeername函数的使用,布布扣,bubuko.com

时间: 2024-10-20 13:02:47

getsockname函数与getpeername函数的使用的相关文章

UNIX网络编程——getsockname和getpeername函数

UNIX网络编程——getsockname和getpeername函数 来源:网络转载   http://www.educity.cn/linux/1241293.html 这两个函数或者返回与某个套接字关联的本地协议地址(getsockname),或者返回与某个套接字关联的外地协议地址即得到对方的地址(getpeername). #include <sys/socket.h> int getsockname(int sockfd,struct sockaddr* localaddr,sock

async(await)函数和 Generator 函数 区别

async 函数是 Generator 函数的语法糖. async 函数对 Generator 函数的改进体现在: 1. async 内置执行器. Generator 函数的执行必须靠执行器,需要调用 next() 方法,或者用co 模块:而 async 函数自带执行器.也就是说,async 函数的执行与普通函数一模一样,只要一行. 2. 更好的语义. async 和 await 比起星号和 yield,语义更清楚. 3.更广的适用性. co 模块约定,yield 命令后面只能是 Thunk 函

Python-lambda函数,map函数,filter函数

lambda函数主要理解: lambda 参数:操作(参数). lambda语句中,冒号前是参数,可以有多个,用逗号隔开,冒号右边的返回值.lambda语句构建的其实是一个函数对象 map函数: map(function_to_apply, list_of_inputs).map函数可以把list_of_inputs内的对象依次输入到function_to_apply中进行操作. filter函数: filter(function_to_apply, list_of_inputs).Filter

C#委托与C语言函数指针及函数指针数组

C#委托与C语言函数指针及函数指针数组 在使用C#时总会为委托而感到疑惑,但现在总新温习了一遍C语言后,才真正理解的委托. 其实委托就类似于C/C++里的函数指针,在函数传参时传递的是函数指针,在调用的时候通过指针访问这个函数. 在C语言中函数指针的申明如下: //可以理解为申明一个指着变量 Func ,它的类型是 返回Type(可以为 void )类型的参数,接收 (Type one,Type two,...)类型的//参数(可以不接受参数). Type *Func(Type one,Type

oracle listagg函数、lag函数、lead函数 实例

Oracle大师Thomas Kyte在他的经典著作中,反复强调过一个实现需求方案选取顺序: “如果你可以使用一句SQL解决的需求,就使用一句SQL:如果不可以,就考虑PL/SQL是否可以:如果PL/SQL实现不了,就考虑Java存储过程是否可以:如果这些都不可能实现,那么就需要考虑你是否真的需要实现这个需求.” 各个关系型DBMS产品都在遵守关系型数据库模型的基本体系架构,遵循通用的SQL国际规范.同时,为了更好地配合自身数据库实现的特征,以及提供更加丰富的功能,各个DBMS纷纷在标准SQL上

JS杂谈系列-函数知识、函数模式

函数,函数,function,function,go go go! 创建函数: 第一种:function aa(){alert(1)}; 第二种:var aa=function(){alert(1)}; 其实对于使用没有太大的区别,第一个是用函数关键字创建,第二个是创建变量,然后赋值为一个函数. 同样我们还可以创建匿名函数 function(){alert(1)}; 函数的里面可以传递参数arg function aa(arg){alert(arg)}; function aa(arg1,arg

javascript 函数声明和函数表达式的区别(学习笔记)

javascript中声明函数的方法有两种:函数声明式和函数表达式. 区别如下: 1).以函数声明的方法定义的函数,函数名是必须的,而函数表达式的函数名是可选的. 2).以函数声明的方法定义的函数,函数可以在函数声明之前调用,而函数表达式的函数只能在声明之后调用. 3).以函数声明的方法定义的函数并不是真正的声明,它们仅仅可以出现在全局中,或者嵌套在其他的函数中,但是它们不能出现在循环,条件或者try/catch/finally中,而 函数表达式可以在任何地方声明. 下面分别用两种方法定义函数:

itoa函数,sprintf函数

itoa函数 itoa 为c语言的一个函数.itoa 函数是一个广泛应用的,从非标准扩展到标准的C语言.它不能被移植,因为它不是标准定义下的C语言,但是,编译器通常在一个不遵循程式标准的模式下允许其通过header ,因为它是一个从逻辑对应到标准库的函数.itoa 取整数输入值,并将其转换为相应进制数字的字符串. 用法: itoa(i ,num ,10 ); i ---- 需要转换成字符串的数字 num ---- 转换后保存字符串的变量 10 ---- 转换数字的基数(即进制).10就是说按10

python--函数的返回值、函数参数的使用、名称空间与作用域、函数嵌套、函数对象

今天学习内容有函数的返回值.函数参数的使用.名称空间与作用域.函数嵌套. 下来我们一一查看. 函数的返回值 看几个栗子: def func(x): return x**2 y=func(10) print(y) def foo(): return None res=foo() print(res) def foo(): return{'a':1} res=foo() print(res['a']) def foo(): return {'a':1},1,'a',[1,2] res=foo() p