该网站(点击打开链接)提供了一些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、监听套接字
4、接收请求
接下来完成网络IO操作即可。
以下为代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#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;
}