getsockname和getpeername函数
getsockname函数用于获取与某个套接字关联的本地协议地址
getpeername函数用于获取与某个套接字关联的外地协议地址
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地址和端口号。
如下面的客户端-服务器程序:
服务器端代码
1. /*服务器端*/
2. #define MAXLINE 4096
3. #define PORT 6563
4. #define LISTENQ 1024
5. #include<stdio.h>
6. #include<sys/socket.h>
7. #include<netinet/in.h>
8. #include<unistd.h>
9. #include<string.h>
10. #include<arpa/inet.h>
11.
12. int main() {
13. int listenfd, connfd;
14. struct sockaddr_in servaddr;//服务器绑定的地址
15. struct sockaddr_in listendAddr, connectedAddr, peerAddr;//分别表示监听的地址,连接的本地地址,连接的对端地址
16. int listendAddrLen, connectedAddrLen, peerLen;
17. char ipAddr[INET_ADDRSTRLEN];//保存点分十进制的地址
18. listenfd = socket(AF_INET, SOCK_STREAM, 0);
19. memset(&servaddr, 0, sizeof(servaddr));
20.
21. servaddr.sin_family = AF_INET;
22. servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
23. servaddr.sin_port = htons(PORT);
24.
25. bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));//服务器端绑定地址
26.
27. listen(listenfd, LISTENQ);
28. listendAddrLen = sizeof(listendAddr);
29. getsockname(listenfd, (struct sockaddr *)&listendAddr, &listendAddrLen);//获取监听的地址和端口
30. printf("listen address = %s:%d\n", inet_ntoa(listendAddr.sin_addr), ntohs(listendAddr.sin_port));
31.
32. while(1) {
33. connfd = accept(listenfd, (struct sockaddr *)NULL, NULL);
34. connectedAddrLen = sizeof(connectedAddr);
35. getsockname(connfd, (struct sockaddr *)&connectedAddr, &connectedAddrLen);//获取connfd表示的连接上的本地地址
36. printf("connected server address = %s:%d\n", inet_ntoa(connectedAddr.sin_addr), ntohs(connectedAddr.sin_port));
37. getpeername(connfd, (struct sockaddr *)&peerAddr, &peerLen); //获取connfd表示的连接上的对端地址
38. printf("connected peer address = %s:%d\n", inet_ntop(AF_INET, &peerAddr.sin_addr, ipAddr, sizeof(ipAddr)), ntohs(peerAddr.sin_port));
39. }
40. return 0;
41. }
上面的代码中,在调用listen函数之后就获取监听套接字描述符对应的本地地址,在accept()函数后,由于accept返回了一个套接字描述符connfd用于表示该连接,所以可以对这个connfd调用getsockname函数和getpeername函数,分别获取内核赋予该连接的本地IP地址和连接的对端地址。
客户端代码
1. /*客户端*/
2. #define PORT 6563
3. #include<stdio.h>
4. #include<sys/socket.h>
5. #include<netinet/in.h>
6. #include<unistd.h>
7. #include<string.h>
8. #include<arpa/inet.h>
9.
10. int main(int argc, char **argv) {
11. struct sockaddr_in servaddr;//服务器端地址
12. struct sockaddr_in clientAddr;//客户端地址
13. int sockfd;
14. int clientAddrLen = sizeof(clientAddr);
15. char ipAddress[INET_ADDRSTRLEN];//保存点分十进制的ip地址
16.
17. if(argc < 2) {
18. printf("parameter error");
19. }
20.
21. sockfd = socket(AF_INET, SOCK_STREAM, 0);
22. memset(&servaddr, 0, sizeof(servaddr));
23. servaddr.sin_family = AF_INET;
24. servaddr.sin_port = htons(PORT);
25. if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {
26. printf("server address error\n");//地址参数不合法
27. }
28.
29. connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//向服务器端发起连接请求
30.
31. getsockname(sockfd, (struct sockaddr*)&clientAddr, &clientAddrLen);//获取sockfd表示的连接上的本地地址
32.
33. printf("client:client ddress = %s:%d\n", inet_ntop(AF_INET, &clientAddr.sin_addr, ipAddress, sizeof(ipAddress)), ntohs(clientAddr.sin_port));
34. return 0;
35. }
在客户端的代码中,调用connect函数后,即可调用getsockname来获连接上的本地地址。
代码的运行结果如下:
服务区端输出
listen address = 0.0.0.0:6563
connected server address = 127.0.0.1:6563
connected peer address = 127.0.0.1:37511
客户端输出:
client address = 127.0.0.1:37511
从上面的代码中可以看到,服务器端listenfd套接字描述符对应的地址即为绑定的通配IP地址和指定的端口,而connfd套接字描述符对应的连接的服务器端的地址为内核赋予的地址和用户指定的端口。