linux socket通讯如何获取本地的源端口号

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux socket通讯如何获取本地的源端口号相关的知识,希望对你有一定的参考价值。

关于TCP IP网络通讯的资料非常多,TCP IP通过IP数据包模式进行端对端通讯。典型的TCP数据包如下
技术分享
可以看到数据包包含了源端口号和目的端口号,客户端socket向服务端发起连接时,系统会给socket随机分配一个源端口号,我们可以通过getsocketname来获取连接成功的socket的原端口信息。
函数原型
[cpp] view plain copy
 
  1. #include <sys/socket.h>  
  2. int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);  
参数:
sockfd socket连接的句柄
addr 网络地址指针,用来存储本地端socket地址信息,
addrlen addr的空间大小
返回结果,如果调用成功,返回0,并将本地网络地址信息存放在addr里面,失败返回-1,并通过errno反应错误信息。
source_port.cpp
[cpp] view plain copy
 
  1. #include <cstring>  
  2. #include <cstdio>  
  3. #include <cstdlib>  
  4. #include <sys/socket.h>  
  5. #include <sys/types.h>  
  6. #include <netinet/in.h>  
  7. #include <netinet/ip.h>  
  8. #include <netdb.h>  
  9. #include <errno.h>  
  10. #include <unistd.h>  
  11. #include <arpa/inet.h>  
  12.   
  13. void safe_close(int &sock);  
  14.   
  15. int main(int argc, char *argv[]) {  
  16.     int sockfd = 0, n = 0;  
  17.     socklen_t len = 0;  
  18.     char host[512] = {0};  
  19.     char buf[1024] = {0};  
  20.     struct hostent *server;  
  21.     struct sockaddr_in serv_addr, loc_addr;  
  22.   
  23.     if (argc < 2) {  
  24.         printf("Please input host name\n");  
  25.         exit(-1);  
  26.     }  
  27.     strncpy(host, argv[1], sizeof(host));  
  28.     server = gethostbyname(host);// 判断输入的域名是否正确  
  29.     if (NULL == server) {  
  30.         printf("find host: %s failed.\n", host);  
  31.         exit(-1);  
  32.     }  
  33.   
  34.     if (-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0))) {// 创建socket  
  35.         memset(buf, 0, sizeof(buf));  
  36.         snprintf(buf, sizeof(buf), "new socket failed. errno: %d, error: %s", errno, strerror(errno));  
  37.         perror(buf);  
  38.         exit(-1);  
  39.     }  
  40.   
  41.     memset(&serv_addr, 0, sizeof(serv_addr));  
  42.     serv_addr.sin_family = AF_INET;  
  43.     serv_addr.sin_port = htons(80);// http标准端口号  
  44.     memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length);  
  45.   
  46.     if (-1 == inet_pton(AF_INET, host, &serv_addr.sin_addr)) {  
  47.         memset(buf, 0, sizeof(buf));  
  48.         snprintf(buf, sizeof(buf), "inet_pton failed. errno: %d, error: %s", errno, strerror(errno));  
  49.         perror(buf);  
  50.         exit(-1);  
  51.     }  
  52.   
  53.     if (-1 == connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) {// 连接socket  
  54.         memset(buf, 0, sizeof(buf));  
  55.         snprintf(buf, sizeof(buf), "connect socket failed. errno: %d, error: %s", errno, strerror(errno));  
  56.         perror(buf);  
  57.         exit(-1);  
  58.     }  
  59.     printf("connect to %s success.\n", host);  
  60.   
  61.     len = sizeof(sizeof(loc_addr));  
  62.     memset(&loc_addr, 0, len);  
  63.     if (-1 == getsockname(sockfd, (struct sockaddr *)&loc_addr, &len)) {// 获取socket绑定的本地address信息  
  64.         memset(buf, 0, sizeof(buf));  
  65.         snprintf(buf, sizeof(buf), "get socket name failed. errno: %d, error: %s", errno, strerror(errno));  
  66.         perror(buf);  
  67.         safe_close(sockfd);  
  68.         exit(-1);  
  69.     }  
  70.   
  71.     if (loc_addr.sin_family == AF_INET) {// 打印信息  
  72.         printf("local port: %u\n", ntohs(loc_addr.sin_port));  
  73.     }  
  74.   
  75.     safe_close(sockfd);  
  76.     return 0;  
  77. }  
  78.   
  79. void safe_close(int &sock) {  
  80.     if (-1 != sock) {  
  81.         shutdown(sock, SHUT_RDWR);  
  82.         sock = -1;  
  83.     }  
  84. }  
本程序首先会启动一个socket连接一个普通的http服务器(baidu,qq,163,csdn),当socket连通时就通过getsocketname获取连接绑定的本地地址,并通过该地址获取源端口号。
终端1: 编译及运行
$ g++ source_port.cpp
$ ./a.out www.baidu.com
connect to www.baidu.com success.
local port: 39702
终端2: 通过tcpdump抓包验证
$ sudo tcpdump host www.baidu.com -v
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
18:38:32.381448 IP (tos 0x0, ttl 64, id 35033, offset 0, flags [DF], proto TCP (6), length 60)
icentos.39702 > 220.181.111.188.http: Flags [S], cksum 0x8cd2 (incorrect -> 0x596a), seq 2381397554, win 29200, options [mss 1460,sackOK,TS val 3513497323 ecr 0,nop,wscale 7], length 0
18:38:32.425904 IP (tos 0x0, ttl 55, id 35033, offset 0, flags [DF], proto TCP (6), length 60)
220.181.111.188.http > icentos.39702: Flags [S.], cksum 0xc315 (correct), seq 3561856904, ack 2381397555, win 8192, options [mss 1424,sackOK,nop,nop,nop,nop,nop,nop,nop,nop,nop,nop,nop,wscale 5], length 0
18:38:32.425930 IP (tos 0x0, ttl 64, id 35034, offset 0, flags [DF], proto TCP (6), length 40)

 

对比终端一和终端二表明获取的源端口地址是正确的。


















以上是关于linux socket通讯如何获取本地的源端口号的主要内容,如果未能解决你的问题,请参考以下文章

Linux网络编程套接字

Python基础篇第十四篇:网络编程

linux:网络:socket套接字

linux:网络:socket套接字

TCP报文格式

Linux网络套接字编程