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

购物网站界面设计策划东莞网站自动化推广

购物网站界面设计策划,东莞网站自动化推广,引流获客app下载,网站建设jw100文章目录 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.yayakq.cn/news/226954/

相关文章:

  • wordpress可以做门户网站wordpress登陆维护
  • 公司网站建设的市场需求重庆市公共资源交易中心网站
  • 一个网站百度百科怎么做园林设计公司网站
  • 重庆网站制作公司 做服装设计兼职的网站
  • 慕课网网站建设目的用数字做域名的网站
  • 建设银行网站的目的江苏纬信网站建设
  • 东阿做网站多少钱李宁网站建设计划书
  • 海口企业建站系统模板酒店网站开发协议
  • 深圳建设执业注册中心网站南昌企业建设网站设计
  • 门户网站维护怎么做wordpress 打不开 怎么办
  • 常见的静态网站开发技术网站互动
  • 北京响应式网站建设报价wordpress支持空格键
  • 出售东西的网站怎么做女生做网站运营
  • 品牌网站建设堅持大蝌蚪公众号平台怎么做
  • 建一网站要多少钱深圳网络推广市场
  • 甘孜网站建设恩施州建设局网站
  • 宁夏考试教育网站鞍山一地发布最新通知
  • 清远住房和城乡建设部网站潍坊哪家做网站做的最好
  • 建设项目备案网站中山百度首页推广
  • 怎样创造一个网站昌平上门做网站那
  • 游艇 高端网站建设西安今天出大事
  • 做贸易的网站企业名录搜索软件免费
  • 微网站建设计划书wordpress填写数据库
  • 2017年内蒙古建设厅网站中山网站免费制作
  • 简洁风格的网站模板网络运维工程师需要学什么
  • 开网络公司需要多少资金搜索引擎的网站优化
  • 可以上传资源的网站开发费用thinkphp做的网站源码
  • 莆田哪里有做网站的安徽建站模板
  • 营销型网站建设的5大技巧图片在线制作网站
  • 网站推广的方式?中移电子商务有限公司