当前位置: 首页 > news >正文

钢材网站模板百度打开

钢材网站模板,百度打开,wordpress 评论回复,php购物网站开发背景文章目录 1、不同函数介绍1.1 recvfrom1.2 accept1.3 getsockname、getpeername 2、使用场景2.1、获取本地地址信息2.1.1 UDP客户端获取本地地址2.1.2 TCP客户端获取本地地址 2.2、获取对端地址信息2.2.1 UDP中获取对端地址2.2.2 TCP中获取对端地址 3、总结3.1 获取对端地址信息…

文章目录

  • 1、不同函数介绍
    • 1.1 recvfrom
    • 1.2 accept
    • 1.3 getsockname、getpeername
  • 2、使用场景
    • 2.1、获取本地地址信息
      • 2.1.1 UDP客户端获取本地地址
      • 2.1.2 TCP客户端获取本地地址
    • 2.2、获取对端地址信息
      • 2.2.1 UDP中获取对端地址
      • 2.2.2 TCP中获取对端地址
  • 3、总结
    • 3.1 获取对端地址信息
    • 3.2 获取本地地址信息
    • 3.3 解析地址信息

在UDP/TCP套接字编程,因为业务需要知道本地客户端、对端服务端的地址信息。先将有关地址获取的函数进行说明,再根据使用场景选择对应函数。

1、不同函数介绍

先介绍有关获取地址信息的函数 recvfrom、accept、getsockname、getpeername等,其他函数这里暂不做说明。

1.1 recvfrom

#include <sys/socket.h>
ssize_t recvfrom(int sock, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);

关注后两个参数from和fromlen,用于获取对端的地址信息。指针from为NULL时,表示不关心对端地址信息,同时指针fromlen也赋值为NULL。

当需要获取对端信息时,需要传递对象保存地址结构的指针from,以及当前结构地址的大小fromlen。recvfrom函数正常执行时,会将对端的地址信息写入from指向对象,并且会重写fromlen值为实际对端地址结构大小。但是,当传入的fromlen值小于对端地址结构大小,会造获取信息截断。例如传入的是sockaddr_in,而实际的对端地址结构是sockaddr_in6。

可以选择足够大的空间对象保存对端地址结构,例如传入sockaddr_in6对象,以满足对端是ipv4或ipv6地址,再根据fromlen来解析不同地址结构的信息。

1.2 accept

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *clientaddr, socklen_t* addrlen); // 成功返回非负的已连接套接字,出错返回-1

使用参数完全同recvfrom函数,获取地址结构也相同。注意返回值,成功时会返回一个非负的已连接套接字描述符(已经绑定对端地址信息)。我们可以利用这个返回值,调用getpeername获取对端客户端的地址信息

1.3 getsockname、getpeername

#include <sys/socket.h>
int getpeername (int sockfd, struct sockaddr *localaddr, socklen_t * addrlen);
int getsockname (int sockfd, struct sockaddr *peeraddr, socklen_t * addrlen);    
// 成功返回0,出错返回-1

两个函数放在一起说明,后两个参数含义、使用方法同上述recvfrom,要求参数sockfd是一个已连接的套接字

当使用getsockname时,sockfd 应该是一个已经绑定到本地地址的套接字,这个本地地址可以是手动bind或者由内核分配的。
当使用getpeername时,sockfd 应该是一个已经绑定对端地址的套接字,例如服务端经accept后的返回值套接字,客户端经过connect之后的本地套接字。

2、使用场景

用于udp和tcp客户端,不同客户端继续可以划分。

2.1、获取本地地址信息

2.1.1 UDP客户端获取本地地址

客户端bind或者connect之后使用 getsockname。为操作方便,先实现一个通用函数以打印输出本地地址信息。

(1) 输出本地地址函数

void print_getsockname(int socket_fd)
{sockaddr_storage storage; // 能够适应不同种类的地址协议结构socklen_t   sock_len = sizeof(storage);  // 必须给初值int ret = getsockname(socket_fd, (sockaddr*)&storage, &sock_len);     if(ret < 0){printf("getsockname error: %s\n", strerror(errno));return;}if (storage.ss_family == AF_INET){sockaddr_in* addr = (sockaddr_in* )&storage;printf("local addr: %s:%d\n", inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));}else if(storage.ss_family == AF_INET6){sockaddr_in6* addr = (sockaddr_in6* )&storage;char ip[INET6_ADDRSTRLEN];inet_ntop(AF_INET6, &addr->sin6_addr, ip, sizeof(addr));printf("local addr: %s:%d\n", ip, ntohs(addr->sin6_port));}  
}

