第07章IPv4和IPv6编程
- 格式:ppt
- 大小:198.00 KB
- 文档页数:14
IPv的地址类型和编码规则IPv是Internet Protocol的缩写,是互联网上使用的一种网络协议。
它定义了计算机在互联网上进行通信时所使用的地址类型和编码规则。
本文将介绍IPv的地址类型以及相关的编码规则。
一、IPv4地址类型和编码规则IPv4(Internet Protocol version 4)是互联网上广泛采用的一种网络协议版本,它采用32位的地址长度,共分为四个8位组(也称为四个字节)。
IPv4地址的编码规则如下:1. 地址类型:IPv4地址分为公网地址和私有地址。
公网地址用于在互联网上进行通信,而私有地址则用于内部网络中的通信。
IPv4私有地址范围为10.0.0.0至10.255.255.255、172.16.0.0至172.31.255.255、192.168.0.0至192.168.255.255。
2. 地址分配:IPv4地址的分配由互联网号码分配机构(IANA)负责。
根据地址的需求和分配原则,全球的地址资源被分配给各个地区的注册局,再由注册局将地址块分配给网络服务提供商、企业或机构。
3. 地址转换:由于IPv4地址数量有限,为了解决地址短缺问题,采用了地址转换技术。
其中最常见的是网络地址转换(NAT),通过在内部网络和公网之间转换IP地址,实现多个设备共享一个公网IP地址。
4. 地址格式:IPv4地址通常以点分十进制表示法表示,如192.168.0.1。
每个八位组可以表示0~255之间的十进制数,共计2^32(约42亿)个地址。
二、IPv6地址类型和编码规则IPv6(Internet Protocol version 6)是IPv4的下一代网络协议版本,采用128位的地址长度,相比IPv4,IPv6拥有更多的地址空间。
IPv6地址的编码规则如下:1. 地址类型:IPv6地址分为单播地址、多播地址和任播地址。
单播地址用于一对一的通信,多播地址用于一对多的通信,任播地址用于一对多的通信,但只选择最近的一个节点进行通信。
使用 IPv4 兼容地址使用 IPv4 兼容地址派生自 IPv4 公用地址的 IPv4 兼容地址可以为通过现有 IPv4 Internet 结构连接 IPv6 主机或站点提供一种方法。
使用 IPv4 兼容地址时,IPv6 通信不要求其他的 IPv6 路由器。
将用 IPv4 标头封装它的通信。
下图显示了使用 IPv4 兼容地址跨 IPv4 路由器通信的独立子网上两个节点的配置。
启用兼容 IPv4 的 IPv6 地址时,Windows Server 2003 家族和 Windows XP 的IPv6 协议会将兼容 IPv4 的地址自动配置为“自动隧道伪接口(接口 ID 2)”上的 IPv4 公用地址。
IPv4 兼容地址的格式是 ::w.x.y.z,其中w.x.y.z是一个指派给计算机上接口的 IPv4 公用地址。
启用 IPv4 兼容地址时,IPv6 协议也自动创建一个 ::/96 路由,该路由使用“自动隧道伪接口”(接口 ID 为 2)转发所有 IPv4 兼容地址通信。
由此主机转发到 IPv4 兼容目标的所有通信都将用 IPv4 标头封装。
默认情况下,禁用 IPv4 兼容地址。
要启用兼容 IPv4 的地址,请打开“命令提示符”,然后键入:netsh interface ipv6 set state v4compat=enabled将通信发送到 IPv4 兼容地址时,将从 IPv4 兼容地址发送通信,并用 IPv4 标头封装。
IPv4 标头中的“协议”字段将被设置为 41,表示负载是 IPv6 数据包。
IPv4 标头允许跨 IPv4 结构进行通信。
嵌入在 IPv6 标头的源和目标 IPv4 兼容地址中的 IPv4 地址,将成为 IPv4 标头中的 IPv4 源和目标地址。
例如,当主机 A(用 IPv4 地址 131.107.41.17 配置)使用 IPv4 兼容地址将IPv6 通信发送给主机 B(用 IPv4 地址 157.60.15.93 配置)时,用于 IPv4 和IPv6 标头的源地址和目标地址在下表中列示。
IPV4与IPV6 兼容的socket编程套接字Socket可以看成在两个程序进行通讯连接中的一个端点,一个程序将一段信息写入Socket中,该Socket将这段信息发送给另外一个Socket中,使这段信息能传送到其他程序中。
生成套接字,主要有3个参数:通信目的IP地址、使用的协议,使用的端口号。
通过将这3个参数结合起来,应用层就可以和传输层(或网络层)通过套接字接口,区分来自不同应用程序进程或网络连接的通信。
Socket通讯流程图:TCP编程的服务器端一般步骤是:1、创建一个socket,用函数socket();2、设置socket属性,用函数setsockopt(); * 可选3、绑定IP地址、端口等信息到socket上,用函数bind();4、开启监听,用函数listen();5、接收客户端上来的连接,用函数accept();6、收发数据,用函数send()和recv(),或者read()和write(); 之后close(clientsocket);7、关闭网络连接; close(socket)TCP编程的客户端一般步骤是:1、创建一个socket,用函数socket();2、设置socket属性,用函数setsockopt();* 可选3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选4、设置要连接的对方的IP地址和端口等属性;5、连接服务器,用函数connect();6、收发数据,用函数send()和recv(),或者read()和write();7、关闭网络连接;UDP编程的服务器端一般步骤是:1、创建一个socket,用函数socket();2、设置socket属性,用函数setsockopt();* 可选3、绑定IP地址、端口等信息到socket上,用函数bind();不需要listen()4、数据交互,用函数recvfrom(); sendto()5、关闭网络连接;UDP编程的客户端一般步骤是:1、创建一个socket,用函数socket();2、设置socket属性,用函数setsockopt();* 可选3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选4、设置对方的IP地址和端口等属性;函数connect();* 可选5、数据交互,用函数sendto(); recvfrom();如果调用了connect就直接可以用send和recv了,这时最后两个参数会自动用connect建立时的地址信息填充6、关闭网络连接;几个Socket 地址结构相关结构体注:在操作系统(内核)不同情况下,首两个字节可能是length+family#ifdef ISC_PLATFORM_HA VESALENntp_u_int8_t ss_len; /* address length */ntp_u_int8_t ss_family; /* address family */#elseshort ss_family; /* address family */#endifstruct in_addr {u_int32_t s_addr; /* 4bytes, IPv4 address */};=4struct in6_addr {u_int8_t s6_addr[16]; /* IPv6 address */}=16一般socket函数都是传入的这个类型指针struct sockaddr {sa_family_t sa_family; /* unsigned short address family, AF_xxx */ char sa_data[14]; /* 14 bytes of protocol address */};2+14=16struct sockaddr_in {short int sin_family; /* Address family */unsigned short int sin_port; /* Port number */struct in_addr sin_addr; /* Internet address */unsigned char sin_zero[8]; /* Same size as struct sockaddr */};2+2+4+8=16struct sockaddr_in6{sa_family_t sin6_family; //地址簇类型,为AF_INET6in_port_t sin6_port; //16 位端口号,网络字节序uint32_t sin6_flowinfo; //32 位流标签struct in6_addr sin6_addr; //128 位IP 地址/*uint32_t sin6_scope_id;*/(新版本可能会多4字节scope id, RFC2553)}2+2+4+16=24#define UNIX_PATH_MAX 108struct sockaddr_un {sa_family_t sun_family; /* AF_UNIX,本地进程通讯*/char sun_path[UNIX_PATH_MAX]; /* pathname */};=110例:my_addr.sun_family = AF_UNIX;strncpy(my_addr.sun_path, MY_SOCK_PATH, sizeof(my_addr.sun_path) - 1);sockaddr_storage能提供严格的结构对齐sockaddr_storage能容纳系统支持的更大的地址结构,容器在使用sockaddr_storage定义的变量时,尽量强制转换成struct sockaddr, struct sockaddr_in, struct sockaddr_in6后操作struct __kernel_sockaddr_storage {unsigned short ss_family; /* address family *//* Following field(s) are implementation specific */char __data[_K_SS_MAXSIZE - sizeof(unsigned short)];/* space to achieve desired size, *//* _SS_MAXSIZE value minus size of ss_family */} __attribute__ ((aligned(_K_SS_ALIGNSIZE))); /* force desired alignment */#define sockaddr_storage __kernel_sockaddr_storage=128不同于sockaddr_storag的一个IPV4,IPV6兼容结构体(来源于busybox)typedef struct len_and_sockaddr {socklen_t len;union {struct sockaddr sa;struct sockaddr_in sin;struct sockaddr_in6 sin6;};} len_and_sockaddr;用struct sockaddr_storage的好处,看:取得res后声明一个struct sockaddr_storage变量以兼容IPV4和IPV6 //….getaddrinfo后建立socketstruct sockaddr_storage local_addr;memset(&local_addr, 0, sizeof(struct sockaddr_storage));if(res->ai_family == AF_INET6){struct sockaddr_in6 *local_addr6 = (struct sockaddr_in6 *)&local_addr;local_addr6->sin6_family = AF_INET6;local_addr6->sin6_port = htons(udp_local_port);local_addr6->sin6_addr = in6addr_any;}else if(res->ai_family == AF_INET){struct sockaddr_in *local_addr4 = (struct sockaddr_in *)&local_addr;local_addr4->sin_family = AF_INET;local_addr4->sin_port = htons(udp_local_port);local_addr4->sin_addr.s_addr = htonl(INADDR_ANY);}if(bind(usd, (struct sockaddr *)&local_addr, sizeof(local_addr)) == -1){perror("bind");fprintf(stderr, "could not bind to local udp port %d\n", udp_local_port);exit(1);}freeaddrinfo(res);//….send recv等数据交互一些重要函数主机字节序到网络字节序间的转换函数uint16_t htons(uint16_t hostshort);uint16_t ntohs(uint16_t netshort);uint32_t htonl(uint32_t hostlong);uint32_t ntohl(uint32_t netlong);例:本机字节序与网络字节序short int port=0x1234; //本地存储, windows为小端系统,所以存34 12 sockaddr_in.sin_port = htons(port); //网络传输,TCP包头里的数据顺序IPV4字符串地址和网络序ip地址的转换函数int inet_aton(const char *cp, struct in_addr *inp);char *inet_ntoa(struct in_addr in);in_addr_t inet_addr(const char *cp);IPV6,IPV4兼容的字符串地址和网络序ip地址的转换函数int inet_pton(int af, const char *src, void *dst);const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);两个地址长度的宏#define INET_ADDRSTRLEN 16#define INET6_ADDRSTRLEN 46IPV4主机名和地址的转换struct hostent *gethostbyname(const char *name);struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);IPV6,IPV4兼容的主机名和地址的转换函数struct hostent *gethostbyname2(const char *name, int af); //扩展版本,也支持IPV6int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);void freeaddrinfo(struct addrinfo *res); //调用时机:在bind或connect之后就可以释放该内存int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags);getaddrinfo函数原型addrinfo结构首先了解下:1.通配地址IPV4:0.0.0.0IPV6:::stat指令查询监听,绑定等的IP和port信息作为服务器端时:struct addrinfo hints, *res=NULL;memset(&hints,0,sizeof(hints));hints.ai_family=AF_UNSPEC;//hints.ai_family=AF_INET6;hints.ai_socktype=SOCK_DGRAM;//hints.ai_protocol=IPPROTO_UDP;hints.ai_flags=AI_PASSIVE;rc=getaddrinfo(NULL,"123",&hints,&res);socket=socket(res->ai_family,res->ai_socktype,res->ai_protocol);bind (socket,res->ai_addr,res->ai_addrlen);hints可看作是过滤条件res返回一个地址链表,一般来说用第一个bind成功就可以了可以看到sa_addr的sa_data在端口后面是全0的,相当于是通配地址因此其相当于下面不调用getaddrinfo方法的简化版本:usd = socket(AF_INET6, SOCK_DGRAM, 0);struct sockaddr_storage local_addr;memset(&local_addr, 0, sizeof(struct sockaddr_storage));struct sockaddr_in6 *local_addr6 = (struct sockaddr_in6 *)&local_addr;local_addr6->sin6_family = AF_INET6;local_addr6->sin6_port = htons(123);local_addr6->sin6_addr = in6addr_any;if(bind(usd, (struct sockaddr *)&local_addr, sizeof(local_addr)) == -1)……提示:用IPV6建立服务器端的话, 即使客户端仍用IPV4的socket连接也可以正常通讯, IPV4的地址会被转换成这种地址::ffff:192.168.27.25而旧的IPV4时用以下代码:usd = socket(AF_INET, SOCK_DGRAM, 0);struct sockaddr_in servaddr;bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(123);servaddr.sin_addr.s_addr = htons(INADDR_ANY);if (bind(servfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)……服务器端从IPV4移植到IPV6要做些什么?可以调用getaddrinfo得到AF_INET6的通配地址,也可以直接将sockaddr_in结构体更改为sockaddr_in6结构体并相应初始化即可客户器端如何同时兼容IPV4和IPV6?按如上方法就可同时兼容IPV4和IPV6的连接请求作为客户器端时:memset(&hints,0,sizeof(hints));hints.ai_family=AF_UNSPEC;hints.ai_socktype=SOCK_DGRAM;//hints.ai_protocol=IPPROTO_UDP;hints.ai_flags=AI_CANONNAME;rc=getaddrinfo("","ntp",&hints,&res);socketfd=socket(res->ai_family,res->ai_socktype,res->ai_protocol);connect(sockfd,res->ai_addr , res->ai_addrlen);如图res返回两个ai_addr,一个sa_family是0x2为AF_INET,另一个为0x17 AF_INET6;说明该主机既有AAAA(或A6)记录(IPV6)地址,同时又有A记录(IPV4)地址,那么AAAA 记录将作为sockaddr_in6结构返回,而A记录则作为sockaddr_in结构返回dig AAAA 客户器端从IPV4移植到IPV6要做些什么?如果服务器端具有IPV4地址则基本不需改动,因为IPV6的服务器对IPV4客户端是兼容的,如果是域名且是IPV6的地址则必须调用getaddrinfo得到addrinfo的可用IP地址。
ipv4与ipv6兼容socket编程//客户端代码#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#includeint main(int argc,char *argv[]){struct addrinfo *ailist,*aip;struct addrinfo hint;struct sockaddr_in6 *sinp;int sockfd;int err;int i=0;char seraddr[INET_ADDRSTRLEN];short serport;char msg[200];int len;if(argc !=2){printf("usage:%s \n",argv[0]);exit(-1);}hint.ai_family=0;hint.ai_socktype=SOCK_DGRAM;hint.ai_flags=AI_CANONNAME;hint.ai_protocol=0;hint.ai_addrlen=0;hint.ai_addr=NULL;hint.ai_canonname=NULL;hint.ai_next=NULL;if((err=getaddrinfo(argv[1],"8888",&hint,&ailist))!=0){printf("getaddrinfo error: %s\n",gai_strerror(err));exit(-1);}printf("getaddrinfo ok\n");for(aip=ailist;aip!=NULL;aip=aip->ai_next){sinp=(struct sockaddr_in6 *)aip->ai_addr;if(inet_ntop(sinp->sin6_family,&sinp->sin6_addr,seraddr,INE T_ADDRSTRLEN)!=NULL){printf("server address is %s \n",seraddr);}serport=ntohs(sinp->sin6_port);printf("server port is %d \n",serport);if((sockfd=socket(aip->ai_family,SOCK_DGRAM,0))<0){printf("create socket failed : %s\n",strerror(errno));}printf("create socket ok \n");while(1){bzero(msg,sizeof(msg));len=read(STDIN_FILENO,msg,sizeof(msg));if(sendto(sockfd,msg,sizeof(msg),0,aip->ai_addr,aip->ai_add rlen)<0){printf("error");exit(1);}if((len=recvfrom(sockfd,msg,sizeof(msg),0,aip->ai_addr,&ai p->ai_addrlen))<0){perror("recvfrom error");exit(1);};printf("%d:",i);i++;printf("Received message : %s\n",msg);}}exit(0);}//服务器端代码#include#include#include#include#include#include#include#include#include#include#include#include#include#includeint main(int argc,char *argv[]){if(argc !=2){printf("参数格式错误!正确的格式是:\n\t\t%s 端口\t 比如:\t%s 80 \n",argv[0],argv[0]);}struct addrinfo hints,*res,*ressave;char msg[200];char buf[300];size_t addrlen;bzero(&hints,sizeof(struct addrinfo)); res=NULL;ressave=NULL;hints.ai_flags=AI_PASSIVE;hints.ai_family=0;hints.ai_socktype=SOCK_DGRAM; hints.ai_protocol=IPPROTO_UDP; hints.ai_addrlen=0;hints.ai_addr=NULL;hints.ai_canonname=NULL;hints.ai_next=NULL;int err;int i=0;int len;int addr_len;if((err=getaddrinfo(NULL,argv[1],&hints,&res))!=0) {perror("getaddrinfo error");exit(1);}ressave=res;int mySock;if(!res){printf("res is NULL\n");}struct sockaddr_storage addr;bzero(&addr,sizeof(addr));while(res){mySock=socket(res->ai_family,res->ai_socktype,res->ai_pro tocol);if(mySock<0){perror("sock error");continue;}const int on=1;setsockopt(mySock,SOL_SOCKET,SO_REUSEADDR,&on,sizeo f(on));if(bind(mySock,res->ai_addr,res->ai_addrlen)==0){printf("bind success \n");break;}else{perror("bind error");}close(mySock);res=res->ai_next;}if(!res){printf("failed to get a address for service \n");}addr_len=sizeof(struct sockaddr_storage);while(1){bzero(msg,sizeof(msg));printf("it is recving !\n");len=recvfrom(mySock,msg,sizeof(msg),0,ressave->ai_addr, &ressave->ai_addrlen);printf("%d:",i);printf("addr->sa_family:%d",ressave->ai_family);printf("%s\n",msg);i++;if(sendto(mySock,msg,len,0,ressave->ai_addr,ressave->ai_a ddrlen)<0){perror("error");exit(1);}}}。
LINUX IPV6 IPV4 兼容编程 HTTP SERVER 源代码讲解代码是在linux上运行的,winsock的已经有人写了IPv4转换示例192.168.0.77c:::ffff:192.168.0.77。
打印后,删除::ffff:以获取IPv4地址有些函数比如debug_printf什么的我就不贴了,太多了看不过来,重点看红色标注部分intmain(){intlistenfd=-1,connectfd;pthread_utthread;//idofthreadstructarg*arg;//passthisvartothethreadcharbuf[bufsize];chartemp[bufsize];调试打印(\intport=80;调试打印(\memset(temp,0x0,bufsize);sprintf(temp,\//配置地址信息structaddrinfoaddrcriteria;memset(&addrcriteria,0,sizeof(addrcriteria));addrcriteria。
ai_uufamily=af_uuuunsec;addrcriteria。
ai_u标志=ai_u被动;addrcriteria.ai_socktype=sock_stream;addrcriteria.ai_protocol=ipproto_tcp;//获取地址信息。
此函数是必需的,因为接口同时具有IPv6和IPv4。
它通常位于Dr_uAnyStructAddRinfo*服务器uAddr;intretval=getaddrinfo(null,temp,&addrcriteria,&server_addr);if(retval!=0){调试打印(\}structaddrinfo*addr=server_addr;//获得的地址是一个链表。
事实上,它只是以下地址的IPv6地址:while(addr!=null){//建立套接字listenfd=socket(addr->ai_family,addr->ai_socktype,addr->ai_protocol);if(listenfd<0)continue;//绑定端口和侦听端口intopt=so_uureuseaddr;setsockopt(listenfd,sol_socket,so_reuseaddr,&opt,sizeof(opt));//可重用fcntl(listenfd,f_setfd,fd_cloexec);fcntl(列表、f_设置、o_非块);//非阻塞if((bind(listenfd,addr->ai_addr,addr->ai_addrlen)==0)&&listen(listenfd,128)==0){structsockaddr_uuStorageLocal_uuAddr;socklen_uutaddr_uusize=sizeof(本地地址);if(getsockname(listenfd,(structsockaddr*)&local_addr,&addr_size)<0){调试\uPrintf(\}debug_printf(\printsocketaddress((StructSocketAddr*)和local_uuAddr);//一般来说:break;}close(listenfd);addr=addr->ai_uuNext;}freeaddrinfo(server_addr);//事实上,以上都是胡说八道。
ipv4和ipv6正则表达式IPv4和IPv6正则表达式IPv4和IPv6是互联网中常用的两种IP地址类型。
为了验证和匹配这两种类型的IP地址,可以使用正则表达式。
本文将介绍如何使用正则表达式来验证和匹配IPv4和IPv6地址。
一、IPv4正则表达式IPv4地址由四个由点分隔的十进制数字组成,每个数字的范围是0-255。
下面是一个用于匹配IPv4地址的正则表达式示例:^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$正则表达式解析:- ^(开始)和$(结束)限制了整个字符串必须是一个IPv4地址,没有其他额外的字符。
- (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)匹配每个点分隔的数字。
其中,25[0-5]匹配250-255之间的数字,2[0-4][0-9]匹配200-249之间的数字,[01]?[0-9][0-9]?匹配0-199之间的数字。
- \.匹配点。
使用该正则表达式,可以验证一个字符串是否是有效的IPv4地址。
二、IPv6正则表达式IPv6地址由八个由冒号分隔的十六进制数字组成,每个数字的范围是0-FFFF。
下面是一个用于匹配IPv6地址的正则表达式示例:^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$正则表达式解析:- ^和$限制了整个字符串必须是一个IPv6地址,没有其他额外的字符。
- (?:[0-9a-fA-F]{1,4}:){7}匹配七个由冒号分隔的十六进制数字。
[0-9a-fA-F]{1,4}匹配一个十六进制数字,{1,4}表示该数字的长度为1到4位。
- [0-9a-fA-F]{1,4}匹配最后一个十六进制数字。
使用该正则表达式,可以验证一个字符串是否是有效的IPv6地址。
三、如何使用正则表达式要使用正则表达式验证和匹配IPv4或IPv6地址,可以使用编程语言中支持正则表达式的函数或类库。