用c/c++语言在IPv6下的socket通信编程

下面为Daytime这个服务的源代码例子,同时兼容IPV6和IPV4的地址,最后部分有更多说明。

单播模式下的Server方代码:

"listen_server.h":

#ifndef listen__server__h__

#define listen__server__h__

/*

listen_server

creates a server server socket listening at a hostname:service

using the family and socket type specified in the function

arguments.

*/

int

listen_server(const char *hostname,

const char *service,

int         family,

int         socktype);

#endif

"listen_server.cpp":

#include

#include

#include

#include

#include "listen_server.h"

const int LISTEN_QUEUE=128;

int

listen_server(const char *hostname,

const char *service,

int         family,

int         socktype)

{

struct addrinfo hints, *res, *ressave;

int n, sockfd;

memset(&hints, 0, sizeof(struct addrinfo));

/*

AI_PASSIVE flag: the resulting address is used to bind

to a socket for accepting incoming connections.

So, when the hostname==NULL, getaddrinfo function will

return one entry per allowed protocol family containing

the unspecified address for that family.

*/

hints.ai_flags    = AI_PASSIVE;

hints.ai_family   = family;

hints.ai_socktype = socktype;

n = getaddrinfo(hostname, service, &hints, &res);

if (n <0) {

fprintf(stderr,

"getaddrinfo error:: [%s]\n",

gai_strerror(n));

return -1;

}

ressave=res;

/*

Try open socket with each address getaddrinfo returned,

until getting a valid listening socket.

*/

sockfd=-1;

while (res) {

sockfd = socket(res->ai_family,

res->ai_socktype,

res->ai_protocol);

if (!(sockfd < 0)) {

if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0)

break;

close(sockfd);

sockfd=-1;

}

res = res->ai_next;

}

if (sockfd < 0) {

freeaddrinfo(ressave);

fprintf(stderr,

"socket error:: could not open socket\n");

return -1;

}

listen(sockfd, LISTEN_QUEUE);

freeaddrinfo(ressave);

return sockfd;

}

TCP模式 "tcp_daytime_server.cpp":

#include

#include

#include

#include

#include

#include

#include

#include "listen_server.h"

const char *DAYTIME_PORT="13";

int

main(int argc, char *argv[])

{

int listenfd, connfd;

socklen_t addrlen;

char timeStr[256];

struct sockaddr_storage clientaddr;

time_t now;

char clienthost[NI_MAXHOST];

char clientservice[NI_MAXSERV];

/* local server socket listening at daytime port=13 */

listenfd = listen_server( NULL, DAYTIME_PORT,

AF_UNSPEC, SOCK_STREAM);

if (listenfd < 0) {

fprintf(stderr,

"listen_socket error:: could not create listening "

"socket\n");

return -1;

}

for ( ; ;) {

addrlen = sizeof(clientaddr);

/* accept daytime client connections */

connfd = accept(listenfd,

(struct sockaddr *)&clientaddr,

&addrlen);

if (connfd < 0)

continue;

memset(clienthost, 0, sizeof(clienthost));

memset(clientservice, 0, sizeof(clientservice));

getnameinfo((struct sockaddr *)&clientaddr, addrlen,

clienthost, sizeof(clienthost),

clientservice, sizeof(clientservice),

NI_NUMERICHOST);

printf("Received request from host=[%s] port=[%s]\n",

clienthost, clientservice);

/* process daytime request from a client */

memset(timeStr, 0, sizeof(timeStr));

time(&now);

sprintf(timeStr, "%s", ctime(&now));

write(connfd, timeStr, strlen(timeStr));

close(connfd);

}

return 0;

}

UDP模式 "udp_daytime_server.cpp":

#include

#include

#include

#include

#include

#include

#include "listen_server.h"

const char *DAYTIME_PORT="13";

int

main(int argc, char *argv[])