(2)常规使用
对于未bind的套接字,必须发送数据后,内核会分配端口。

  /// 创建socketint socket_fd = socket(AF_INET,SOCK_DGRAM, 0);   // udp/// 连接sockaddr_in servaddr;servaddr.sin_family = AF_INET;inet_pton(AF_INET,"127.0.0.1", &servaddr.sin_addr);servaddr.sin_port = htons(8080);// 对于未bind的套接字,必须发送数据后,内核会分配端口int ret = sendto(socket_fd, "",0,0, (sockaddr*)&servaddr, sizeof(servaddr));if(ret < 0){printf("bind error: %s\n", strerror(errno));}/// 获取本地信息print_getsockname(socket_fd);/// 4、关闭连接::close(socket_fd);

print_getsockname(socket_fd);函数输出的ip地址默认是”0.0.0.0”,但是服务端是仍然能解析的。如下:

在这里插入图片描述
(3)使用connect函数

将上面常规方式中发送数据的代码替换成connect,效果一样。

  /// 创建socketint socket_fd = socket(AF_INET,SOCK_DGRAM, 0);   // udp/// 连接sockaddr_in servaddr;servaddr.sin_family = AF_INET;inet_pton(AF_INET,"127.0.0.1", &servaddr.sin_addr);servaddr.sin_port = htons(8080);int ret = ::connect(socket_fd, (sockaddr*)&servaddr, sizeof(servaddr));if(ret < 0){printf("bind error: %s\n", strerror(errno));return 0;}/// 获取本地信息print_getsockname(socket_fd);/// 4、关闭连接::close(socket_fd);

(4)使用bind

首先,替换常规方法中发送数据部分,仅调用bind后执行print_getsockname函数。结果相对会存在一些问题。例如:

ip指定为127.0.0.1, port指定9000,bind成功后,getsockname函数返回正常;
ip指定127.0.0.1, port指定0,bind成功后,getsockname函数返回正常,端口为内核分配;
ip指定INADDR_ANY, port指定0,bind成功后,getsockname函数返回ip为”0.0.0.0”, 端口为内核分配。

接着,bind之后,先发送任意数据到服务端,再调用getsockname,结果不变。
换句话说,使用bind后,端口要要么是指定的,要么是内核分配的,最终的端口getsockname都能正确获取;当地址为通配INADDR_ANY时,最终选择的地址getsockname没有办法知道的

(5)不常见的使用方法

见2.2.1节。

2.1.2 TCP客户端获取本地地址

对于TCP客户端,使用bind后再调用getsockname的结果,也是跟UDP的情况一样。
TCP客户端获取本地地址,常规使用connect函数,之后调用getsockname。代码如下

  /// 创建socketint socket_fd = socket(AF_INET,SOCK_STREAM, 0);   // tcp/// 连接sockaddr_in servaddr;servaddr.sin_family = AF_INET;inet_pton(AF_INET,"127.0.0.1", &servaddr.sin_addr);servaddr.sin_port = htons(8080);int ret = ::connect(socket_fd, (sockaddr*)&servaddr, sizeof(servaddr));if(ret < 0){printf("bind error: %s\n", strerror(errno));return 0;}/// 获取本地信息print_getsockname(socket_fd);/// 4、关闭连接::close(socket_fd);

结果如下
在这里插入图片描述
当先使用bind指定端口,且指定地址为INADDR_ANY, 再经过connect之后,能够使用getsockname获取最终tcp选择的ip地址。
如bind部分代码

  /// 创建socketint socket_fd = socket(AF_INET,SOCK_STREAM, 0);   // tcp/// 连接sockaddr_in servaddr;servaddr.sin_family = AF_INET;inet_pton(AF_INET,"127.0.0.1", &servaddr.sin_addr);servaddr.sin_port = htons(8080);/// bind 或 connectint ret;sockaddr_in localaddr;localaddr.sin_family = AF_INET;localaddr.sin_addr.s_addr = INADDR_ANY;localaddr.sin_port = htons(9000);ret = ::bind(socket_fd, (sockaddr*)&localaddr, sizeof(localaddr));if(ret < 0){printf("bind error: %s\n", strerror(errno));}ret = ::connect(socket_fd, (sockaddr*)&servaddr, sizeof(servaddr));if(ret < 0){printf("bind error: %s\n", strerror(errno));return 0;}/// 获取本地信息print_getsockname(socket_fd);/// 4、关闭连接::close(socket_fd);

在这里插入图片描述

2.2、获取对端地址信息

