tcp.cはgethostbyname()関数でサーバのIPアドレス、getservbyname()関数でポート番号を調べている。 これに対し、tcpv6c.cではgetaddrinfo()関数で上記の2つの関数の役割を果たしているため、コードが短くなる。
getaddrinfo()関数は、4つの関数getipnodebyname()、getipnodebyaddr()、 getservbyname()、getservbyport()の機能をまとめて一つのインターフェースにしたものである。gethostbynemep()関数は、IPv4ネットワークファミリにしかアクセスできない getipnodebyname()は複数のネットワークアドレスファミリーにアクセス可能。
IPv6アドレス体系の中には、IPv4射影アドレスと呼ばれるIPv6アドレスが存在する。IPv4射影IPv6アドレスとは、IPv6アドレスの上位80bitに0、81~96bit目に1を入れ、下位32bitにIPv4アドレスを埋め込んだアドレスである。AF_INET6ケットは、IPv6通信とIPv4通信の両方に使用できる。
IPv4射影アドレス
IPv4射影IPv6アドレスとは、 IPv6アドレスの上位80bitに0、81~96bit目に1を入れ、下位32bitにIPv4アドレスを 埋め込んだアドレスである。IPv6をサポートしていないノードのアドレスをIPv6アドレスとして表現するために用 いられている。
myclient6.c #include #include #include #include #include #include #include #include #include #include int main __P((int, char **)); int main(argc, argv) int argc; char **argv; { struct addrinfo info, *res; ssize_t l; int s; char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; char buf[1024]; char ip[256]; /* 引数の数をチェック */ if (argc != 3){ fprintf(stderr, "usage: %s host (port)\n",argv[0]); exit(EXIT_FAILURE); } /*アドレスをバイナリに変更*/ memset(&info, 0, sizeof(info)); info.ai_flags = AI_CANONNAME; info.ai_family = AF_UNSPEC; info.ai_socktype = SOCK_STREAM; if (getaddrinfo(argv[1],argv[2], &info, &res) != 0) { perror("getaddrinfo"); exit(EXIT_FAILURE); } s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s < 0) { perror("socket"); exit(EXIT_FAILURE); } getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); fprintf(stderr, "trying %s port %s\n", hbuf, argv[2]); if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { perror("connect"); exit(EXIT_FAILURE); } while ((l = read(s, buf, sizeof(buf))) > 0) write(STDOUT_FILENO, buf, l); close(s); return EXIT_SUCCESS; }
myserver6.c #include #include #include #include #include #include #include #include #include #include int main __P((int, char **)); int main(argc, argv) int argc; char **argv; { struct servent *sp; unsigned long lport; u_int16_t port; char *ep; struct sockaddr_in6 serv; int servlen; struct sockaddr_in6 from; socklen_t fromlen; int s; int ls; char hbuf[INET6_ADDRSTRLEN]; if (argc != 2) { fprintf(stderr, "usage: test (port)\n"); exit(1); /*NOTREACHED*/ } else{ sp = getservbyname(argv[1], "tcp"); if (sp) port = sp->s_port & 0xffff; else { ep =NULL; errno = 0; lport =strtoul(argv[1], &ep, 10); if (!*argv[1] || errno || !ep || *ep) { fprintf(stderr, "%s: no such service\n", argv[1]); exit(1); /*NOTREACHED*/ } if (lport & ~0xffff) { fprintf(stderr, "%s: out of range\n", argv[1]); exit(1); /*NOTREACHED*/ } port = htons(lport & 0xffff); } } endservent(); memset(&serv, 0, sizeof(serv)); serv.sin6_family = AF_INET6; /* linux/Solarisでは以下の行は不要 */ serv.sin6_len = sizeof(struct sockaddr_in6); serv.sin6_port = port; servlen = sizeof(struct sockaddr_in6); s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); if (s < 0) { perror("socket"); exit(1); /*NOTREACHED*/ } if (bind(s, (struct sockaddr *)&serv, servlen) < 0) { perror("bind"); exit(1); /*NOTREACHED*/ } if (listen(s, 5) < 0) { perror("listen"); exit(1); /*NOTREACHED*/ } while (1) { fromlen = sizeof(from); ls = accept(s, (struct sockaddr *)&from, &fromlen); if (ls < 0) continue; if (from.sin6_family != AF_INET6 || fromlen != sizeof(struct sockaddr_in6)) { exit(1); /*NOTREACHED*/ } if (inet_ntop(AF_INET6, &from.sin6_addr, hbuf, sizeof(hbuf)) == NULL) { exit(1); /*NOTREACHED*/ } write(ls, "Hello ",6); write(ls, hbuf, strlen(hbuf)); write(ls, "\n", 1); close(ls); } /*NOTREACHED*/ }
まずサーバを起動する
[j05030@~]% ./myserver6 2000
次にIPv6でクライアントを起動する。
[j05030@~]% ./tcp6c 2001:02f8:001c:d048:0211:24ff:fe74:311a 2000 connected to '2001:2f8:1c:d048:211:24ff:fe74:311a' by IPv6 trying 2001:2f8:1c:d048:211:24ff:fe74:311a port 2000 Hello 2001:2f8:1c:d048:211:24ff:fe74:311a
次にIPv4でクライアントを起動する。
[j05030@~]%./tcp6c 133.13.59.30 2000 connected to '133.13.59.30' by IPv4 trying 133.13.59.30 port 2000 Hello ::ffff:133.13.59.30