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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章