{

int listenfd, n;

socklen_t addrlen;

char *myhost;

char timeStr[256];

struct sockaddr_storage clientaddr;

time_t now;

char b[256];

char clienthost[NI_MAXHOST];

char clientservice[NI_MAXSERV];

myhost=NULL;

if (argc > 1)

myhost=argv[1];

listenfd= listen_server(myhost, DAYTIME_PORT, AF_UNSPEC, SOCK_DGRAM);

if (listenfd < 0) {

fprintf(stderr,

"listen_server error:: could not create listening "

"socket\n");

return -1;

}

addrlen = sizeof(clientaddr);

for ( ; ;) {

n = recvfrom(listenfd,

b,

sizeof(b),

0,

(struct sockaddr *)&clientaddr,

&addrlen);

if (n < 0)

continue;

memset(clienthost, 0, sizeof(clienthost));

memset(clientservice, 0, sizeof(clientservice));

getnameinfo((struct sockaddr *)&clientaddr, addrlen,

clienthost, sizeof(clienthost),

clientservice, sizeof(clientservice),

NI_NUMERICHOST);

printf("Received request from host=[%s] port=[%s]\n",

clienthost, clientservice);

memset(timeStr, 0, sizeof(timeStr));

time(&now);

sprintf(timeStr, "%s", ctime(&now));

n = sendto(listenfd, timeStr, sizeof(timeStr), 0,

(struct sockaddr *)&clientaddr,

addrlen);

}

return 0;

}

单播模式下的Client方代码:

"connect_client.h":

#ifndef connect__client__h__

#define connect__client__h__

int

connect_client (const char *hostname,

const char *service,

int         family,

int         socktype);

#endif

"connect_client.cpp":

#include

#include

#include

#include

#include "connect_client.h"

int

connect_client (const char *hostname,

const char *service,

int         family,

int         socktype)

{

struct addrinfo hints, *res, *ressave;

int n, sockfd;

memset(&hints, 0, sizeof(struct addrinfo));

hints.ai_family = family;

hints.ai_socktype = socktype;

n = getaddrinfo(hostname, service, &hints, &res);

if (n <0) {

fprintf(stderr,

"getaddrinfo error:: [%s]\n",

gai_strerror(n));

return -1;

}

ressave = res;

sockfd=-1;

while (res) {

sockfd = socket(res->ai_family,

res->ai_socktype,

res->ai_protocol);

if (!(sockfd < 0)) {

if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)

break;

close(sockfd);

sockfd=-1;

}

res=res->ai_next;

}

freeaddrinfo(ressave);

return sockfd;

}

TCP模式 "tcp_daytime_client.cpp":

#include

#include

#include

#include

#include

#include

#include "connect_client.h"

const char *DAYTIME_PORT="13";

int

main(int argc, char *argv[])

{

int connfd;

char *myhost;

char timeStr[256];

myhost = "localhost";

if (argc > 1)

myhost = argv[1];

connfd= connect_client(myhost, DAYTIME_PORT, AF_UNSPEC, SOCK_STREAM);

if (connfd < 0) {

fprintf(stderr,

"client error:: could not create connected socket "

"socket\n");

return -1;

}

memset(timeStr, 0, sizeof(timeStr));

while (read(connfd, timeStr, sizeof(timeStr)) > 0)

printf("%s", timeStr);

close(connfd);

return 0;

}

UDP模式 "udp_daytime_client.cpp":

#include

#include

#include

#include

#include

#include

#include "connect_client.h"

const char *DAYTIME_PORT="13";

int

main(int argc, char *argv[])

{

int connfd, n, m;

char *myhost;

char timeStr[256];

char letter;

myhost = "localhost";

if (argc > 1)

myhost=argv[1];

connfd = connect_client(myhost, DAYTIME_PORT, AF_UNSPEC, SOCK_DGRAM);

if (connfd < 0) {

fprintf(stderr,

"client error:: could not create connected socket "

"socket\n");

return -1;

}

letter = '1';

m= write(connfd, &letter, sizeof(letter));

memset(timeStr, 0, sizeof(timeStr));

n = read(connfd,

timeStr,

sizeof(timeStr));

printf("%s\n", timeStr);

close(connfd);

return 0;

}