获取对端的地址,UDP主要通过recvfrom函数, TCP主要通过getpeername函数。

2.2.1 UDP中获取对端地址

UPP服务端和客户端都可能使用recvfrom函数接收来自对端的数据,同时也能获取对端的地址信息。以服务端为例,如下

  /// 创建socketint socket_fd = socket(AF_INET,SOCK_DGRAM, 0);  // udp/// bindint ret;sockaddr_in localaddr;localaddr.sin_family = AF_INET;inet_pton(AF_INET, "127.0.0.1", &localaddr.sin_addr);localaddr.sin_port = htons(8080);ret = ::bind(socket_fd, (sockaddr*)&localaddr, sizeof(localaddr));if(ret < 0){printf("bind error: %s\n", strerror(errno));}/// 接收char buf[1024];int len;while(true){sockaddr_storage storage;socklen_t   sock_len = sizeof(storage);  // 必须给初值len = ::recvfrom(socket_fd, buf, sizeof(buf), 0, (struct sockaddr *)&storage, &sock_len);if (len < 0){printf("recv failed. err %s\n", strerror(errno));return;}buf[len] = '\0';/// 输出对端信息if (storage.ss_family == AF_INET){sockaddr_in* addr = (sockaddr_in* )&storage;char ip[INET_ADDRSTRLEN];inet_ntop(AF_INET, &addr->sin_addr, ip, sock_len);printf("recv client [%s:%d] %2d: %s", ip, ntohs(addr->sin_port), len, buf);}else if(storage.ss_family == AF_INET6){sockaddr_in6* addr = (sockaddr_in6* )&storage;char ip[INET6_ADDRSTRLEN];inet_ntop(AF_INET6, &addr->sin6_addr, ip, sock_len);printf("recv client [%s:%d] %2d: %s", ip, ntohs(addr->sin6_port), len, buf);}  }// 关闭连接::close(socket_fd);

函数recvfrom返回成功后,参数storage、sock_len存储了对端的地址信息,同前面获取本地地址一样解析,根据长度或协议类型进行解析即可。

UDP客户端使用中,有一个不常用的方法获取本地信息,即通过connect后,使用getsockname获取本地信息

  int socket_fd = socket(AF_INET,SOCK_DGRAM, 0);   // udp/// 连接sockaddr_in servaddr;servaddr.sin_family = AF_INET;inet_pton(AF_INET,"127.0.0.1", &servaddr.sin_addr);servaddr.sin_port = htons(8080);::connect(socket_fd, (sockaddr*)&servaddr, sizeof(servaddr));/// 获取地址信息print_getpeername(socket_fd);   // 函数见下一节 TCP中获取对端地址print_getsockname(socket_fd);  // 当前socket_fd是一个已连接的UDP套接字, 理论上需要使用write或send函数//::sendto(socket_fd,"123",3, 0, (sockaddr*)&servaddr, sizeof(servaddr));::write(socket_fd,"123",3);  // 关闭连接::close(socket_fd);

在这里插入图片描述

2.2.2 TCP中获取对端地址

TCP服务端直接使用accept,能够接收客户端的连接,并且能够获取客户端的地址信息;其次,accept返回值是当前客户端已连接的套接字,可以使用getpeername获取客户端地址信息。

TCP客户端也可以在connect之后,调用getpeername获取服务端信息。

(1)使用accept直接获取对端信息

  /// 创建socketint socket_fd = socket(AF_INET, SOCK_STREAM, 0); // tcp/// bindint ret;sockaddr_in localaddr;localaddr.sin_family = AF_INET;inet_pton(AF_INET, "127.0.0.1", &localaddr.sin_addr);localaddr.sin_port = htons(8080);ret = ::bind(socket_fd, (sockaddr *)&localaddr, sizeof(localaddr));// 监听::listen(socket_fd, 5);sockaddr_storage storage;socklen_t sock_len = sizeof(storage); // 必须给初值::accept(socket_fd, (sockaddr *)&storage, &sock_len);/// 输出对端信息if (storage.ss_family == AF_INET){sockaddr_in *addr = (sockaddr_in *)&storage;char ip[INET_ADDRSTRLEN];inet_ntop(AF_INET, &addr->sin_addr, ip, sock_len);printf("client [%s:%d] \n", ip, ntohs(addr->sin_port));}else if (storage.ss_family == AF_INET6){sockaddr_in6 *addr = (sockaddr_in6 *)&storage;char ip[INET6_ADDRSTRLEN];inet_ntop(AF_INET6, &addr->sin6_addr, ip, sock_len);printf("client [%s:%d] \n", ip, ntohs(addr->sin6_port));}// 关闭连接::close(socket_fd);

