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