多播模式下的代码:

服务端和客户端共用 "mcastutil.cpp":

#include

#include

#include

#include

#include

#include

#include

#include

#include "mcastutil.h"

int

get_addr (const char *hostname,

const char *service,

int         family,

int         socktype,

struct sockaddr_storage *addr)

{

struct addrinfo hints, *res, *ressave;

int n, sockfd, retval;

retval = -1;

memset(&hints, 0, sizeof(struct addrinfo));

hints.ai_family = family;

hints.ai_socktype = socktype;

n = getaddrinfo(hostname, service, &hints, &res);

if (n <0) {

fprintf(stderr,

"getaddrinfo error:: [%s]\n",

gai_strerror(n));

return retval;

}

ressave = res;

sockfd=-1;

while (res) {

sockfd = socket(res->ai_family,

res->ai_socktype,

res->ai_protocol);

if (!(sockfd < 0)) {

if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0) {

close(sockfd);

memcpy(addr, res->ai_addr, sizeof(*addr);

retval=0;

break;

}

close(sockfd);

sockfd=-1;

}

res=res->ai_next;

}

freeaddrinfo(ressave);

return retval;

}

int

joinGroup(int sockfd, int loopBack, int mcastTTL,

struct sockaddr_storage *addr)

{

int r1, r2, r3, retval;

retval=-1;

switch (addr->ss_family) {

case AF_INET: {

struct ip_mreq      mreq;

mreq.imr_multiaddr.s_addr=

((struct sockaddr_in *)addr)->sin_addr.s_addr;

mreq.imr_interface.s_addr= INADDR_ANY;

r1= setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP,

&loopBack, sizeof(loopBack));

if (r1<0)

perror("joinGroup:: IP_MULTICAST_LOOP:: ");

r2= setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL,

&mcastTTL, sizeof(mcastTTL));

if (r2<0)

perror("joinGroup:: IP_MULTICAST_TTL:: ");

r3= setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,

(const void *)&mreq, sizeof(mreq));

if (r3<0)

perror("joinGroup:: IP_ADD_MEMBERSHIP:: ");

} break;

case AF_INET6: {

struct ipv6_mreq    mreq6;

memcpy(&mreq6.ipv6mr_multiaddr,

&(((struct sockaddr_in6 *)addr)->sin6_addr),

sizeof(struct in6_addr));

mreq6.ipv6mr_interface= 0; // cualquier interfaz

r1= setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,

&loopBack, sizeof(loopBack));

if (r1<0)

perror("joinGroup:: IPV6_MULTICAST_LOOP:: ");

r2= setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,

&mcastTTL, sizeof(mcastTTL));

if (r2<0)

perror("joinGroup:: IPV6_MULTICAST_HOPS::  ");

r3= setsockopt(sockfd, IPPROTO_IPV6,

IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));

if (r3<0)

perror("joinGroup:: IPV6_ADD_MEMBERSHIP:: ");

} break;

default:

r1=r2=r3=-1;

}

if ((r1>=0) && (r2>=0) && (r3>=0))

retval=0;

return retval;

}

int

isMulticast(struct sockaddr_storage *addr)

{

int retVal;

retVal=-1;

switch (addr->ss_family) {

case AF_INET: {

struct sockaddr_in *addr4=(struct sockaddr_in *)addr;

retVal = IN_MULTICAST(ntohl(addr4->sin_addr.s_addr));

} break;

case AF_INET6: {

struct sockaddr_in6 *addr6=(struct sockaddr_in6 *)addr;

retVal = IN6_IS_ADDR_MULTICAST(&addr6->sin6_addr);

} break;

default:

;

}

return retVal;

}

服务端 "mcastserver.cpp":

#include

#include

#include

#include

#include

#include

#include

#include

#include "mcastutil.h"

const char *DAYTIME_PORT="13";

int

main(int argc, char *argv[])

