服务端通过noa接口获取源ip示例程序
TCP-Java
tcp相关说明
只有裸金属后端,或者IPV6 LB才需要适配,如果是裸金属后端请先根据指引安装noa模块
以下示例,在jdk8,jdk11可以确保正常获取,其他版本的jdk如有问题请联系【网易人员】
客户端IP为IPv4场景
该示例仅支持获取客户端为IPv4的场景
Java代码
import java.io.*;
import java.net.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8000); // 使用8000端口
while (true) {
final Socket socket = serverSocket.accept();
// 创建一个新线程来处理连接
new Thread(new Runnable() {
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
System.out.println("New connection from " + socket.getInetAddress().getHostAddress());
System.out.println("local address: " + socket.getLocalAddress().getHostAddress());
String line;
while ((line = reader.readLine()) != null) {
System.out.println("Received: " + line);
writer.println("Echo: " + line); // 发送回显
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
}
客户端IP为IPv4或IPv6场景
该示例同时支持客户端为IPV4 或者 IPV6的场景
准备JIN-TCP
定义JNI返回模型-TCP
public class ToaSockopt {
private int[] addr; // 对应 ip 或 ip6
private int port; // 对应 port
private byte family; // 对应 family
private byte toaSet; // 对应 toa_set
// getter and setter methods...
public int[] getAddr() {
return addr;
}
public void setAddr(int[] addr) {
this.addr = addr;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public byte getFamily() {
return family;
}
public void setFamily(byte family) {
this.family = family;
}
public byte getToaSet() {
return toaSet;
}
public void setToaSet(byte toaSet) {
this.toaSet = toaSet;
}
}
定义JNI-TCP
public class Toa {
static {
System.loadLibrary("Toa"); //加载动态链接库
}
// 本地方法声明
private native ToaSockopt getsockoptNative(int sockfd);
public ToaSockopt getsockopt(int sockfd) {
return getsockoptNative(sockfd);
}
}
编译Toa.java
Toa.java
javah -jni Toa
构建JIN头文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Toa */
#ifndef _Included_Toa
#define _Included_Toa
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Toa
* Method: getsockoptNative
* Signature: (I)LToaSockopt;
*/
JNIEXPORT jobject JNICALL Java_Toa_getsockoptNative
(JNIEnv *, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
编写JNI实现-TCP
#include <jni.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/types.h>
#ifdef __KERNEL__
#include <linux/in.h>
#include <linux/in6.h>
#else
#include <arpa/inet.h>
#endif
#define TOA_CTL (2049) /* tcp sockopt option */
#define UOA_CTL (2048) /* udp sockopt option */
struct toa_sockopt {
union {
__be32 ip[1];
__be32 ip6[4];
} addr; /* 真实客户端 ip */
__be16 port; /* 真实客户端端口 */
__u8 family; /* 真实客户端 ip 类型:AF_INET / AF_INET6 */
__u8 toa_set; /* 1 表示设置了 toa */
};
struct toa_sockopt * toa_getsockopt(int sockfd) {
int ret = 0;
socklen_t socktoa_len;
struct toa_sockopt *socktoa;
socktoa_len = sizeof(struct toa_sockopt);
socktoa = (struct toa_sockopt *) malloc(socktoa_len);
if (socktoa == NULL) {
return NULL; // malloc failed
}
memset(socktoa, 0, sizeof(struct toa_sockopt));
ret = getsockopt(sockfd, IPPROTO_IP, TOA_CTL, socktoa, &socktoa_len);
if (ret == 0) {
return socktoa;
}
free(socktoa); // free memory if getsockopt failed
return NULL;
}
JNIEXPORT jobject JNICALL Java_Toa_getsockoptNative(JNIEnv *env, jobject obj, jint sockfd) {
int i;
struct toa_sockopt * result = toa_getsockopt(sockfd);
if (result == NULL) {
return NULL;
}
// 找到Java类ToaSockopt的类引用
jclass cls = (*env)->FindClass(env, "ToaSockopt");
if(cls == NULL) {
free(result);
return NULL; // 类未找到
}
// 获取ToaSockopt类的构造函数
jmethodID constructor = (*env)->GetMethodID(env, cls, "<init>", "()V");
if(constructor == NULL) {
free(result);
return NULL; // 构造函数未找到
}
// 创建一个新的ToaSockopt对象
jobject objToaSockopt = (*env)->NewObject(env, cls, constructor);
// 获取ToaSockopt类的各个字段
jfieldID addrField = (*env)->GetFieldID(env, cls, "addr", "[I");
jfieldID portField = (*env)->GetFieldID(env, cls, "port", "I");
jfieldID familyField = (*env)->GetFieldID(env, cls, "family", "B");
jfieldID toaSetField = (*env)->GetFieldID(env, cls, "toaSet", "B");
jintArray addrArray = (*env)->NewIntArray(env, 4);
if(addrArray == NULL) {
// 无法创建数组
free(result);
return NULL;
}
// 给新创建的ToaSockopt对象的各个字段赋值
jint addr[4];
for (i = 0; i < 4; i++) {
addr[i] = (jint)ntohl(result->addr.ip6[i]);
}
(*env)->SetIntArrayRegion(env, addrArray, 0, 4, addr);
(*env)->SetObjectField(env, objToaSockopt, addrField, addrArray);
(*env)->SetIntField(env, objToaSockopt, portField, (jint)ntohs(result->port));
(*env)->SetByteField(env, objToaSockopt, familyField, (jbyte)result->family);
(*env)->SetByteField(env, objToaSockopt, toaSetField, (jbyte)result->toa_set);
free(result);
return objToaSockopt;
}
编译JNI实现-TCP
gcc -shared -o libToa.so Toa.c -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -fPIC
使用JNI-TCP
使用以下类掉调用JNI获取客户端真实IP
import sun.nio.ch.SocketAdaptor;
import java.io.FileDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.channels.SocketChannel;
public class RealIp {
public static String getRealIp(Socket socket) {
try {
int fd = getFd(socket);
System.out.println("channel File Descriptor: " + fd);
Toa toa = new Toa();
ToaSockopt result = toa.getsockopt(fd);
if (result != null) {
int length = 0;
int[] addr = result.getAddr();
if (result.getFamily() == 2) {
length = 4;
} else if (result.getFamily() == 10) {
length = 16;
}
byte[] byteArray = new byte[length];
for (int i = 0; i < length / 4; i++) {
byteArray[i * 4] = (byte) (addr[i] >> 24);
byteArray[i * 4 + 1] = (byte) (addr[i] >> 16);
byteArray[i * 4 + 2] = (byte) (addr[i] >> 8);
byteArray[i * 4 + 3] = (byte) (addr[i]);
}
InetAddress inetAddr = InetAddress.getByAddress(byteArray);
return inetAddr.getHostAddress();
} else {
return socket.getInetAddress().getHostAddress();
}
} catch (Exception e) {
return socket.getInetAddress().getHostAddress();
}
}
private static int getFd(Socket socket) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
int cfd = -1;
int sfd = -1;
if (socket instanceof SocketAdaptor) {
SocketChannel socketChannel = socket.getChannel();
// 获取 SocketChannel 的实现类名称
System.out.println("SocketChannel class: " + socketChannel.getClass().getName());
// 假设我们知道 SocketChannelImpl 是具体实现类
Class<?> socketChannelImplClass = Class.forName("sun.nio.ch.SocketChannelImpl");
if (socketChannelImplClass.isInstance(socketChannel)) {
// 使用反射获取 SocketChannelImpl 中的 fd 字段
Field fdField = socketChannelImplClass.getDeclaredField("fd");
fdField.setAccessible(true);
// 从 fd 字段获取 FileDescriptor 对象
FileDescriptor fds = (FileDescriptor) fdField.get(socketChannel);
// 通过反射获取 FileDescriptor 中的 fd 整数字段
Field fdIntField = FileDescriptor.class.getDeclaredField("fd");
fdIntField.setAccessible(true);
cfd = fdIntField.getInt(fds);
} else {
System.out.println("SocketChannel is not an instance of SocketChannelImpl.");
}
return cfd;
} else {
Method getImplMethod = Socket.class.getDeclaredMethod("getImpl");
getImplMethod.setAccessible(true);
Object socketImpl = getImplMethod.invoke(socket);
Method getFileDescriptorMethod = java.net.SocketImpl.class.getDeclaredMethod("getFileDescriptor");
getFileDescriptorMethod.setAccessible(true);
Object fileDescriptor = getFileDescriptorMethod.invoke(socketImpl);
Field fdField = fileDescriptor.getClass().getDeclaredField("fd");
fdField.setAccessible(true);
sfd = fdField.getInt(fileDescriptor);
return sfd;
}
}
}
测试-TCP
NIO场景测试-TCP
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class TestToaServer {
public static void main(String[] args) {
try {
// Create a selector to handle channels
Selector selector = Selector.open();
// Open a ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false); // Configure it to be non-blocking
serverSocketChannel.bind(new InetSocketAddress(9999)); // Bind to port 9999
// Register the server socket channel to the selector for accepting connections
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Server started on port 9999...");
while (true) {
// Wait for events
selector.select();
// Get the keys for which events are available
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
// Accept a new client connection
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = serverChannel.accept();
clientChannel.configureBlocking(false);
// Register the client channel for reading
clientChannel.register(selector, SelectionKey.OP_READ);
System.out.println(RealIp.getRealIp( clientChannel.socket()));
System.out.println("Accepted new connection from: " + clientChannel.getRemoteAddress());
} else if (key.isReadable()) {
// Read data from the client
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(256);
int bytesRead = clientChannel.read(buffer);
if (bytesRead == -1) {
// Connection was closed by the client
clientChannel.close();
System.out.println("Closed connection from: " + clientChannel.getRemoteAddress());
} else {
// Process data
buffer.flip();
String receivedData = new String(buffer.array(), 0, bytesRead);
System.out.println("Received: " + receivedData);
// Echo the received data back to the client
clientChannel.write(ByteBuffer.wrap(("Echo: " + receivedData).getBytes()));
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
BIO场景测试-TCP
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class TestToaServerB {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(9999); // 使用9999端口
System.out.println("Server started on port 9999...");
while (true) {
final Socket socket = serverSocket.accept();
// 创建一个新线程来处理连接
new Thread(new Runnable() {
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
System.out.println("New connection from " + socket.getInetAddress().getHostAddress());
System.out.println("local address: " + socket.getLocalAddress().getHostAddress());
System.out.println(RealIp.getRealIp(socket));
String line;
while ((line = reader.readLine()) != null) {
System.out.println("Received: " + line);
writer.println("Echo: " + line); // 发送回显
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
}
UDP-Java
udp相关说明
udp 只能通过调用 getsockopt 来获取真实源 ip,并且需要提供初始的源目信息。
只有裸金属后端,或者IPV6 LB才需要适配,如果是裸金属后端请先根据指引安装noa模块
注意事项
Java服务端启动监听时,需要指明服务端的真实IP,这个IP和LB的RS IP应该是一致的,不能直接使用0.0.0.0
示例
示例仅作为在UDP场景下获取客户端源IP的演示,实际生产请结合应用需求适配
实例环境:JDK11
准备JNI-UDP
定义JNI返回模型-UDP
UoaUtils.java
public class UoaUtils {
public static class UoaResult {
private String realSourceIp;
private int realSourcePort;
public UoaResult() {
// 默认构造函数,JNI 代码中使用 NewObject 调用
}
public String getRealSourceIp() {
return realSourceIp;
}
public void setRealSourceIp(String realSourceIp) {
this.realSourceIp = realSourceIp;
}
public int getRealSourcePort() {
return realSourcePort;
}
public void setRealSourcePort(int realSourcePort) {
this.realSourcePort = realSourcePort;
}
public String toString() {
return "realSourceIp:" + realSourceIp + ", realSourcePort:" + realSourcePort;
}
}
}
定义JNI-UDP
Uoa.java
import java.net.DatagramSocket;
public class Uoa {
static {
System.loadLibrary("Uoa"); //加载动态链接库
}
/**
* 本地方法声明,通过指定的四元组获取当前报文的真实客户端Ip和Port
* @param fd socket fd
* @param clientIp 从报文获取的初始源Ip
* @param clientPort 从报文获取的初始源Port
* @param serverIp 从报文获取的初始目的Ip
* @param serverPort 从报文获取的初始目的Port
* @return
*/
public native UoaUtils.UoaResult getsockoptNative(int fd, String clientIp, int clientPort, String serverIp, int serverPort);
}
编译Uoa.java
Uoa.java
javac Uoa.java
使用指令构建JNI头文件
// 在不同的jdk版本,该指令有所不同,请根据jdk版本选择
javac -h . Uoa.java
上述指令执行后,会生成名为Uoa.h的头文件,类似如下内容:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Uoa */
#ifndef _Included_Uoa
#define _Included_Uoa
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Uoa
* Method: getsockoptNative
* Signature: (ILjava/lang/String;ILjava/lang/String;I)LUoaUtils/UoaResult;
*/
JNIEXPORT jobject JNICALL Java_Uoa_getsockoptNative
(JNIEnv *, jobject, jint, jstring, jint, jstring, jint);
#ifdef __cplusplus
}
#endif
#endif
编写JNI实现-UDP
Uoa.c
#include <jni.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/types.h>
#ifdef __KERNEL__
#include <linux/in.h>
#include <linux/in6.h>
#else
#include <arpa/inet.h>
#endif
#define TOA_CTL (2049) /* tcp sockopt option */
#define UOA_CTL (2048) /* udp sockopt option */
union inet_addr {
struct in_addr in;
struct in6_addr in6;
};
/*
* uoa_param 结构是通过 getsockopt 接口获取 udp 连接真实源 ip 的入参 + 返回值,
* 使用方式:getsockopt(sockfd, IPPROTO_IP, UOA_CTL, ¶m, &plen)
* sockfd:连接的文件句柄,
* IPPROTO_IP:协议,固定
* UOA_CTL:option,固定
* ¶m:struct uoa_param 类型的指针,用来传递入参及保存 getsockopt 的返回结果,输入是五元组,输出可以初始化为 0
* &plen:param 变量的长度,通常设置为 sizeof(struct uoa_param)
*/
struct uoa_param {
/* input */
union inet_addr saddr;
union inet_addr daddr;
__be16 sport;
__be16 dport;
__u8 af;
/* output */
union inet_addr real_saddr;
__be16 real_sport;
__u8 real_af;
} __attribute__((__packed__));
struct uoa_param * uoa_getsockopt(int sockfd, struct sockaddr_in *clientaddr, struct sockaddr_in *serveraddr) {
int ret = 0;
struct uoa_param *uparam = (struct uoa_param *)malloc(sizeof(struct uoa_param));
if (uparam == NULL) {
perror("malloc failed");
return NULL; // malloc failed
}
memset(uparam, 0, sizeof(struct uoa_param));
// Assuming clientaddr and serveraddr are valid pointers passed to the function
memcpy(&uparam->saddr.in, &clientaddr->sin_addr, sizeof(struct in_addr));
memcpy(&uparam->daddr.in, &serveraddr->sin_addr, sizeof(struct in_addr));
uparam->sport = clientaddr->sin_port;
uparam->dport = serveraddr->sin_port;
uparam->af = AF_INET;
socklen_t up_len = sizeof(*uparam);
ret = getsockopt(sockfd, IPPROTO_IP, UOA_CTL, uparam, &up_len);
if (ret == 0) {
return uparam;
}
free(uparam); // free memory if getsockopt failed
perror("get socketopt failed");
return NULL;
}
JNIEXPORT jobject JNICALL Java_Uoa_getsockoptNative(JNIEnv *env, jobject obj, jint sockfd, jstring jClientIp, jint clientPort, jstring jServerIp, jint serverPort) {
// Convert Java strings to C strings
const char *clientIp = (*env)->GetStringUTFChars(env, jClientIp, 0);
const char *serverIp = (*env)->GetStringUTFChars(env, jServerIp, 0);
// Prepare socket address structures
struct sockaddr_in clientaddr;
struct sockaddr_in serveraddr;
memset(&clientaddr, 0, sizeof(clientaddr));
memset(&serveraddr, 0, sizeof(serveraddr));
clientaddr.sin_family = AF_INET;
serveraddr.sin_family = AF_INET;
clientaddr.sin_port = htons(clientPort);
serveraddr.sin_port = htons(serverPort);
inet_pton(AF_INET, clientIp, &clientaddr.sin_addr);
inet_pton(AF_INET, serverIp, &serveraddr.sin_addr);
struct uoa_param *result = uoa_getsockopt(sockfd, &clientaddr, &serveraddr);
// Release the Java strings
(*env)->ReleaseStringUTFChars(env, jClientIp, clientIp);
(*env)->ReleaseStringUTFChars(env, jServerIp, serverIp);
if (result == NULL) {
perror("result failed");
return NULL;
}
// Create a new UoaResult Java object
jclass resultClass = (*env)->FindClass(env, "UoaUtils$UoaResult");
jmethodID constructor = (*env)->GetMethodID(env, resultClass, "<init>", "()V");
jobject uoaResult = (*env)->NewObject(env, resultClass, constructor);
// Prepare real source IP string
char realSourceIp[INET6_ADDRSTRLEN];
printf("Real Address Family: %u\n", result->real_af);
if (result->real_af == AF_INET) {
// IPv4
inet_ntop(AF_INET, &result->real_saddr.in, realSourceIp, INET_ADDRSTRLEN);
} else if (result->real_af == AF_INET6) {
// IPv6
inet_ntop(AF_INET6, &result->real_saddr.in6, realSourceIp, INET6_ADDRSTRLEN);
}
// Set the fields in the UoaResult object
jmethodID setRealSourceIp = (*env)->GetMethodID(env, resultClass, "setRealSourceIp", "(Ljava/lang/String;)V");
jmethodID setRealSourcePort = (*env)->GetMethodID(env, resultClass, "setRealSourcePort", "(I)V");
jstring realSourceIpStr = (*env)->NewStringUTF(env, realSourceIp);
(*env)->CallVoidMethod(env, uoaResult, setRealSourceIp, realSourceIpStr);
(*env)->CallVoidMethod(env, uoaResult, setRealSourcePort, ntohs(result->real_sport));
free(result);
return uoaResult;
}
编译Uoa.c
编译Uoa.c
以下指令仅支持在linux系统上编译,请核对jdk路径是否正确
gcc -shared -o libUoa.so Uoa.c -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -fPIC
使用JNI-UDP
UdpRealIp.java
import java.io.FileDescriptor;
import java.lang.reflect.Field;
import java.net.DatagramSocket;
import java.net.DatagramSocketImpl;
import java.net.InetAddress;
import java.nio.channels.DatagramChannel;
public class UdpRealIp {
/*
* BIO 模式下获取真实客户端IP
*/
public static String getRealIp(DatagramSocket socket, String clientAddress, int clientPort) {
try {
// 获取服务器端信息
InetAddress serverAddress = socket.getLocalAddress();
int serverPort = socket.getLocalPort();
// 获取 DatagramSocket 的私有 'impl' 字段,该字段是 DatagramSocketImpl 的一个实例
Field implField = DatagramSocket.class.getDeclaredField("impl");
implField.setAccessible(true);
DatagramSocketImpl impl = (DatagramSocketImpl)implField.get(socket);
// 打印实现类以确认实际使用的类
System.out.println("Actual implementation class: " + impl.getClass());
// 尝试从父类获取 'fd' 字段
Field fdField = null;
Class<?> clazz = impl.getClass();
while (clazz != null) {
try {
fdField = clazz.getDeclaredField("fd");
fdField.setAccessible(true);
break;
} catch (NoSuchFieldException e) {
// 如果当前类没有该字段,则继续在父类中查找
clazz = clazz.getSuperclass();
}
}
if (fdField != null) {
FileDescriptor fdes = (FileDescriptor) fdField.get(impl);
System.out.println("FileDescriptor: " + fdes);
// 获取 FileDescriptor 的私有 'fd' 字段的值
Field fdValueField = FileDescriptor.class.getDeclaredField("fd");
fdValueField.setAccessible(true);
int fd = fdValueField.getInt(fdes);
Uoa uoa = new Uoa();
UoaUtils.UoaResult result = uoa.getsockoptNative(fd,clientAddress,clientPort,serverAddress.getHostAddress(),serverPort);
System.out.println("Received packet from IP: " + result);
if (result != null) {
return result.getRealSourceIp();
}
return null;
} else {
throw new RuntimeException("Could not find 'fd' field in class hierarchy");
}
} catch (Exception e) {
throw new RuntimeException("get RealIp failure", e);
}
}
/**
*
* NIO 场景下获取真实客户端IP
*/
public static String getRealIp(DatagramChannel channel, String clientAddress, int clientPort) {
try {
// 获取服务器端信息
DatagramSocket socket = channel.socket();
InetAddress serverAddress = socket.getLocalAddress();
int serverPort = socket.getLocalPort();
Field fdField = channel.getClass().getDeclaredField("fd");
fdField.setAccessible(true);
FileDescriptor fdes = (FileDescriptor) fdField.get(channel);
// 通过反射获取 FileDescriptor 的私有 'fd' 字段
Field fdValueField = FileDescriptor.class.getDeclaredField("fd");
fdValueField.setAccessible(true);
int fd = fdValueField.getInt(fdes);
Uoa uoa = new Uoa();
UoaUtils.UoaResult result = uoa.getsockoptNative(fd,clientAddress,clientPort,serverAddress.getHostAddress(),serverPort);
System.out.println("Received packet from IP: " + result);
if (result != null) {
return result.getRealSourceIp();
}
return null;
} catch (Exception e) {
throw new RuntimeException("get RealIp failure", e);
}
}
}
测试-UDP
测试前,请先确认裸金属后端已经安装noa,并在实例的监听器上开启了NOA功能,检查方式如下:
(1) 是否安装noa,输出以下内容表示已安装
lsmod | grep noa
noa 32768 0
(2) 是否开启NOA功能,1表示已开启
cat /proc/net/noa/toa_on
1
cat /proc/net/noa/uoa_on
1
如果noa模块相关检查异常,可联系【网易人员】
NIO场景测试-UDP
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.charset.StandardCharsets;
public class TestUoaServer {
public static void main(String[] args) {
try {
InetAddress localAddress = InetAddress.getByName("Server Ip");
int port = 9876;
InetSocketAddress socketAddress = new InetSocketAddress(localAddress, port);
try (DatagramChannel channel = DatagramChannel.open()) {
channel.bind(socketAddress);
System.out.println("Listening on port 9876...");
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (true) {
buffer.clear();
InetSocketAddress clientAddress = (InetSocketAddress) channel.receive(buffer);
buffer.flip();
String receivedMessage = StandardCharsets.UTF_8.decode(buffer).toString();
System.out.println("Message: " + receivedMessage);
int clientPort = clientAddress.getPort();
UdpRealIp.getRealIp(channel, clientAddress.getAddress().getHostAddress(), clientPort);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
BIO场景测试-UDP
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
public class TestUoaServerB {
public static void main(String[] args) {
DatagramSocket socket = null;
try {
InetAddress localAddress = InetAddress.getByName("服务端IP");
int port = 9876;
InetSocketAddress socketAddress = new InetSocketAddress(localAddress, port);
socket = new DatagramSocket(socketAddress);
byte[] receiveData = new byte[1024];
System.out.println("Listene 9876...");
while (true) {
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
socket.receive(receivePacket);
String receivedMessage = new String(receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("Message: " + receivedMessage);
InetAddress clientAddress = receivePacket.getAddress();
int clientPort = receivePacket.getPort();
UdpRealIp.getRealIp(socket, clientAddress.getHostAddress(), clientPort);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (socket != null && !socket.isClosed()) {
socket.close();
}
}
}
}
分别编译上述实现类:例如
javac TestUoaServerB.java
执行
java -Djava.library.path=包含uoa.so的路径 -cp . TestUoaServerB
使用nc模拟客户端UDP报文
echo "Hello, UDP" | nc -u evip port
预期内容
Message: Hello, UDP
Received packet from IP: realSourceIp:真实客户端IP, realSourcePort:真实客户端端口