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