{

int sockfd, n;

char *mcastaddr;

char timeStr[256];

char b[256];

struct sockaddr_storage clientaddr, addr;

socklen_t addrlen;

time_t now;

char clienthost[NI_MAXHOST];

char clientservice[NI_MAXSERV];

mcastaddr = "FF01::1111";

if (argc ==2)

mcastaddr=argv[1];

memset(&addr, 0, sizeof(addr));

if (get_addr(mcastaddr, DAYTIME_PORT, PF_UNSPEC,

SOCK_DGRAM, &addr) <0)

{

fprintf(stderr, "get_addr error:: could not find multicast "

"address=[%s] port=[%s]\n", mcastaddr, DAYTIME_PORT);

return -1;

}

if (isMulticast(&addr)<0) {

fprintf(stderr,

"This address does not seem a multicast address [%s]\n",

mcastaddr);

return -1;

}

sockfd = socket(addr.ss_family, SOCK_DGRAM, 0);

if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {

perror("bind error:: ");

close(sockfd);

return -1;

}

if (joinGroup(sockfd, 0 , 8, &addr) <0) {

close(sockfd);

return -1;

}

addrlen=sizeof(clientaddr);

for ( ; ;) {

n = recvfrom(sockfd,

b,

sizeof(b),

0,

(struct sockaddr *)&clientaddr,

&addrlen);

if (n <0)

continue;

memset(clienthost, 0, sizeof(clienthost));

memset(clientservice, 0, sizeof(clientservice));

getnameinfo((struct sockaddr *)&clientaddr, addrlen,

clienthost, sizeof(clienthost),

clientservice, sizeof(clientservice),

NI_NUMERICHOST);

printf("Received request from host=[%s] port=[%s]\n",

clienthost, clientservice);

memset(timeStr, 0, sizeof(timeStr));

time(&now);

sprintf(timeStr, "%s", ctime(&now));

n = sendto(sockfd, timeStr, sizeof(timeStr), 0,

(struct sockaddr *)&addr,

sizeof(addr));

if (n<1)

perror("sendto error:: \n");

}

return 0;

}

客户端 "mcastclient.cpp":

#include

#include

#include

#include

#include

#include

#include

#include "mcastutil.h"

const char *DAYTIME_PORT="13";

int

main(int argc, char *argv[])

{

int sockfd, n;

char *myhost;

char timeStr[256];

char letter;

struct sockaddr_storage addr, clientaddr;

int addrlen;

socklen_t clientaddrlen;

myhost = "FF01::1111";

if (argc == 2)

myhost=argv[1];

addrlen=sizeof(addr);

memset(&addr, 0, addrlen);

get_addr(myhost, DAYTIME_PORT, PF_UNSPEC, SOCK_DGRAM, &addr);

sockfd = socket(addr.ss_family, SOCK_DGRAM, 0);

if (bind(sockfd, (struct sockaddr *)&addr, addrlen) <0) {

perror("bind error:: \n");

close(sockfd);

return -1;

}

if (joinGroup(sockfd, 0 , 8, &addr) <0) {

close(sockfd);

return -1;

}

letter = '1';

n = sendto(sockfd, &letter, sizeof(letter), 0,

(struct sockaddr *)&addr,

addrlen);

if (n<0) {

perror("sendto error:: ");

close(sockfd);

return -1;

}

memset(timeStr, 0, sizeof(timeStr));

clientaddrlen=sizeof(clientaddr);

n = recvfrom(sockfd,

timeStr,

sizeof(timeStr),

0,

(struct sockaddr *)&clientaddr,

&clientaddrlen);

if (n<0) {

perror("sendto error:: ");

close(sockfd);

return -1;

}

printf("%s\n", timeStr);

close(sockfd);

return 0;

}

不使用主机名或DNS解析时怎么操作

    上面例子中都是用的主机名或者dns解析来得到具体地址的,从hosts文件或者dns解析通过gethostbyname能得到具体的IPV6或IPV4地址,如下图示:


用c/c++语言在IPv6下的socket通信编程_第1张图片

