注意:说明:
注意:本章节操作仅在源站需同时获取 IPv4 和 IPv6 客户端地址信息时参考,该操作将指引你如何修改源站业务代码。
源站在建立服务监听时,可参考采用如下两种方式:
一、示例代码
监听 IPv4 地址监听 IPv6 地址CJava#include <sys/socket.h>#include <stdio.h>#include <unistd.h>#include <netinet/in.h>#include <memory.h>#include <arpa/inet.h>int main(int argc, char **argv){ int l_sockfd; // 服务器地址采用v4结构 struct sockaddr_in serveraddr; // 业务修改点: 客户端地址必须采用v6结构 struct sockaddr_in6 clientAddr; int server_port = 10000; memset(&serveraddr, 0, sizeof(serveraddr)); // 创建socket l_sockfd = socket(AF_INET, SOCK_STREAM, 0); if (l_sockfd == -1){ printf("Failed to create socket.\\n"); return -1; } // 初始化服务器地址信息 memset(&serveraddr, 0, sizeof(struct sockaddr_in)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(server_port); serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); int isReuse = 1; setsockopt(l_sockfd, SOL_SOCKET,SO_REUSEADDR,(const char*)&isReuse,sizeof(isReuse)); // 关联socket和服务器地址信息 int nRet = bind(l_sockfd,(struct sockaddr*)&serveraddr, sizeof(serveraddr)); if(-1 == nRet) { printf("bind error\\n"); return -1; } // 监听socket listen(l_sockfd, 5); int clientAddrLen = sizeof(clientAddr); memset(&clientAddr, 0, sizeof(clientAddr)); // 接受来自客户端的连接 int linkFd = accept(l_sockfd, (struct sockaddr*)&clientAddr, &clientAddrLen); if(-1 == linkFd) { printf("accept error\\n"); return -1; } // 业务修改点: 根据客户端sin6_family的类型, 判断客户端是v4地址还是v6地址 // 当为AF_INET时, 表示客户端是IPv4, 将客户端地址指针转换为struct sockaddr_in*进行获取 // 当为AF_INET6时, 表示客户端是IPv6, 使用struct sockaddr_in6*进行获取 if (clientAddr.sin6_family == AF_INET) { printf("AF_INET accept getpeername %s : %d successful\\n", inet_ntoa(((struct sockaddr_in*)&clientAddr)->sin_addr), ntohs(((struct sockaddr_in*)&clientAddr)->sin_port)); }else if (clientAddr.sin6_family == AF_INET6){ char addr_p[128] = {0}; inet_ntop(AF_INET6, (void *)&((struct sockaddr_in6*)&clientAddr)->sin6_addr, addr_p, (socklen_t )sizeof(addr_p)); printf("AF_INET6 accept getpeername %s : %d successful\\n", addr_p, ntohs(((struct sockaddr_in6*)&clientAddr)->sin6_port)); }else{ printf("unknow sin_family:%d \\n", clientAddr.sin6_family); } close(l_sockfd); return 0;}import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.InetAddress;import java.net.InetSocketAddress;import java.net.ServerSocket;import java.net.Socket;import java.net.SocketAddress;public class ServerDemo { /** 若采用 IPv4 的地址结构搭建服务,使用 IPV4_HOST */ public static final String IPV4_HOST = "0.0.0.0"; /** 若采用 IPv6 的地址结构搭建服务,使用 IPV6_HOST */ public static final String IPV6_HOST = "::"; public static void main(String[] args) { int serverPort = 10000; try (ServerSocket serverSocket = new ServerSocket()) { // 设置地址复用 serverSocket.setReuseAddress(true); // 绑定服务器地址和端口,这里使用 IPv4 serverSocket.bind(new InetSocketAddress(InetAddress.getByName(IPV4_HOST), serverPort)); System.out.println("Server is listening on port " + serverPort); while (true) { // 接受客户端连接 Socket clientSocket = serverSocket.accept(); System.out.println("New client connected: " + clientSocket.getRemoteSocketAddress()); // 处理客户端请求 handleClientRequest(clientSocket); } } catch (IOException e) { System.err.println("Failed to create server socket: " + e.getMessage()); } } /** * 处理函数,具体业务具体实现,这里只做为示例 * 此函数的作用是将 client 的输入原封不动的返回给 client */ private static void handleClientRequest(Socket clientSocket) { try (InputStream inputStream = clientSocket.getInputStream(); OutputStream outputStream = clientSocket.getOutputStream()) { // 读取客户端发来的数据 byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { // 将接收到的数据原样回复给客户端 outputStream.write(buffer, 0, bytesRead); } } catch (IOException e) { // 当客户端断开连接后 System.err.println("Failed to handle client request: " + e.getMessage()); } finally { try { clientSocket.close(); } catch (IOException e) { System.err.println("Failed to close client socket: " + e.getMessage()); } } }}CJava#include <sys/socket.h>#include <stdio.h>#include <unistd.h>#include <netinet/in.h>#include <memory.h>#include <arpa/inet.h>int main(int argc, char **argv){ int l_sockfd; // 服务器地址采用v6结构 struct sockaddr_in6 serveraddr; // 客户端地址采用v6结构 struct sockaddr_in6 clientAddr; int server_port = 10000; memset(&serveraddr, 0, sizeof(serveraddr)); // 创建socket l_sockfd = socket(AF_INET6, SOCK_STREAM, 0); if (l_sockfd == -1){ printf("Failed to create socket.\\n"); return -1; } // 设置服务器地址信息 memset(&serveraddr, 0, sizeof(struct sockaddr_in6)); serveraddr.sin6_family = AF_INET6; serveraddr.sin6_port = htons(server_port); serveraddr.sin6_addr = in6addr_any; int isReuse = 1; setsockopt(l_sockfd, SOL_SOCKET,SO_REUSEADDR,(const char*)&isReuse,sizeof(isReuse)); // 关联socket和服务器地址信息 int nRet = bind(l_sockfd,(struct sockaddr*)&serveraddr, sizeof(serveraddr)); if(-1 == nRet) { printf("bind error\\n"); return -1; } // 监听socket listen(l_sockfd, 5); int clientAddrLen = sizeof(clientAddr); memset(&clientAddr, 0, sizeof(clientAddr)); // 接受来自客户端的连接请求 int linkFd = accept(l_sockfd, (struct sockaddr*)&clientAddr, &clientAddrLen); if(-1 == linkFd) { printf("accept error\\n"); return -1; } // 这里收到的客户端地址信息全部都采用v6的结构进行存储 // 其中,客户端的IPv4地址也被映射成了一个IPv6的地址,例如:::ffff:119.29.1.1 char addr_p[128] = {0}; inet_ntop(AF_INET6, (void *)&clientAddr.sin6_addr, addr_p, (socklen_t )sizeof(addr_p)); printf("accept %s : %d successful\\n", addr_p, ntohs(clientAddr.sin6_port)); // 业务修改点:通过系统宏定义IN6_IS_ADDR_V4MAPPED来判断一个IPv6地址是否是IPv4的映射地址(代表客户端是IPv4) if(IN6_IS_ADDR_V4MAPPED(&clientAddr.sin6_addr)) { struct sockaddr_in real_v4_sin; memset (&real_v4_sin, 0, sizeof (struct sockaddr_in)); real_v4_sin.sin_family = AF_INET; real_v4_sin.sin_port = clientAddr.sin6_port; // 读取最后四个字节即为客户端真实IPv4地址 memcpy (&real_v4_sin.sin_addr, ((char *)&clientAddr.sin6_addr) + 12, 4); printf("connect %s successful\\n", inet_ntoa(real_v4_sin.sin_addr)); } close(l_sockfd); return 0;}import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.InetAddress;import java.net.InetSocketAddress;import java.net.ServerSocket;import java.net.Socket;import java.net.SocketAddress;public class ServerDemo { /** 若采用 IPv4 的地址结构搭建服务,使用 IPV4_HOST */ public static final String IPV4_HOST = "0.0.0.0"; /** 若采用 IPv6 的地址结构搭建服务,使用 IPV6_HOST */ public static final String IPV6_HOST = "::"; public static void main(String[] args) { int serverPort = 10000; try (ServerSocket serverSocket = new ServerSocket()) { // 设置地址复用 serverSocket.setReuseAddress(true); // 绑定服务器地址和端口,这里使用 IPv4 serverSocket.bind(new InetSocketAddress(InetAddress.getByName(IPV6_HOST), serverPort)); System.out.println("Server is listening on port " + serverPort); while (true) { // 接受客户端连接 Socket clientSocket = serverSocket.accept(); System.out.println("New client connected: " + clientSocket.getRemoteSocketAddress()); // 处理客户端请求 handleClientRequest(clientSocket); } } catch (IOException e) { System.err.println("Failed to create server socket: " + e.getMessage()); } } /** * 处理函数,具体业务具体实现,这里只做为示例 * 此函数的作用是将 client 的输入原封不动的返回给 client */ private static void handleClientRequest(Socket clientSocket) { try (InputStream inputStream = clientSocket.getInputStream(); OutputStream outputStream = clientSocket.getOutputStream()) { // 读取客户端发来的数据 byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { // 将接收到的数据原样回复给客户端 outputStream.write(buffer, 0, bytesRead); } } catch (IOException e) { // 当客户端断开连接后 System.err.println("Failed to handle client request: " + e.getMessage()); } finally { try { clientSocket.close(); } catch (IOException e) { System.err.println("Failed to close client socket: " + e.getMessage()); } } }}
二、控制台输出结果
Server is listening on port 10000New client connected: /127.0.0.1:50680New client connected: /0:0:0:0:0:0:0:1:51124New client connected: /127.0.0.1:51136