经过accept之后,需要根据协议类型解析地址信息。测试代码反复运行,结果如下:
在这里插入图片描述
(2)使用accept返回值调用getpeername
注意,传递给getpeername的是函数accept的返回值sock_id,这个是已连接的客户端套接字,不是服务端的套接字sock_fd。
其中包含了print_getpeername()的代码,见下面注释。演示效果同使用accept直接获取对端地址信息方法。

  /// 创建socketint socket_fd = socket(AF_INET, SOCK_STREAM, 0); // tcp/// bindint ret;sockaddr_in localaddr;localaddr.sin_family = AF_INET;inet_pton(AF_INET, "127.0.0.1", &localaddr.sin_addr);localaddr.sin_port = htons(8080);ret = ::bind(socket_fd, (sockaddr *)&localaddr, sizeof(localaddr));// 监听::listen(socket_fd, 5);// 等待连接int sock_id = ::accept(socket_fd, NULL, NULL); /// 输出对端信息,实际是 print_getsockname()函数;sockaddr_storage storage;socklen_t sock_len = sizeof(storage); // 必须给初值ret = ::getpeername(sock_id, (sockaddr *)&storage, &sock_len); // 注意是sock_idif (ret < 0){printf("getpeername error: %s\n", strerror(errno));}else{if (storage.ss_family == AF_INET){sockaddr_in *addr = (sockaddr_in *)&storage;char ip[INET_ADDRSTRLEN];inet_ntop(AF_INET, &addr->sin_addr, ip, sock_len);printf("client [%s:%d] \n", ip, ntohs(addr->sin_port));}else if (storage.ss_family == AF_INET6){sockaddr_in6 *addr = (sockaddr_in6 *)&storage;char ip[INET6_ADDRSTRLEN];inet_ntop(AF_INET6, &addr->sin6_addr, ip, sock_len);printf("client [%s:%d] \n", ip, ntohs(addr->sin6_port));}}// 关闭连接::close(sock_id);::close(socket_fd);

3、总结

3.1 获取对端地址信息

recvfrom------------- 多用于udp服务端和客户端
accept --------------- 用于tcp服务端
getpeername ------ tcp服务端要在accept之后,tcp/udp客户端要在connect之后

3.2 获取本地地址信息

getsockname ----- 可以直接在bind后获取准确的port,在connect、accept之后可以获取准确的port和ip。

3.3 解析地址信息

接收地址新的对象空间足够大,根据函数返回的地址信息长度或者协议类型进行解析。
以Ipv4和ipv6地址为例,选择sockaddr_in6时根据长度解析,选择sockaddr_storage时根据协议解析。

http://www.hengruixuexiao.com/news/41481.html

相关文章:

  • 建设网站前的目的app拉新推广平台有哪些
  • wordpress查看留言北京网络推广公司wyhseo
  • 做网站要注意什么重庆seo职位
  • 上海医疗旅游开发网站建设小红书seo排名
  • 长清做网站公司项目推广方案怎么写
  • 贵阳做企业网站百度官方网站网址是多少
  • 景观规划设计公司搜索引擎优化是什么?
  • 上海 .net网站建设能搜任何网站的浏览器
  • 北京网站建设 性价比学seo推广
  • 上海行业门户网站建设工具成都网络推广优化
  • 江西做网站哪家好广告策划方案范文
  • 武汉高端商城网站建设网络营销的实现方式
  • 电影网站模板html百度搜索关键词排名优化
  • 易语言做钓鱼网站如何搭建一个自己的网站
  • 博山网站建设建立网站
  • 网站开发大致过程企业网络营销策划书
  • 网站建设中html模板网址域名查询ip地址
  • 网站建设沈阳天堂网长尾关键词挖掘网站
  • 潍坊疫情最新情况北京网站优化方式
  • 网站修改数据国内搜索引擎有哪些
  • 多城市网站建设淘宝友情链接怎么设置
  • 网站建设知乎核酸检测最新消息
  • 网站做跳转怎么做家庭优化大师免费下载
  • 百度推广送企业网站吗电商网站seo优化
  • 怎么将网站关键词排名首页网络销售哪个平台最好
  • 网站建设色彩企业网站推广方法实验报告
  • 新浪博客怎么做网站搜索关键词的方法
  • 小公司做网站的实力网络营销的主要推广方式
  • 防红短链接生成小红书seo是什么意思
  • wordpress上传参数有哪些文登seo排名