内核支持双栈协议:

    如果要直接使用IPV6地址或IPV4地址,如果内核支持双栈协议(见下图),则只需要实现成ipv6地址操作即可,底层内核会自动进行ipv6和ipv4地址的转换

用c/c++语言在IPv6下的socket通信编程_第2张图片
用c/c++语言在IPv6下的socket通信编程_第3张图片
用c/c++语言在IPv6下的socket通信编程_第4张图片

    客户端代码IPv6代码(如果使用ipv4方式,则使用代码中的注释IPv4的代码,同时注释掉IPv6的代码;另一种方式还是用ipv6代码,但是服务器端的地址使用::ffff:a.b.c.d方式去进行连接):

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define MAXBUF 1024

int main(int argc, char **argv)

{

int sockfd, len;

/* struct sockaddr_in dest; */// IPv4

struct sockaddr_in6 dest;// IPv6

char buffer[MAXBUF + 1];

if (argc != 3) {

printf

("参数格式错误!正确用法如下:/n/t/t%s IP地址 端口/n/t比如:/t%s 127.0.0.1 80/n此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",

argv[0], argv[0]);

exit(0);

}

/* 创建一个 socket 用于 tcp 通信 */

/* if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { */ // IPv4

if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {      // IPv6

perror("Socket");

exit(errno);

}

printf("socket created/n");

/* 初始化服务器端(对方)的地址和端口信息 */

bzero(&dest, sizeof(dest));

/* dest.sin_family = AF_INET; */  // IPv4

dest.sin6_family = AF_INET6;     // IPv6

/* dest.sin_port = htons(atoi(argv[2])); */ // IPv4

dest.sin6_port = htons(atoi(argv[2]));     // IPv6

/* if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) { */ // IPv4  ,argv[1]: 211.43.56.5

if ( inet_pton(AF_INET6, argv[1], &dest.sin6_addr) < 0 ) {                 // IPv6  ,argv[1]: 2001:f653:98d3::1

perror(argv[1]);

exit(errno);

}

printf("address created/n");

/* 连接服务器 */

if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {

perror("Connect ");

exit(errno);

}

printf("server connected/n");

/* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */

bzero(buffer, MAXBUF + 1);

/* 接收服务器来的消息 */

len = recv(sockfd, buffer, MAXBUF, 0);

if (len > 0)

printf("接收消息成功:'%s',共%d个字节的数据/n",

buffer, len);

else

printf

("消息接收失败!错误代码是%d,错误信息是'%s'/n",

errno, strerror(errno));

bzero(buffer, MAXBUF + 1);

strcpy(buffer, "这是客户端发给服务器端的消息/n");

/* 发消息给服务器 */

len = send(sockfd, buffer, strlen(buffer), 0);

if (len < 0)

printf

("消息'%s'发送失败!错误代码是%d,错误信息是'%s'/n",

buffer, errno, strerror(errno));

else

printf("消息'%s'发送成功,共发送了%d个字节!/n",

buffer, len);

/* 关闭连接 */

close(sockfd);

return 0;

}

服务器端代码(同时支持IPv4和IPv6的客户端来连接):

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define MAXBUF 1024

int main(int argc, char **argv)

{

int sockfd, new_fd;

socklen_t len;

/* struct sockaddr_in my_addr, their_addr; */ // IPv4

struct sockaddr_in6 my_addr, their_addr; // IPv6

unsigned int myport, lisnum;

char buf[MAXBUF + 1];

if (argv[1])

myport = atoi(argv[1]);

else

myport = 7838;

if (argv[2])

lisnum = atoi(argv[2]);

else

lisnum = 2;

/* if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { */ // IPv4

if ((sockfd = socket(PF_INET6, SOCK_STREAM, 0)) == -1) { // IPv6

perror("socket");

exit(1);

} else

printf("socket created/n");

bzero(&my_addr, sizeof(my_addr));

/* my_addr.sin_family = PF_INET; */ // IPv4

my_addr.sin6_family = PF_INET6;    // IPv6

/* my_addr.sin_port = htons(myport); */ // IPv4

my_addr.sin6_port = htons(myport);   // IPv6

if (argv[3])

/* my_addr.sin_addr.s_addr = inet_addr(argv[3]); */ // IPv4

inet_pton(AF_INET6, argv[3], &my_addr.sin6_addr);  // IPv6

else

/* my_addr.sin_addr.s_addr = INADDR_ANY; */ // IPv4  ,use 0.0.0.0

my_addr.sin6_addr = in6addr_any;            // IPv6           ,use ::

/* if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) */ // IPv4

if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_in6))  // IPv6

