该网站(点击打开链接)提供了一些Linux下的IPv6的tcp/udp socket编程范例,然而经过测试发现这些实例都只能用于同一台机器间的客户端、服务器通信,当在两台机器间使用link-local地址进行通信时,可ping通,然而客户端通过connectI()无法连接上服务器,被该问题困扰许久。之后在StackOverflow找到了答案。点击打开链接

现在将tcp 的IPv6服务器、客户端编程说明如下:

一、服务器编程遵循以下流程:

1、创建套接字

int socket(int domain, int type, int protocol)

该函数用于创建一个socket描述符,可唯一标识一个套接字,参数domain代表协议域,这里使用PF_INET6, type可设置是使用tcp还是udp,这里使用SOCK_STREAM,参数protocol这里不使用

2、绑定套接字

将服务端的IP地址、端口号与socket套接字进行绑定,用以给客户端提供服务,可用int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen)进行绑定

3、监听套接字

可调用该函数int listen(int sockfd, int backlog)进行监听,当客户端发出connect()函数时,服务器会调用accept()接收该请求。参数sockfd为套接字标识符,backlog为可接收的最大连接数。

4、接收请求

当客户端发起connect()请求时,服务器监听到该请求,会调用accept()函数接收请求。函数原型为int accept4(int sockfd, struct sockaddr *addr,socklen_t *addrlen, int flags); sockfd为套接字描述符,addr 为客户端的IP地址与端口号。

接下来完成网络IO操作即可。

以下为代码

#include

#include

#include

#include

#include

#include

#include

#include

#define PORT 12345

#define MESSAGE "hello"

int main(void)

{

int sock, conn;

socklen_t clilen;

struct sockaddr_in6 server_addr, client_addr;

char addrbuf[INET6_ADDRSTRLEN];

/* create a STREAM (TCP) socket in the INET6 (IPv6) protocol */

sock = socket(PF_INET6, SOCK_STREAM, 0);

if (sock < 0) {

perror("creating socket");

exit(1);

}

#ifdef V6ONLY

// setting this means the socket only accepts connections from v6;

// unset, it accepts v6 and v4 (mapped address) connections

{ int opt = 1;

if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) < 0) {

perror("setting option IPV6_V6ONLY");

exit(1);

}

}

#endif

/* create server address: this will say where we will be willing to

accept connections from */

/* clear it out */

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

/* it is an INET6 address */

server_addr.sin6_family = AF_INET6;

/* the client IP address, in network byte order */

/* in this example we accept connections from ANYwhere */

server_addr.sin6_addr = in6addr_any;

/* the port we are going to listen on, in network byte order */

server_addr.sin6_port = htons(PORT);

/* associate the socket with the address and port */

if (bind(sock, (struct sockaddr *)&server_addr,

sizeof(server_addr)) < 0) {

perror("bind failed");

exit(2);

}

/* start the socket listening for new connections */

if (listen(sock, 5) < 0) {

perror("listen failed");

exit(3);

}

while (1) {

/* now wait until we get a connection */

printf("waiting for a connection...\n");

clilen = sizeof(client_addr);

conn = accept(sock, (struct sockaddr *)&client_addr, &clilen);

if (conn < 0) {

perror("accept failed");

exit(4);

}

/* now client_addr contains the address of the client */

printf("connection from %s\n",

inet_ntop(AF_INET6, &client_addr.sin6_addr, addrbuf,

INET6_ADDRSTRLEN));

printf("sending message\n");

write(conn, MESSAGE, sizeof(MESSAGE));

/* close connection */

close(conn);

}

return 0;

}

二、客户端编程遵循以下流程

1、创建套接字

这个跟服务器端socketI()函数一样

2、连接服务器

客户端可用connect()函数连接服务器,服务器在监听到该连接请求时,可接受该请求。

由于IPv6中引入了 scope id域,这里需用到getaddrinfo()函数,该函数可实现IP地址、端口到addrinfo结构体的转换,具体说明日后补充。吃饭中。。。

看代码

#include

#include

#include

#include

#include

#include

#define PORT "12345"

#define SERVADDR "fe80::223:8bff:fe59:de90%wlan0"

int main(void)

{

struct addrinfo hints = {0};

struct addrinfo *res;

int get_err;

int sockfd;

char buffer[1024];

hints.ai_family = AF_INET6;

hints.ai_socktype = SOCK_STREAM;

get_err = getaddrinfo(SERVADDR, PORT, &hints, &res);

if(get_err)

{

fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(get_err));

return 1;

}

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

if(sockfd < 0)

{

perror("socket");

return 1;

}

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

{

perror("connect");

return 1;

}

printf("reading message\n");

read(sockfd, buffer, 1024);

printf("got '%s'\n", buffer);

close(sockfd);

return 0;

}

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