UNXI网络编程笔记 第十二章IPV4与IPV6的互操作性

IPv4客户与IPv6服务器:
:

地址转换是由服务器端处理的
假设服务器支持双协议栈,而且即有一个IPv4地址,由有一个IPv6地址。服务器绑定了IPv6的通配地址。此时IPv4客户端要与服务器通信:
1)IPv6服务器启动,创建IPv6套接字,并且绑定通配地址
2)IPv4客户通过域名解析getaddrinfo找到服务器主机的IPv4地址
3)客户调用connect连接服务器IPv4地址
4)服务器收到SYN报文,根据端口发现端口是绑定到IPv6地址上面的,所以设置一个标识指示这个连接应使用IPv4映射的IPv6地址。然后正常回复SYN报文,建立连接。连接建立后,accept返回给主机进程的客户端地址就是客户端的IPv4地址映射的IPv6地址。
5)当服务器向客户发送数据时,IP栈会把目的地址设置为客户端的IPv4地址。所以链路上的数据都是IPv4承载的。

IPv6客户与IPv4服务器:
:

此时客户需要连接IPv4服务器的‘IPv4地址映射的IPv6地址’
客户端向这个映射到IPv6地址发消息时,IP栈检查到报文到目的地址是一个映射地址,所以会把目的地址修改为IPv4地址。
客户端的一种比较好的实现方式是,在getaddrinfo时,设置addrinfo结构的ai_flags = AI_V4MAPPED | AI_ALL,这样如果服务器只有IPv4地址,DNS会把服务器的IPv4地址映射的IPv6地址返回给客户端,客户端需要遍历返回的addrinfo结构中的地址进行连接尝试。

//tcpclientv6.c
#include "common.h"

int main(int argc, char **argv){

	if(argc < 2){
		puts("usage:tcpclient hostname");
		return 1;
	}

	struct addrinfo hints,*result;
	bzero(&hints,sizeof(hints));
	bzero(&result,sizeof(result));
	hints.ai_family = AF_INET6;
	hints.ai_flags = AI_V4MAPPED | AI_ALL;
	
	int n = getaddrinfo(argv[1],NULL,&hints,&result);
	if(n != 0){
	    	printf("getaddrinfo error:%s\n",gai_strerror(n));
		return 1;
	}
	
	struct addrinfo *saveResult = result;
	int needtry = 1;
	
	while(needtry && result != NULL){	
		int sockfd;
		char buf[100];
		struct sockaddr_in6 serveraddr;
		bzero(&serveraddr,sizeof(serveraddr));
	
		serveraddr =*((struct sockaddr_in6*)(result->ai_addr));
	        serveraddr.sin6_port = htons(45000);
		
		char addrbuf[200];
		inet_ntop(result->ai_family,&(((struct sockaddr_in6*)(result->ai_addr))->sin6_addr.s6_addr),addrbuf,sizeof(addrbuf));
		printf("try %s:%d\n",addrbuf,45000);
	
	        sockfd = socket(result->ai_family,SOCK_STREAM,0);
		
		int rtn = connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
		if(rtn != 0){
			puts("connect error");
			result = result->ai_next;
			continue;
		}	
	
		puts("connecetd.");
		needtry = 0;
		
		fd_set readset;
		FD_ZERO(&readset);
		FD_SET(sockfd,&readset);
		int sockStdin = fileno(stdin);
		FD_SET(fileno(stdin),&readset);
	
		int stdineof = 0;
			
		while (1) {
			FD_SET(sockfd,&readset);
			int maxfd = sockfd;
			if(stdineof == 0) {
				FD_SET(sockStdin,&readset);
				maxfd = MAX(sockfd,fileno(stdin));	
			}	
			int nReady = select(maxfd + 1,&readset,NULL,NULL,NULL);
			if(nReady > 0){
				if(FD_ISSET(sockStdin,&readset)){
					int readNum = read(sockStdin,buf,sizeof(buf));
					if(readNum == 0){
						puts("eof, close socket");
						shutdown(sockfd,1);
						FD_CLR(sockStdin,&readset);
						stdineof = 1;
						continue;
	
					}
					write(sockfd,buf,readNum);
				}
	
				if(FD_ISSET(sockfd,&readset)){
					int readNum = read(sockfd,buf,sizeof(buf));
					if(readNum == 0){
						if(stdineof == 1) {
							puts("server close socket");
							return 0;
						}
						else err_sys("client read error");
					}
					write(fileno(stdout),buf,readNum);
				}
			}
		}		
	}
	freeaddrinfo(saveRes);
	return 0;
}

//pollSvr.c
#include "common.h"
#include <poll.h>
#include <limits.h>

void sig_chld(int signo){

	pid_t pid;
	int stat;
	//pid = wait(&stat);
	while( (pid = waitpid(-1,&stat, WNOHANG)) > 0)
		printf("child %d terminated\n",pid);
	return;
}

int main(int argc, char **argv) {

	int sockfd, clientfd;
	struct sockaddr_in6 serveraddr,clientaddr;
	char buf[100];
	bzero(&serveraddr,sizeof(serveraddr));
	bzero(&clientaddr,sizeof(clientaddr));
	serveraddr.sin6_family = AF_INET6;
	inet_pton(AF_INET6,"::0",&serveraddr.sin6_addr.s6_addr);
	serveraddr.sin6_port = htons(45000);

	sockfd = socket(AF_INET6,SOCK_STREAM,0);
	
	bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));

	listen(sockfd,10);

	Signal(SIGCHLD, sig_chld);
	
	printf("OPEN_MAX=%d\n",OPEN_MAX);
	struct pollfd client[OPEN_MAX];	

	int i;
	for(i = 0;i<OPEN_MAX;i++){
		client[i].fd = -1;
	}

	int maxi = 0;
    client[0].fd = sockfd;
    client[0].events = POLLRDNORM;

	while(1){
		int newMaxi = 0;
		for(int i = 0; i <= maxi; i++){
			if(client[i].fd != -1){
				newMaxi = i;
			}
		}
		maxi = newMaxi;
		int nready = poll(client,maxi+1,-1);


		for(i = 1; i <= maxi; i++){
			clientfd = client[i].fd;
			if(clientfd < 0) continue;

			if(client[i].revents & (POLLRDNORM | POLLERR)){
				int n = read(clientfd,buf,sizeof(buf));
				if(n < 0){
					client[i].fd = -1;
					close(clientfd);
					err_sys("read errof");
				}

				if(n == 0){
					puts("client close socket");
					client[i].fd = -1;
					close(clientfd);
					continue;
				}
				buf[n] = 0;
				printf("%s",buf);
				write(clientfd,buf,n);

				if(--nready <= 1)
					break;
			}
		}
		if(client[0].revents & POLLRDNORM){
                        socklen_t len = sizeof(clientaddr);
                        clientfd = accept(sockfd,(struct sockaddr*)&clientaddr,&len);
                        if(clientfd < 0)
                                err_sys("connect error");

                        inet_ntop(AF_INET6,&clientaddr.sin6_addr.s6_addr,buf,len);
                        printf("new connection from:%s:%d\n",buf,ntohs(clientaddr.sin6_port));
                        for(i = 1;i<OPEN_MAX;i++){
                                if(client[i].fd == -1){
                                        client[i].fd = clientfd;
                        		client[i].events = POLLRDNORM;
                                        break;
                                }
                        }

                        if(i == OPEN_MAX)
                                err_sys("too many sockets error");

                        maxi = MAX(i,maxi);

                        if(--nready <= 0)
                                continue;
                }
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值