== -1) {

perror("bind");

exit(1);

} else

printf("binded/n");

if (listen(sockfd, lisnum) == -1) {

perror("listen");

exit(1);

} else

printf("begin listen/n");

while (1) {

len = sizeof(struct sockaddr);

if ((new_fd =

accept(sockfd, (struct sockaddr *) &their_addr,

&len)) == -1) {

perror("accept");

exit(errno);

} else

printf("server: got connection from %s, port %d, socket %d/n",

/* inet_ntoa(their_addr.sin_addr), */ // IPv4

inet_ntop(AF_INET6, &their_addr.sin6_addr, buf, sizeof(buf)), // IPv6

/* ntohs(their_addr.sin_port), new_fd); */ // IPv4

their_addr.sin6_port, new_fd); // IPv6

/* 开始处理每个新连接上的数据收发 */

bzero(buf, MAXBUF + 1);

strcpy(buf,

"这是在连接建立成功后向客户端发送的第一个消息/n只能向new_fd这个用accept函数新建立的socket发消息,不能向sockfd这个监听socket发送消息,监听socket不能用来接收或发送消息/n");

/* 发消息给客户端 */

len = send(new_fd, buf, strlen(buf), 0);

if (len < 0) {

printf

("消息'%s'发送失败!错误代码是%d,错误信息是'%s'/n",

buf, errno, strerror(errno));

} else

printf("消息'%s'发送成功,共发送了%d个字节!/n",

buf, len);

bzero(buf, MAXBUF + 1);

/* 接收客户端的消息 */

len = recv(new_fd, buf, MAXBUF, 0);

if (len > 0)

printf("接收消息成功:'%s',共%d个字节的数据/n",

buf, len);

else

printf

("消息接收失败!错误代码是%d,错误信息是'%s'/n",

errno, strerror(errno));

/* 处理每个新连接上的数据收发结束 */

}

close(sockfd);

return 0;

}

内核只支持隔离栈(独立栈)协议:


用c/c++语言在IPv6下的socket通信编程_第5张图片

则无论是客户端还是服务器端,都必须把ipv4和ipv6分开处理,客户端不用说,使用哪类地址,就使用哪类代码(参考上面双栈协议实现中的注释来支持ipv4),服务器端通过建立两个套接口,然后select检测进来连接的客户端是ipv4还是ipv6来同时支持ipv4或ipv6的连接

SOCKET ServSock[FD_SETSIZE];

ADDRINFO AI0, AI1;

ServSock[0] = socket(AF_INET6, SOCK_STREAM, PF_INET6);

ServSock[1] = socket(AF_INET, SOCK_STREAM, PF_INET);

...

bind(ServSock[0], AI0->ai_addr, AI0->ai_addrlen);

bind(ServSock[1], AI1->ai_addr, AI1->ai_addrlen);

...

select(2, &SockSet, 0, 0, 0);

if (FD_ISSET(ServSocket[0], &SockSet)) {

// IPv6 connection csock = accept(ServSocket[0], (LPSOCKADDR)&From, FromLen);

...

}

if (FD_ISSET(ServSocket[1], &SockSet))

// IPv4 connection csock = accept(ServSocket[1], (LPSOCKADDR)&From, FromLen);

...

}

你可能感兴趣的:(用c/c++语言在IPv6下的socket通信编程)