From 496da3f0c483f178c397c3b2d8941fff03174298 Mon Sep 17 00:00:00 2001
From: YuQing <384681@qq.com>
Date: Mon, 21 Jul 2025 16:51:16 +0800
Subject: [PATCH] protocol automatically compatible with IPv4/IPv6 address
---
HISTORY | 4 +
pom.xml | 2 +-
.../org/csource/fastdfs/ClientGlobal.java | 9 +-
.../java/org/csource/fastdfs/ProtoCommon.java | 18 ++-
.../org/csource/fastdfs/StorageClient.java | 17 ++-
.../fastdfs/StructIPv4StorageStat.java | 45 ++++++
.../fastdfs/StructIPv6StorageStat.java | 45 ++++++
.../csource/fastdfs/StructStorageStat.java | 31 ++--
.../org/csource/fastdfs/TrackerClient.java | 141 ++++++++++++------
src/test/java/org/csource/fastdfs/Test1.java | 2 +-
10 files changed, 237 insertions(+), 77 deletions(-)
create mode 100644 src/main/java/org/csource/fastdfs/StructIPv4StorageStat.java
create mode 100644 src/main/java/org/csource/fastdfs/StructIPv6StorageStat.java
diff --git a/HISTORY b/HISTORY
index 25e8be3..dfd20a5 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,4 +1,8 @@
+Version 1.33 2024-07-21
+ * protocol automatically compatible with IPv4/IPv6 address
+ this version for FastDFS server V6.06 and higher version
+
Version 1.32 2024-02-12
* change FDFS_VERSION_SIZE from 6 to 8
you must upgrade your FastDFS server to V6.12 or higher version
diff --git a/pom.xml b/pom.xml
index 0b4cb6c..ec93418 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser + * General Public License (LGPL). + * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. + */ + +package org.csource.fastdfs; + +import java.util.Date; + +/** + * C struct body decoder + * + * @author Happy Fish / YuQing + * @version Version 1.33 + */ +public class StructIPv4StorageStat extends StructStorageStat { + protected static int fieldsTotalSize; + protected static StructBase.FieldInfo[] fieldsArray = new StructBase.FieldInfo[StructStorageStat.FIELD_COUNT]; + + static { + fieldsTotalSize = StructStorageStat.initFieldsArray(fieldsArray, + ProtoCommon.FDFS_IPV4_SIZE, ProtoCommon.FDFS_OLD_VERSION_SIZE); + } + + /** + * get fields total size + * + * @return fields total size + */ + public static int getFieldsTotalSize() { + return fieldsTotalSize; + } + + /** + * + * @param bs byte array + * @param offset start offset + */ + public void setFields(byte[] bs, int offset) { + super.setFields(fieldsArray, bs, offset); + } +} diff --git a/src/main/java/org/csource/fastdfs/StructIPv6StorageStat.java b/src/main/java/org/csource/fastdfs/StructIPv6StorageStat.java new file mode 100644 index 0000000..ae1aca0 --- /dev/null +++ b/src/main/java/org/csource/fastdfs/StructIPv6StorageStat.java @@ -0,0 +1,45 @@ +/** + * Copyright (C) 2025 Happy Fish / YuQing + *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail.
+ */
+
+package org.csource.fastdfs;
+
+import java.util.Date;
+
+/**
+ * C struct body decoder
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.33
+ */
+public class StructIPv6StorageStat extends StructStorageStat {
+ protected static int fieldsTotalSize;
+ protected static StructBase.FieldInfo[] fieldsArray = new StructBase.FieldInfo[StructStorageStat.FIELD_COUNT];
+
+ static {
+ fieldsTotalSize = StructStorageStat.initFieldsArray(fieldsArray,
+ ProtoCommon.FDFS_IPV6_SIZE, ProtoCommon.FDFS_NEW_VERSION_SIZE);
+ }
+
+ /**
+ * get fields total size
+ *
+ * @return fields total size
+ */
+ public static int getFieldsTotalSize() {
+ return fieldsTotalSize;
+ }
+
+ /**
+ *
+ * @param bs byte array
+ * @param offset start offset
+ */
+ public void setFields(byte[] bs, int offset) {
+ super.setFields(fieldsArray, bs, offset);
+ }
+}
diff --git a/src/main/java/org/csource/fastdfs/StructStorageStat.java b/src/main/java/org/csource/fastdfs/StructStorageStat.java
index 7d17f5e..7693921 100644
--- a/src/main/java/org/csource/fastdfs/StructStorageStat.java
+++ b/src/main/java/org/csource/fastdfs/StructStorageStat.java
@@ -14,7 +14,7 @@ import java.util.Date;
* C struct body decoder
*
* @author Happy Fish / YuQing
- * @version Version 1.25
+ * @version Version 1.33
*/
public class StructStorageStat extends StructBase {
protected static final int FIELD_INDEX_STATUS = 0;
@@ -82,10 +82,9 @@ public class StructStorageStat extends StructBase {
protected static final int FIELD_INDEX_LAST_HEART_BEAT_TIME = 60;
protected static final int FIELD_INDEX_IF_TRUNK_FILE = 61;
- protected static int fieldsTotalSize;
- protected static StructBase.FieldInfo[] fieldsArray = new StructBase.FieldInfo[62];
+ protected static final int FIELD_COUNT = 62;
- static {
+ protected static int initFieldsArray(StructBase.FieldInfo[] fieldsArray, int ipaddr_size, int version_size) {
int offset = 0;
fieldsArray[FIELD_INDEX_STATUS] = new StructBase.FieldInfo("status", offset, 1);
@@ -94,8 +93,8 @@ public class StructStorageStat extends StructBase {
fieldsArray[FIELD_INDEX_ID] = new StructBase.FieldInfo("id", offset, ProtoCommon.FDFS_STORAGE_ID_MAX_SIZE);
offset += ProtoCommon.FDFS_STORAGE_ID_MAX_SIZE;
- fieldsArray[FIELD_INDEX_IP_ADDR] = new StructBase.FieldInfo("ipAddr", offset, ProtoCommon.FDFS_IPADDR_SIZE);
- offset += ProtoCommon.FDFS_IPADDR_SIZE;
+ fieldsArray[FIELD_INDEX_IP_ADDR] = new StructBase.FieldInfo("ipAddr", offset, ipaddr_size);
+ offset += ipaddr_size;
fieldsArray[FIELD_INDEX_DOMAIN_NAME] = new StructBase.FieldInfo("domainName", offset, ProtoCommon.FDFS_DOMAIN_NAME_MAX_SIZE);
offset += ProtoCommon.FDFS_DOMAIN_NAME_MAX_SIZE;
@@ -103,8 +102,8 @@ public class StructStorageStat extends StructBase {
fieldsArray[FIELD_INDEX_SRC_ID] = new StructBase.FieldInfo("srcId", offset, ProtoCommon.FDFS_STORAGE_ID_MAX_SIZE);
offset += ProtoCommon.FDFS_STORAGE_ID_MAX_SIZE;
- fieldsArray[FIELD_INDEX_VERSION] = new StructBase.FieldInfo("version", offset, ProtoCommon.FDFS_VERSION_SIZE);
- offset += ProtoCommon.FDFS_VERSION_SIZE;
+ fieldsArray[FIELD_INDEX_VERSION] = new StructBase.FieldInfo("version", offset, version_size);
+ offset += version_size;
fieldsArray[FIELD_INDEX_JOIN_TIME] = new StructBase.FieldInfo("joinTime", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
@@ -274,7 +273,7 @@ public class StructStorageStat extends StructBase {
fieldsArray[FIELD_INDEX_IF_TRUNK_FILE] = new StructBase.FieldInfo("ifTrunkServer", offset, 1);
offset += 1;
- fieldsTotalSize = offset;
+ return offset;
}
protected byte status;
@@ -340,15 +339,6 @@ public class StructStorageStat extends StructBase {
protected Date lastHeartBeatTime;
protected boolean ifTrunkServer;
- /**
- * get fields total size
- *
- * @return fields total size
- */
- public static int getFieldsTotalSize() {
- return fieldsTotalSize;
- }
-
/**
* get storage status
*
@@ -913,7 +903,7 @@ public class StructStorageStat extends StructBase {
* @param bs byte array
* @param offset start offset
*/
- public void setFields(byte[] bs, int offset) {
+ protected void setFields(StructBase.FieldInfo[] fieldsArray, byte[] bs, int offset) {
this.status = byteValue(bs, offset, fieldsArray[FIELD_INDEX_STATUS]);
this.id = stringValue(bs, offset, fieldsArray[FIELD_INDEX_ID]);
this.ipAddr = stringValue(bs, offset, fieldsArray[FIELD_INDEX_IP_ADDR]);
@@ -979,4 +969,7 @@ public class StructStorageStat extends StructBase {
this.lastHeartBeatTime = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_HEART_BEAT_TIME]);
this.ifTrunkServer = booleanValue(bs, offset, fieldsArray[FIELD_INDEX_IF_TRUNK_FILE]);
}
+
+ public void setFields(byte[] bs, int offset) {
+ }
}
diff --git a/src/main/java/org/csource/fastdfs/TrackerClient.java b/src/main/java/org/csource/fastdfs/TrackerClient.java
index 29312cb..101891f 100644
--- a/src/main/java/org/csource/fastdfs/TrackerClient.java
+++ b/src/main/java/org/csource/fastdfs/TrackerClient.java
@@ -183,18 +183,30 @@ public class TrackerClient {
}
ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(),
- ProtoCommon.TRACKER_PROTO_CMD_RESP,
- ProtoCommon.TRACKER_QUERY_STORAGE_STORE_BODY_LEN);
+ ProtoCommon.TRACKER_PROTO_CMD_RESP, -1);
this.errno = pkgInfo.errno;
if (pkgInfo.errno != 0) {
return null;
}
- ip_addr = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim();
+ if (pkgInfo.body.length < ProtoCommon.TRACKER_QUERY_STORAGE_STORE_IPV4_BODY_LEN) {
+ this.errno = ProtoCommon.ERR_NO_EINVAL;
+ throw new IOException("Invalid body length: " + pkgInfo.body.length);
+ }
- port = (int) ProtoCommon.buff2long(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN
- + ProtoCommon.FDFS_IPADDR_SIZE - 1);
- store_path = pkgInfo.body[ProtoCommon.TRACKER_QUERY_STORAGE_STORE_BODY_LEN - 1];
+ int ip_size;
+ if (pkgInfo.body.length == ProtoCommon.TRACKER_QUERY_STORAGE_STORE_IPV6_BODY_LEN) {
+ ip_size = ProtoCommon.FDFS_IPV6_SIZE;
+ } else if (pkgInfo.body.length == ProtoCommon.TRACKER_QUERY_STORAGE_STORE_IPV4_BODY_LEN) {
+ ip_size = ProtoCommon.FDFS_IPV4_SIZE;
+ } else {
+ this.errno = ProtoCommon.ERR_NO_EINVAL;
+ throw new IOException("Invalid body length: " + pkgInfo.body.length);
+ }
+
+ ip_addr = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, ip_size - 1).trim();
+ port = (int) ProtoCommon.buff2long(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + ip_size - 1);
+ store_path = pkgInfo.body[pkgInfo.body.length - 1];
return new StorageServer(ip_addr, port, store_path);
} catch (IOException ex) {
@@ -269,17 +281,26 @@ public class TrackerClient {
return null;
}
- if (pkgInfo.body.length < ProtoCommon.TRACKER_QUERY_STORAGE_STORE_BODY_LEN) {
+ if (pkgInfo.body.length < ProtoCommon.TRACKER_QUERY_STORAGE_STORE_IPV4_BODY_LEN) {
this.errno = ProtoCommon.ERR_NO_EINVAL;
- return null;
+ throw new IOException("Invalid body length: " + pkgInfo.body.length);
}
int ipPortLen = pkgInfo.body.length - (ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + 1);
- final int recordLength = ProtoCommon.FDFS_IPADDR_SIZE - 1 + ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
-
- if (ipPortLen % recordLength != 0) {
- this.errno = ProtoCommon.ERR_NO_EINVAL;
- return null;
+ int recordLength = ProtoCommon.FDFS_IPV6_SIZE - 1 + ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+ int ip_size;
+ if ((pkgInfo.body.length >= ProtoCommon.TRACKER_QUERY_STORAGE_STORE_IPV6_BODY_LEN) &&
+ ipPortLen % recordLength == 0)
+ {
+ ip_size = ProtoCommon.FDFS_IPV6_SIZE;
+ } else {
+ recordLength = ProtoCommon.FDFS_IPV4_SIZE - 1 + ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+ if (ipPortLen % recordLength == 0) {
+ ip_size = ProtoCommon.FDFS_IPV4_SIZE;
+ } else {
+ this.errno = ProtoCommon.ERR_NO_EINVAL;
+ throw new IOException("Invalid body length: " + pkgInfo.body.length);
+ }
}
int serverCount = ipPortLen / recordLength;
@@ -293,8 +314,8 @@ public class TrackerClient {
int offset = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
for (int i = 0; i < serverCount; i++) {
- ip_addr = new String(pkgInfo.body, offset, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim();
- offset += ProtoCommon.FDFS_IPADDR_SIZE - 1;
+ ip_addr = new String(pkgInfo.body, offset, ip_size - 1).trim();
+ offset += ip_size - 1;
port = (int) ProtoCommon.buff2long(pkgInfo.body, offset);
offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
@@ -424,18 +445,33 @@ public class TrackerClient {
return null;
}
- if (pkgInfo.body.length < ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_BODY_LEN) {
+ int server_count = 1;
+ int ip_size;
+ if (pkgInfo.body.length < ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_IPV4_BODY_LEN) {
+ this.errno = ProtoCommon.ERR_NO_EINVAL;
throw new IOException("Invalid body length: " + pkgInfo.body.length);
}
- if ((pkgInfo.body.length - ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_BODY_LEN) % (ProtoCommon.FDFS_IPADDR_SIZE - 1) != 0) {
+ if ((pkgInfo.body.length >= ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_IPV6_BODY_LEN) &&
+ (pkgInfo.body.length - ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_IPV6_BODY_LEN) %
+ (ProtoCommon.FDFS_IPV6_SIZE - 1) == 0)
+ {
+ ip_size = ProtoCommon.FDFS_IPV6_SIZE;
+ server_count += (pkgInfo.body.length - ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_IPV6_BODY_LEN) /
+ (ProtoCommon.FDFS_IPV6_SIZE - 1);
+ } else if ((pkgInfo.body.length - ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_IPV4_BODY_LEN) %
+ (ProtoCommon.FDFS_IPV4_SIZE - 1) == 0)
+ {
+ ip_size = ProtoCommon.FDFS_IPV4_SIZE;
+ server_count += (pkgInfo.body.length - ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_IPV4_BODY_LEN) /
+ (ProtoCommon.FDFS_IPV4_SIZE - 1);
+ } else {
+ this.errno = ProtoCommon.ERR_NO_EINVAL;
throw new IOException("Invalid body length: " + pkgInfo.body.length);
}
- int server_count = 1 + (pkgInfo.body.length - ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_BODY_LEN) / (ProtoCommon.FDFS_IPADDR_SIZE - 1);
-
- ip_addr = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim();
- int offset = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + ProtoCommon.FDFS_IPADDR_SIZE - 1;
+ ip_addr = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, ip_size - 1).trim();
+ int offset = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + ip_size - 1;
port = (int) ProtoCommon.buff2long(pkgInfo.body, offset);
offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
@@ -443,8 +479,8 @@ public class TrackerClient {
ServerInfo[] servers = new ServerInfo[server_count];
servers[0] = new ServerInfo(ip_addr, port);
for (int i = 1; i < server_count; i++) {
- servers[i] = new ServerInfo(new String(pkgInfo.body, offset, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim(), port);
- offset += ProtoCommon.FDFS_IPADDR_SIZE - 1;
+ servers[i] = new ServerInfo(new String(pkgInfo.body, offset, ip_size - 1).trim(), port);
+ offset += ip_size - 1;
}
return servers;
@@ -568,6 +604,20 @@ public class TrackerClient {
return this.listStorages(trackerServer, groupName, storageIpAddr);
}
+ private int getIpaddrLength(String ip, byte[] bIpAddr) {
+ if (bIpAddr.length < ProtoCommon.FDFS_IPV4_SIZE) {
+ return bIpAddr.length;
+ } else if (ip.indexOf(':') >= 0) { //IPv6 address
+ if (bIpAddr.length < ProtoCommon.FDFS_IPV6_SIZE) {
+ return bIpAddr.length;
+ } else {
+ return ProtoCommon.FDFS_IPV6_SIZE - 1;
+ }
+ } else { //IPv4 address
+ return ProtoCommon.FDFS_IPV4_SIZE - 1;
+ }
+ }
+
/**
* query storage server stat info of the group
*
@@ -576,8 +626,8 @@ public class TrackerClient {
* @param storageIpAddr the storage server ip address, can be null or empty
* @return storage server stat array, return null if fail
*/
- public StructStorageStat[] listStorages(TrackerServer trackerServer,
- String groupName, String storageIpAddr) throws IOException, MyException {
+ public StructStorageStat[] listStorages(TrackerServer trackerServer, String groupName,
+ String storageIpAddr) throws IOException, MyException {
byte[] header;
byte[] bGroupName;
byte[] bs;
@@ -601,11 +651,7 @@ public class TrackerClient {
byte[] bIpAddr;
if (storageIpAddr != null && storageIpAddr.length() > 0) {
bIpAddr = storageIpAddr.getBytes(ClientGlobal.g_charset);
- if (bIpAddr.length < ProtoCommon.FDFS_IPADDR_SIZE) {
- ipAddrLen = bIpAddr.length;
- } else {
- ipAddrLen = ProtoCommon.FDFS_IPADDR_SIZE - 1;
- }
+ ipAddrLen = getIpaddrLength(storageIpAddr, bIpAddr);
} else {
bIpAddr = null;
ipAddrLen = 0;
@@ -627,8 +673,15 @@ public class TrackerClient {
return null;
}
- ProtoStructDecoder