From 8f7a593c18edcc701c41c9f6fa3f2f1225d14156 Mon Sep 17 00:00:00 2001 From: tangjiapeng Date: Wed, 14 May 2025 16:27:32 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E7=BB=84=E8=A3=85=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +- .../xcdebugger/Breakpoints_v2.xcbkptlist | 16 ++ .../TJPNetworkMonitorViewController.m | 2 - .../Category/UIImage+TJPImageOrientation.h | 20 ++ .../Category/UIImage+TJPImageOrientation.m | 97 ++++++++++ .../NetworkUtility/TJPNetworkDefine.h | 4 + .../TJPConcreteSession+TJPMessageAdapter.h | 21 --- .../TJPConcreteSession+TJPMessageAdapter.m | 17 -- .../Bulider/TJPMessageBuilder.h | 20 ++ .../Bulider/TJPMessageBuilder.m | 73 ++++++++ .../Container/TJPConcreteSession.m | 52 ++--- .../Context/TJPMessageContext.h | 20 +- .../Context/TJPMessageContext.m | 28 ++- .../V3_FinalProduct/IMClient/TJPIMClient.h | 2 +- .../Message/TJPMessageSerializer.h | 16 +- .../Message/TJPMessageSerializer.m | 127 ++++++++++++- .../V3_FinalProduct/Message/TJPTextMessage.h | 1 + .../V3_FinalProduct/Message/TJPTextMessage.m | 4 +- .../V3_FinalProduct/Parser/TJPMessageParser.m | 177 +++++++++++++++++- .../V3_FinalProduct/Protocol/TJPMessage.h | 2 +- .../V3_FinalProduct/Utility/TJPCoreTypes.h | 30 ++- .../V3_FinalProduct/Utility/TJPNetworkUtil.h | 5 +- .../V3_FinalProduct/Utility/TJPNetworkUtil.m | 60 +++--- ...10\346\234\254\345\206\205\345\256\271.md" | 55 ++++++ 24 files changed, 742 insertions(+), 115 deletions(-) create mode 100644 iOS-Network-Stack-Dive/ArchitectureExtensions/Utility/Category/UIImage+TJPImageOrientation.h create mode 100644 iOS-Network-Stack-Dive/ArchitectureExtensions/Utility/Category/UIImage+TJPImageOrientation.m delete mode 100644 iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Adapter/TJPConcreteSession+TJPMessageAdapter.h delete mode 100644 iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Adapter/TJPConcreteSession+TJPMessageAdapter.m create mode 100644 iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Bulider/TJPMessageBuilder.h create mode 100644 iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Bulider/TJPMessageBuilder.m create mode 100644 "iOS-Network-Stack-Dive/Docs/Version/v1.2.0\347\211\210\346\234\254\345\206\205\345\256\271.md" diff --git a/README.md b/README.md index ce7ebaf..bbf62d0 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,9 @@ ## 项目进度与规划 ### ✅ **已发布版本** #### v1.0.0 核心能力:**生产级网络通信架构** | **企业级 VIPER 架构** -#### v1.1.0 核心能力:**多维度指标监控、全链路追踪** +#### v1.1.0 核心能力:**多维度指标监控、全链路追踪** + +#### v1.2.0 核心能力:TLV协议扩展增强 --- @@ -130,7 +132,7 @@ client.send(message: imageMessage) { error in #### 已知问题 -- AOP切面日志:多参数方法监听崩溃,问题已定位,后期修复。 +- AOP切面日志(非重点):多参数方法监听崩溃,问题已定位,后期修复。 ### 版本记录 - **v1.0.0**:网络框架基础核心功能基本完成、生产级VIPER架构演示完成 @@ -202,7 +204,7 @@ Socket通信模块架构 +-------------------------+ ``` ### TLV数据区格式 -- **Tag**:业务标识(如 0x1001=用户ID),大端字节序。 +- **Tag**:业务标识(如 0x1001=文字消息),大端字节序。 - **Length**:Value 部分长度(不含 Tag 和 Length),大端字节序。 - **Value**:原始数据或嵌套 TLV(用保留 Tag 0xFFFF 标记)。 ``` diff --git a/iOS-Network-Stack-Dive.xcworkspace/xcuserdata/aarongtang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/iOS-Network-Stack-Dive.xcworkspace/xcuserdata/aarongtang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 313a690..f58a2b3 100644 --- a/iOS-Network-Stack-Dive.xcworkspace/xcuserdata/aarongtang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/iOS-Network-Stack-Dive.xcworkspace/xcuserdata/aarongtang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -24,5 +24,21 @@ + + + + diff --git a/iOS-Network-Stack-Dive/ArchitectureExtensions/NetworkMonitor/TJPNetworkMonitorViewController.m b/iOS-Network-Stack-Dive/ArchitectureExtensions/NetworkMonitor/TJPNetworkMonitorViewController.m index de84c98..ce899d5 100644 --- a/iOS-Network-Stack-Dive/ArchitectureExtensions/NetworkMonitor/TJPNetworkMonitorViewController.m +++ b/iOS-Network-Stack-Dive/ArchitectureExtensions/NetworkMonitor/TJPNetworkMonitorViewController.m @@ -78,8 +78,6 @@ - (void)setupNetwork { self.client = [TJPIMClient shared]; [self.client connectToHost:host port:port]; - - } - (void)setupLogTextView { diff --git a/iOS-Network-Stack-Dive/ArchitectureExtensions/Utility/Category/UIImage+TJPImageOrientation.h b/iOS-Network-Stack-Dive/ArchitectureExtensions/Utility/Category/UIImage+TJPImageOrientation.h new file mode 100644 index 0000000..bf9cf46 --- /dev/null +++ b/iOS-Network-Stack-Dive/ArchitectureExtensions/Utility/Category/UIImage+TJPImageOrientation.h @@ -0,0 +1,20 @@ +// +// UIImage+TJPImageOrientation.h +// iOS-Network-Stack-Dive +// +// Created by 唐佳鹏 on 2025/5/14. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIImage (TJPImageOrientation) + +/// 修正图片方向(解决拍照图片旋转问题) +- (UIImage *)fixOrientation; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/iOS-Network-Stack-Dive/ArchitectureExtensions/Utility/Category/UIImage+TJPImageOrientation.m b/iOS-Network-Stack-Dive/ArchitectureExtensions/Utility/Category/UIImage+TJPImageOrientation.m new file mode 100644 index 0000000..9b7c8f6 --- /dev/null +++ b/iOS-Network-Stack-Dive/ArchitectureExtensions/Utility/Category/UIImage+TJPImageOrientation.m @@ -0,0 +1,97 @@ +// +// UIImage+TJPImageOrientation.m +// iOS-Network-Stack-Dive +// +// Created by 唐佳鹏 on 2025/5/14. +// + +#import "UIImage+TJPImageOrientation.h" + +@implementation UIImage (TJPImageOrientation) + +- (UIImage *)fixOrientation { + // 方向正常的图片直接返回 + if (self.imageOrientation == UIImageOrientationUp) { + return self; + } + + // 计算变换矩阵 + CGAffineTransform transform = CGAffineTransformIdentity; + + switch (self.imageOrientation) { + case UIImageOrientationDown: + case UIImageOrientationDownMirrored: + transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height); + transform = CGAffineTransformRotate(transform, M_PI); + break; + + case UIImageOrientationLeft: + case UIImageOrientationLeftMirrored: + transform = CGAffineTransformTranslate(transform, self.size.width, 0); + transform = CGAffineTransformRotate(transform, M_PI_2); + break; + + case UIImageOrientationRight: + case UIImageOrientationRightMirrored: + transform = CGAffineTransformTranslate(transform, 0, self.size.height); + transform = CGAffineTransformRotate(transform, -M_PI_2); + break; + + default: + break; + } + + // 处理镜像情况 + switch (self.imageOrientation) { + case UIImageOrientationUpMirrored: + case UIImageOrientationDownMirrored: + transform = CGAffineTransformTranslate(transform, self.size.width, 0); + transform = CGAffineTransformScale(transform, -1, 1); + break; + + case UIImageOrientationLeftMirrored: + case UIImageOrientationRightMirrored: + transform = CGAffineTransformTranslate(transform, self.size.height, 0); + transform = CGAffineTransformScale(transform, -1, 1); + break; + + default: + break; + } + + // 应用变换 + CGContextRef ctx = CGBitmapContextCreate( + NULL, + self.size.width, + self.size.height, + CGImageGetBitsPerComponent(self.CGImage), + 0, + CGImageGetColorSpace(self.CGImage), + CGImageGetBitmapInfo(self.CGImage) + ); + + CGContextConcatCTM(ctx, transform); + + switch (self.imageOrientation) { + case UIImageOrientationLeft: + case UIImageOrientationLeftMirrored: + case UIImageOrientationRight: + case UIImageOrientationRightMirrored: + CGContextDrawImage(ctx, CGRectMake(0, 0, self.size.height, self.size.width), self.CGImage); + break; + + default: + CGContextDrawImage(ctx, CGRectMake(0, 0, self.size.width, self.size.height), self.CGImage); + break; + } + + CGImageRef cgimg = CGBitmapContextCreateImage(ctx); + UIImage *result = [UIImage imageWithCGImage:cgimg]; + + CGContextRelease(ctx); + CGImageRelease(cgimg); + + return result ?: self; +} + +@end diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/NetworkUtility/TJPNetworkDefine.h b/iOS-Network-Stack-Dive/CoreNetworkStack/NetworkUtility/TJPNetworkDefine.h index 789e13c..42e624f 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/NetworkUtility/TJPNetworkDefine.h +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/NetworkUtility/TJPNetworkDefine.h @@ -46,6 +46,10 @@ #define kHeartbeatTimeoutNotification @"kHeartbeatTimeoutNotification" +#define TJPMAX_BODY_SIZE (10 * 1024 * 1024) // 10MB 最大消息体大小 +#define TJPMAX_BUFFER_SIZE (20 * 1024 * 1024) // 20MB 最大缓冲区大小 +#define TJPMAX_TIME_WINDOW 60 // 60秒时间窗口,防重放攻击 + #define TJPSCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width) diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Adapter/TJPConcreteSession+TJPMessageAdapter.h b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Adapter/TJPConcreteSession+TJPMessageAdapter.h deleted file mode 100644 index 04e5fc5..0000000 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Adapter/TJPConcreteSession+TJPMessageAdapter.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// TJPConcreteSession+TJPMessageAdapter.h -// iOS-Network-Stack-Dive -// -// Created by 唐佳鹏 on 2025/5/13. -// - -#import "TJPConcreteSession.h" -#import "TJPMessage.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface TJPConcreteSession (TJPMessageAdapter) - -/// 发送消息 -- (void)sendMessage:(id)message; - - -@end - -NS_ASSUME_NONNULL_END diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Adapter/TJPConcreteSession+TJPMessageAdapter.m b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Adapter/TJPConcreteSession+TJPMessageAdapter.m deleted file mode 100644 index 8d7e613..0000000 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Adapter/TJPConcreteSession+TJPMessageAdapter.m +++ /dev/null @@ -1,17 +0,0 @@ -// -// TJPConcreteSession+TJPMessageAdapter.m -// iOS-Network-Stack-Dive -// -// Created by 唐佳鹏 on 2025/5/13. -// - -#import "TJPConcreteSession+TJPMessageAdapter.h" - -@implementation TJPConcreteSession (TJPMessageAdapter) - -- (void)sendMessage:(id)message { - NSData *tlvData = [message tlvData]; - [self sendData:tlvData]; -} - -@end diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Bulider/TJPMessageBuilder.h b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Bulider/TJPMessageBuilder.h new file mode 100644 index 0000000..04747fe --- /dev/null +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Bulider/TJPMessageBuilder.h @@ -0,0 +1,20 @@ +// +// TJPMessageBuilder.h +// iOS-Network-Stack-Dive +// +// Created by 唐佳鹏 on 2025/5/14. +// 消息组装类 + +#import +#import "TJPCoreTypes.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TJPMessageBuilder : NSObject + +/// 组装数据包 ++ (NSData *)buildPacketWithMessageType:(TJPMessageType)msgType sequence:(uint32_t)sequence payload:(NSData *)payload encryptType:(TJPEncryptType)encryptType compressType:(TJPCompressType)compressType sessionID:(NSString *)sessionID; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Bulider/TJPMessageBuilder.m b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Bulider/TJPMessageBuilder.m new file mode 100644 index 0000000..fbbf917 --- /dev/null +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Bulider/TJPMessageBuilder.m @@ -0,0 +1,73 @@ +// +// TJPMessageBuilder.m +// iOS-Network-Stack-Dive +// +// Created by 唐佳鹏 on 2025/5/14. +// + +#import "TJPMessageBuilder.h" +#import "TJPNetworkDefine.h" +#import "TJPNetworkUtil.h" + +@implementation TJPMessageBuilder + + ++ (NSData *)buildPacketWithMessageType:(TJPMessageType)msgType sequence:(uint32_t)sequence payload:(NSData *)payload encryptType:(TJPEncryptType)encryptType compressType:(TJPCompressType)compressType sessionID:(NSString *)sessionID { + if (!payload) { + payload = [NSData data]; // 空载荷使用空数据 + } + + // 确保数据大小不超过限制 + if (payload.length > TJPMAX_BODY_SIZE) { + TJPLOG_ERROR(@"负载数据过大: %lu > %d", (unsigned long)payload.length, TJPMAX_BODY_SIZE); + return nil; + } + + // 初始化协议头 + TJPFinalAdavancedHeader header = {0}; + //网络字节序转换 + header.magic = htonl(kProtocolMagic); + header.version_major = kProtocolVersionMajor; + header.version_minor = kProtocolVersionMinor; + header.msgType = htons(msgType); + header.sequence = htonl(sequence); + header.timestamp = htonl((uint32_t)[[NSDate date] timeIntervalSince1970]); // 当前时间戳 + header.encrypt_type = encryptType; + header.compress_type = compressType; + header.session_id = htons([self sessionIDFromUUID:sessionID]); + + + header.bodyLength = htonl((uint32_t)payload.length); + + // 计算数据体的CRC32 + uint32_t checksum = [TJPNetworkUtil crc32ForData:payload]; + header.checksum = htonl(checksum); // 注意要转换为网络字节序 + + // 构建完整协议包 + NSMutableData *packet = [NSMutableData dataWithBytes:&header length:sizeof(header)]; + [packet appendData:payload]; + return packet; +} + +// 从UUID字符串生成16位会话ID ++ (uint16_t)sessionIDFromUUID:(NSString *)uuidString { + if (!uuidString || uuidString.length == 0) { + return 0; + } + + // 移除UUID中的"-"字符 + NSString *cleanUUID = [uuidString stringByReplacingOccurrencesOfString:@"-" withString:@""]; + + // 取UUID的前8个字符,转换为32位整数 + NSString *prefix = [cleanUUID substringToIndex:MIN(8, cleanUUID.length)]; + unsigned int value = 0; + [[NSScanner scannerWithString:prefix] scanHexInt:&value]; + + // 折叠32位值为16位:XOR高16位和低16位 + uint16_t highPart = (uint16_t)(value >> 16); + uint16_t lowPart = (uint16_t)(value & 0xFFFF); + + return highPart ^ lowPart; // XOR操作保持更好的分布 +} + +@end diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Container/TJPConcreteSession.m b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Container/TJPConcreteSession.m index 87ecaca..8562fcc 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Container/TJPConcreteSession.m +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Container/TJPConcreteSession.m @@ -17,6 +17,7 @@ #import "TJPReconnectPolicy.h" #import "TJPDynamicHeartbeat.h" #import "TJPMessageParser.h" +#import "TJPMessageBuilder.h" #import "TJPMessageContext.h" #import "TJPParsedPacket.h" #import "TJPSequenceManager.h" @@ -27,6 +28,7 @@ + static const NSTimeInterval kDefaultRetryInterval = 10; @interface TJPConcreteSession () @@ -231,21 +233,45 @@ - (void)sendData:(NSData *)data { return; } TJPLOG_INFO(@"session 准备构造数据包"); + + // 参数验证 + if (!data) { + TJPLOG_ERROR(@"发送数据为空"); + return; + } + + // 检查数据大小 + if (data.length > TJPMAX_BODY_SIZE) { + TJPLOG_ERROR(@"数据大小超过限制: %lu > %d", (unsigned long)data.length, TJPMAX_BODY_SIZE); + return; + } + + //创建序列号 uint32_t seq = [self.seqManager nextSequenceForCategory:TJPMessageCategoryNormal]; + // 获取当前会话使用的加密和压缩类型 + TJPEncryptType encryptType = TJPEncryptTypeCRC32; + TJPCompressType compressType = TJPCompressTypeZlib; + + //构造协议包 实际通过Socket发送的协议包(协议头+原始数据) - NSData *packet = [self _buildPacketWithData:data seq:seq]; + NSData *packet = [TJPMessageBuilder buildPacketWithMessageType:TJPMessageTypeNormalData sequence:seq payload:data encryptType:encryptType compressType:compressType sessionID:self.sessionId]; + + if (!packet) { + TJPLOG_ERROR(@"消息包构建失败"); + return; + } //消息的上下文,用于跟踪消息状态(发送时间,重试次数,序列号) - TJPMessageContext *context = [TJPMessageContext contextWithData:data seq:seq]; + TJPMessageContext *context = [TJPMessageContext contextWithData:data seq:seq messageType:TJPMessageTypeNormalData encryptType:encryptType compressType:compressType sessionId:self.sessionId]; //存储待确认消息 self.pendingMessages[@(context.sequence)] = context; //设置超时重传 [self scheduleRetransmissionForSequence:context.sequence]; - TJPLOG_INFO(@"session 消息即将发出"); + TJPLOG_INFO(@"session 消息即将发出, 序列号: %u, 大小: %lu字节", seq, (unsigned long)packet.length); //发送消息 [self.socket writeData:packet withTimeout:-1 tag:context.sequence]; }); @@ -581,26 +607,6 @@ - (void)handleHeartbeatTimeout:(NSNotification *)notification { #pragma mark - Private Method -- (NSData *)_buildPacketWithData:(NSData *)data seq:(uint32_t)seq { - // 初始化协议头 - TJPFinalAdavancedHeader header = {0}; - header.magic = htonl(kProtocolMagic); - header.version_major = kProtocolVersionMajor; - header.version_minor = kProtocolVersionMinor; - header.msgType = htons(TJPMessageTypeNormalData); // 普通消息类型 - header.sequence = htonl(seq); - header.bodyLength = htonl((uint32_t)data.length); - - // 计算数据体的CRC32 - uint32_t checksum = [TJPNetworkUtil crc32ForData:data]; - header.checksum = htonl(checksum); // 注意要转换为网络字节序 - - // 构建完整协议包 - NSMutableData *packet = [NSMutableData dataWithBytes:&header length:sizeof(header)]; - [packet appendData:data]; - return packet; -} - //超时重传 - (void)scheduleRetransmissionForSequence:(uint32_t)sequence { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(dispatch_time(DISPATCH_TIME_NOW, kDefaultRetryInterval) * NSEC_PER_SEC)), self->_socketQueue, ^{ diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Context/TJPMessageContext.h b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Context/TJPMessageContext.h index 5946d21..42dab6d 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Context/TJPMessageContext.h +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Context/TJPMessageContext.h @@ -6,6 +6,7 @@ // 消息上下文 用于记录相关元数据 #import +#import "TJPCoreTypes.h" NS_ASSUME_NONNULL_BEGIN @@ -17,11 +18,28 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong) NSDate *sendTime; /// 重试次数 @property (nonatomic, assign) NSInteger retryCount; +/// 加密类型 +@property (nonatomic, assign) TJPEncryptType encryptType; +/// 压缩类型 +@property (nonatomic, assign) TJPCompressType compressType; +/// 会话ID +@property (nonatomic, copy) NSString *sessionId; +/// 消息类型 +@property (nonatomic, assign) TJPMessageType messageType; +/// 最大重试次数 +@property (nonatomic, assign) NSInteger maxRetryCount; +/// 重试超时时间(秒) +@property (nonatomic, assign) NSTimeInterval retryTimeout; -+ (instancetype)contextWithData:(NSData *)data seq:(uint32_t)seq; ++ (instancetype)contextWithData:(NSData *)data seq:(uint32_t)seq messageType:(TJPMessageType)messageType encryptType:(TJPEncryptType)encryptType compressType:(TJPCompressType)compressType sessionId:(NSString *)sessionId; + +//构建重传包 - (NSData *)buildRetryPacket; +//是否重传 +- (BOOL)shouldRetry; +- (NSTimeInterval)timeElapsedSinceLastSend; @end diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Context/TJPMessageContext.m b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Context/TJPMessageContext.m index 7b8c89c..b50f1b0 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Context/TJPMessageContext.m +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Context/TJPMessageContext.m @@ -7,6 +7,7 @@ #import "TJPMessageContext.h" #import "TJPNetworkUtil.h" +#import "TJPMessageBuilder.h" @interface TJPMessageContext () @@ -17,17 +18,40 @@ @implementation TJPMessageContext { NSData *_originData; } -+ (instancetype)contextWithData:(NSData *)data seq:(uint32_t)seq { ++ (instancetype)contextWithData:(NSData *)data seq:(uint32_t)seq messageType:(TJPMessageType)messageType encryptType:(TJPEncryptType)encryptType compressType:(TJPCompressType)compressType sessionId:(NSString *)sessionId { TJPMessageContext *context = [TJPMessageContext new]; context->_originData = data; context.sendTime = [NSDate date]; context.retryCount = 0; context.sequence = seq; + context.messageType = messageType; + context.encryptType = encryptType; + context.compressType = compressType; + context.sessionId = sessionId; + + + context.maxRetryCount = 3; // 默认最多重试3次 + context.retryTimeout = 3.0; // 默认3秒超时 + return context; } - (NSData *)buildRetryPacket { _retryCount++; - return [TJPNetworkUtil buildPacketWithData:_originData type:TJPMessageTypeNormalData sequence:_sequence]; + self.sendTime = [NSDate date]; // 更新发送时间 + return [TJPMessageBuilder buildPacketWithMessageType:self.messageType sequence:self.sequence payload:_originData encryptType:self.encryptType compressType:self.compressType sessionID:self.sessionId]; +} + + +- (BOOL)shouldRetry { + // 判断是否应该重试 + return (self.retryCount < self.maxRetryCount); } + +- (NSTimeInterval)timeElapsedSinceLastSend { + // 计算自上次发送以来经过的时间 + return [[NSDate date] timeIntervalSinceDate:self.sendTime]; +} + + @end diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/IMClient/TJPIMClient.h b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/IMClient/TJPIMClient.h index 45120d9..812daa9 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/IMClient/TJPIMClient.h +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/IMClient/TJPIMClient.h @@ -18,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN /// 连接方法 - (void)connectToHost:(NSString *)host port:(uint16_t)port; -/// 发送消息 +/// 发送消息 消息类型详见 TJPCoreTypes 头文件定义的 TJPContentType - (void)sendMessage:(id)message; /// 断开连接 diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPMessageSerializer.h b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPMessageSerializer.h index 1821074..00ff0af 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPMessageSerializer.h +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPMessageSerializer.h @@ -11,13 +11,25 @@ NS_ASSUME_NONNULL_BEGIN @interface TJPMessageSerializer : NSObject -/// 文字序列化 + +/// 文本内容序列化成TLV格式的二进制数据 +/// - Parameters: +/// - text: 要序列化的文本内容(UTF-8编码) +/// - tag: 消息类型标识 详见 TJPContentType + (NSData *)serializeText:(NSString *)text tag:(uint16_t)tag; -/// 图片序列化 + + +/// 图片序列化成TLV格式的二进制数据 +/// - Parameters: +/// - image: 要序列化的图片 +/// - tag: 消息类型标识 详见 TJPContentType + (NSData *)serializeImage:(UIImage *)image tag:(uint16_t)tag; + +// 后续增加别的消息类型直接增加方法即可 + @end NS_ASSUME_NONNULL_END diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPMessageSerializer.m b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPMessageSerializer.m index ecb5a21..13e00e5 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPMessageSerializer.m +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPMessageSerializer.m @@ -6,23 +6,140 @@ // #import "TJPMessageSerializer.h" +#import +#import + +#import "TJPNetworkDefine.h" +#import "UIImage+TJPImageOrientation.h" @implementation TJPMessageSerializer + (NSData *)serializeText:(NSString *)text tag:(uint16_t)tag { + if (!text.length && text == nil) { + TJPLOG_ERROR(@"图片序列化失败:image参数为nil"); + return nil; + } NSData *textData = [text dataUsingEncoding:NSUTF8StringEncoding]; + + //构建TLV结构 return [self buildTLVWithTag:tag value:textData]; } + + ++ (NSData *)serializeImage:(UIImage *)image tag:(uint16_t)tag { + //参数校验 + NSCParameterAssert(image && [image isKindOfClass:[UIImage class]]); + if (!image) { + TJPLOG_ERROR(@"图片序列化失败:image参数为nil"); + return nil; + } + + //图片预处理 调整尺寸和方向 + UIImage *processedImage = [self _processImageBeforeEncoding:image]; + + // 3. 智能选择编码格式 + NSData *imageData = [self _encodeImageData:processedImage]; + if (!imageData) { + TJPLOG_ERROR(@"图片编码失败:无法生成有效数据"); + return nil; + } + + // 4. 构建TLV结构(复用基础方法) + return [self buildTLVWithTag:tag value:imageData]; +} + + +#pragma mark - Private Method ++ (UIImage *)_processImageBeforeEncoding:(UIImage *)srcImage { + //最大允许尺寸 + static const CGSize kMaxSize = {1024, 1024}; + + //方向修正 (解决图片拍摄旋转问题) + UIImage *fixedImage = [srcImage fixOrientation]; + + // 尺寸缩放检查 + if (fixedImage.size.width <= kMaxSize.width && + fixedImage.size.height <= kMaxSize.height) { + return fixedImage; + } + + // 等比例缩放 + CGFloat ratio = MIN(kMaxSize.width / fixedImage.size.width, kMaxSize.height / fixedImage.size.height); + + //处理后的尺寸 + CGSize newSize = CGSizeMake(floor(fixedImage.size.width * ratio), floor(fixedImage.size.height * ratio)); + + UIGraphicsBeginImageContextWithOptions(newSize, YES, [UIScreen mainScreen].scale); + [fixedImage drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; + UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return scaledImage ?: fixedImage; + +} + ++ (NSData *)_encodeImageData:(UIImage *)image { + // 格式选择策略 + BOOL hasAlpha = [self _imageHasAlphaChannel:image]; + + // 编码参数配置 + NSDictionary *options = @{ + // 透明图用PNG,不透明用JPEG + (id)kCGImageDestinationLossyCompressionQuality: @(hasAlpha ? 1.0 : 0.85) + }; + + // 自动选择最佳格式 + CFStringRef type = hasAlpha ? (CFStringRef)@"public.png" : (CFStringRef)@"public.jpeg"; + + // 编码为二进制数据 + NSMutableData *data = [NSMutableData data]; + CGImageDestinationRef dest = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)data, type, 1, NULL); + + if (!dest) return nil; + + CGImageDestinationAddImage(dest, image.CGImage, (__bridge CFDictionaryRef)options); + BOOL success = CGImageDestinationFinalize(dest); + CFRelease(dest); + + return success ? [data copy] : nil; +} + + + + + +#pragma mark - Common Method + (NSData *)buildTLVWithTag:(uint16_t)tag value:(NSData *)value { - uint16_t netTag = CFSwapInt16HostToBig(tag); - uint32_t netLength = CFSwapInt32HostToBig((uint32_t)value.length); + //字节序转换 (主机序→网络序) + uint16_t netTag = CFSwapInt16HostToBig(tag); // 2字节Tag转大端 + uint32_t netLength = CFSwapInt32HostToBig((uint32_t)value.length); // 4字节Length转大端 + + //单次内存分配 避免频繁扩容 (Tag2 + Length4 + valueN) + NSUInteger totalLength = 2 + 4 + value.length; - NSMutableData *data = [NSMutableData dataWithBytes:&netTag length:2]; - [data appendBytes:&netLength length:4]; - [data appendData:value]; + NSMutableData *data = [NSMutableData dataWithCapacity:totalLength]; + //直接操作底层缓冲区(零拷贝) + [data setLength:totalLength]; + uint8_t *buffer = [data mutableBytes]; + + memcpy(buffer, &netTag, 2); + memcpy(buffer + 2, &netLength, 4); + memcpy(buffer + 6, value.bytes, value.length); + return [data copy]; } +/** + * 检测图片是否包含透明通道 + */ ++ (BOOL)_imageHasAlphaChannel:(UIImage *)image { + CGImageAlphaInfo alpha = CGImageGetAlphaInfo(image.CGImage); + return (alpha == kCGImageAlphaFirst || + alpha == kCGImageAlphaLast || + alpha == kCGImageAlphaPremultipliedFirst || + alpha == kCGImageAlphaPremultipliedLast); +} + @end diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPTextMessage.h b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPTextMessage.h index fc57297..3693090 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPTextMessage.h +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPTextMessage.h @@ -13,6 +13,7 @@ NS_ASSUME_NONNULL_BEGIN @interface TJPTextMessage : NSObject @property (nonatomic, copy) NSString *text; + - (instancetype)initWithText:(NSString *)text; diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPTextMessage.m b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPTextMessage.m index cd87327..5f0680c 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPTextMessage.m +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Message/TJPTextMessage.m @@ -24,7 +24,9 @@ - (instancetype)initWithText:(NSString *)text { return self; } -+ (uint16_t)messageTag { return TJPContentTypeText; } ++ (uint16_t)messageTag { + return TJPContentTypeText; +} - (NSData *)tlvData { return [TJPMessageSerializer serializeText:self.text tag:[self.class messageTag]]; diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Parser/TJPMessageParser.m b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Parser/TJPMessageParser.m index 6164cd0..fdeea7f 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Parser/TJPMessageParser.m +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Parser/TJPMessageParser.m @@ -9,11 +9,17 @@ #import "TJPNetworkDefine.h" #import "TJPParsedPacket.h" #import "TJPCoreTypes.h" +#import "TJPNetworkUtil.h" + @interface TJPMessageParser () { TJPFinalAdavancedHeader _currentHeader; NSMutableData *_buffer; TJPParseState _state; + + //安全相关 + NSMutableSet *_recentSequences; //防重放攻击 + NSDate *_lastCleanupTime; //定期清理计数器 } @end @@ -24,24 +30,47 @@ - (instancetype)init { if (self = [super init]) { _buffer = [NSMutableData data]; _state = TJPParseStateHeader; + + _recentSequences = [NSMutableSet setWithCapacity:1000]; + _lastCleanupTime = [NSDate date]; } return self; } - (void)feedData:(NSData *)data { + // 防止缓冲区过大导致内存耗尽 + if (data.length > TJPMAX_BUFFER_SIZE || (_buffer.length + data.length) > TJPMAX_BUFFER_SIZE) { + TJPLOG_ERROR(@"数据大小超过限制: 当前缓冲区 %lu, 新增数据 %lu, 限制 %d", (unsigned long)_buffer.length, (unsigned long)data.length, TJPMAX_BUFFER_SIZE); + [self reset]; + _state = TJPParseStateError; + return; + } [_buffer appendData:data]; + + //定期清理过期序列号 + [self cleanupExpiredSequences]; } - (BOOL)hasCompletePacket { if (_state == TJPParseStateHeader) { return _buffer.length >= sizeof(TJPFinalAdavancedHeader); - }else { + }else if (_state == TJPParseStateBody) { return _buffer.length >= ntohl(_currentHeader.bodyLength); + }else if (_state == TJPParseStateError) { + // 错误状态下,需要先重置解析器 + return NO; } + return NO; } - (TJPParsedPacket *)nextPacket { + // 错误状态下不处理 + if (_state == TJPParseStateError) { + TJPLOG_ERROR(@"解析器处于错误状态,请先重置"); + return nil; + } + //如果当前数据包是头部 先解析头部 if (_state == TJPParseStateHeader) { if (![self parseHeaderData]) { @@ -66,14 +95,13 @@ - (BOOL)parseHeaderData { //解析头部 [_buffer getBytes:¤tHeader length:sizeof(TJPFinalAdavancedHeader)]; - - //魔数校验失败 - if (ntohl(currentHeader.magic) != kProtocolMagic) { - TJPLOG_INFO(@"解析头部后魔数校验失败... 请检查"); + // 安全验证 + if (![self validateHeader:currentHeader]) { _state = TJPParseStateError; return NO; } + TJPLOG_INFO(@"解析数据头部成功...魔数校验成功!"); _currentHeader = currentHeader; //移除已处理的Header数据 @@ -95,10 +123,18 @@ - (TJPParsedPacket *)parseBodyData { NSData *payload = [_buffer subdataWithRange:NSMakeRange(0, bodyLength)]; [_buffer replaceBytesInRange:NSMakeRange(0, bodyLength) withBytes:NULL length:0]; + // 验证CRC32校验和 + if (![self validateChecksum:_currentHeader.checksum forData:payload]) { + TJPLOG_ERROR(@"校验和验证失败,可能数据已被篡改"); + _state = TJPParseStateError; + return nil; + } + NSError *error = nil; TJPParsedPacket *body = [TJPParsedPacket packetWithHeader:_currentHeader payload:payload policy:TJPTLVTagPolicyRejectDuplicates maxNestedDepth:4 error:&error]; if (error) { TJPLOG_INFO(@"解析序列号:%u 的内容失败: %@", ntohl(_currentHeader.sequence), error.localizedDescription); + _state = TJPParseStateError; return nil; } @@ -108,9 +144,117 @@ - (TJPParsedPacket *)parseBodyData { return body; } +- (BOOL)validateChecksum:(uint32_t)expectedChecksum forData:(NSData *)data { + uint32_t calculatedChecksum = [TJPNetworkUtil crc32ForData:data]; + + if (calculatedChecksum != expectedChecksum) { + TJPLOG_ERROR(@"校验和不匹配: 期望 %u, 计算得到 %u", expectedChecksum, calculatedChecksum); + return NO; + } + + return YES; +} + - (void)reset { [_buffer setLength:0]; _currentHeader = (TJPFinalAdavancedHeader){0}; + _state = TJPParseStateHeader; +} + +#pragma mark - Private Method +- (BOOL)validateHeader:(TJPFinalAdavancedHeader)header { + //魔数校验 + if (ntohl(header.magic) != kProtocolMagic) { + TJPLOG_ERROR(@"魔数校验失败: 0x%X != 0x%X", ntohl(header.magic), kProtocolMagic); + return NO; + } + + //版本校验 + if (header.version_major != kProtocolVersionMajor || header.version_minor > kProtocolVersionMinor) { + TJPLOG_ERROR(@"协议版本不支持: %d.%d (当前支持: %d.%d)", header.version_major, header.version_minor, kProtocolVersionMajor, kProtocolVersionMinor); + return NO; + } + + //消息体长度校验 + uint32_t bodyLength = ntohl(header.bodyLength); + if (bodyLength > TJPMAX_BODY_SIZE) { + TJPLOG_ERROR(@"消息体长度超过限制: %u > %d", bodyLength, TJPMAX_BODY_SIZE); + return NO; + } + + //时间戳校验 + uint32_t currTime = (uint32_t)[[NSDate date] timeIntervalSince1970]; + uint32_t timestamp = ntohl(header.timestamp); // 确保字节序转换 + int32_t timeDiff = (int32_t)currTime - (int32_t)timestamp; + + if (abs(timeDiff) > TJPMAX_TIME_WINDOW) { + TJPLOG_ERROR(@"时间戳超出有效窗口: 当前时间 %u, 消息时间 %u, 差值 %d秒", currTime, timestamp, timeDiff); + return NO; + } + + //序列号防重放检查 + uint32_t sequence = ntohl(header.sequence); + NSString *uniqueID = [NSString stringWithFormat:@"%u-%u", sequence, timestamp]; + + @synchronized (_recentSequences) { + if ([_recentSequences containsObject:uniqueID]) { + TJPLOG_ERROR(@"检测到重放攻击: 序列号 %u, 时间戳 %u", sequence, timestamp); + return NO; + } + [_recentSequences addObject:uniqueID]; + } + + // 6. 校验加密类型和压缩类型 + if (![self isSupportedEncryptType:header.encrypt_type]) { + TJPLOG_ERROR(@"不支持的加密类型: %d", header.encrypt_type); + return NO; + } + + if (![self isSupportedCompressType:header.compress_type]) { + TJPLOG_ERROR(@"不支持的压缩类型: %d", header.compress_type); + return NO; + } + + return YES; +} + +- (BOOL)isSupportedEncryptType:(TJPEncryptType)type { + // 根据实际支持的加密类型进行验证 + switch (type) { + case TJPEncryptTypeNone: + case TJPEncryptTypeCRC32: + case TJPEncryptTypeAES256: + return YES; + default: + return NO; + } +} + +- (BOOL)isSupportedCompressType:(TJPCompressType)type { + // 根据实际支持的压缩类型进行验证 + switch (type) { + case TJPCompressTypeNone: + case TJPCompressTypeZlib: +// case TJPCompressTypeLZ4: + return YES; + default: + return NO; + } +} + +- (void)cleanupExpiredSequences { + NSDate *now = [NSDate date]; + NSTimeInterval elapsed = [now timeIntervalSinceDate:_lastCleanupTime]; + + // 每分钟清理一次 + if (elapsed > 60) { + @synchronized (_recentSequences) { + // 过期的序列号集合会随着时间增加而增长,定期清理 + TJPLOG_INFO(@"清理过期序列号缓存,当前数量: %lu", (unsigned long)_recentSequences.count); + [_recentSequences removeAllObjects]; + _lastCleanupTime = now; + } + } } @@ -125,3 +269,26 @@ - (TJPFinalAdavancedHeader)currentHeader { return _currentHeader; } @end + + + +//旧版本 +//- (NSData *)_buildPacketWithData:(NSData *)data seq:(uint32_t)seq { +// // 初始化协议头 +// TJPFinalAdavancedHeader header = {0}; +// header.magic = htonl(kProtocolMagic); +// header.version_major = kProtocolVersionMajor; +// header.version_minor = kProtocolVersionMinor; +// header.msgType = htons(TJPMessageTypeNormalData); // 普通消息类型 +// header.sequence = htonl(seq); +// header.bodyLength = htonl((uint32_t)data.length); +// +// // 计算数据体的CRC32 +// uint32_t checksum = [TJPNetworkUtil crc32ForData:data]; +// header.checksum = htonl(checksum); // 注意要转换为网络字节序 +// +// // 构建完整协议包 +// NSMutableData *packet = [NSMutableData dataWithBytes:&header length:sizeof(header)]; +// [packet appendData:data]; +// return packet; +//} diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Protocol/TJPMessage.h b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Protocol/TJPMessage.h index 3f17c58..f29eda4 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Protocol/TJPMessage.h +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Protocol/TJPMessage.h @@ -3,7 +3,7 @@ // iOS-Network-Stack-Dive // // Created by 唐佳鹏 on 2025/5/13. -// +// 消息接口 #import diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Utility/TJPCoreTypes.h b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Utility/TJPCoreTypes.h index a2544f2..428ab96 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Utility/TJPCoreTypes.h +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Utility/TJPCoreTypes.h @@ -24,6 +24,18 @@ typedef NS_ENUM(NSUInteger, TJPTLVParseError) { TJPTLVParseErrorInvalidNestedTag // 非法嵌套Tag(未使用保留Tag进行嵌套) }; +typedef NS_ENUM(uint8_t, TJPEncryptType) { + TJPEncryptTypeNone = 0, + TJPEncryptTypeCRC32, + TJPEncryptTypeAES256, +}; + +typedef NS_ENUM(uint8_t, TJPCompressType) { + TJPCompressTypeNone = 0, + TJPCompressTypeZlib, +}; + + // 内容类型标签枚举 typedef NS_ENUM(uint16_t, TJPContentType) { TJPContentTypeText = 0x1001, // 文本消息 @@ -117,13 +129,17 @@ typedef NS_ENUM(NSUInteger, TJPNetworkHealthStatus) { //基于V2的协议头扩充完善 #pragma pack(push, 1) typedef struct { - uint32_t magic; //魔数 0xDECAFBAD 4字节 - uint8_t version_major; //协议主版本 1字节 - uint8_t version_minor; //协议次版本 1字节 - uint16_t msgType; //消息类型 2字节 - uint32_t sequence; //序列号 4字节 - uint32_t bodyLength; //Body长度(网络字节序) 4字节 - uint32_t checksum; //CRC32 4字节 + uint32_t magic; //魔数 0xDECAFBAD 4字节 + uint8_t version_major; //协议主版本 1字节 + uint8_t version_minor; //协议次版本 1字节 + uint16_t msgType; //消息类型 2字节 + uint32_t sequence; //序列号 4字节 + uint32_t timestamp; //时间戳 (秒级,防重放攻击) 4字节 + TJPEncryptType encrypt_type; // 加密类型 1字节 + TJPCompressType compress_type; // 压缩类型 1字节 + uint16_t session_id; // 会话ID 2字节 + uint32_t bodyLength; //Body长度(网络字节序) 4字节 + uint32_t checksum; //CRC32 4字节 } TJPFinalAdavancedHeader; #pragma pack(pop) diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Utility/TJPNetworkUtil.h b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Utility/TJPNetworkUtil.h index f00a7c0..17fa2a6 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Utility/TJPNetworkUtil.h +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Utility/TJPNetworkUtil.h @@ -12,9 +12,6 @@ NS_ASSUME_NONNULL_BEGIN @interface TJPNetworkUtil : NSObject -/// 组装协议包 -+ (NSData *)buildPacketWithData:(NSData *)data type:(TJPMessageType)type sequence:(uint32_t)sequence; - /// crc32校验 + (uint32_t)crc32ForData:(NSData *)data; @@ -30,6 +27,8 @@ NS_ASSUME_NONNULL_BEGIN /// 获取当前设备IP地址 + (NSString *)deviceIPAddress; + (BOOL)isValidIPAddress:(NSString *)ip; + + @end NS_ASSUME_NONNULL_END diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Utility/TJPNetworkUtil.m b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Utility/TJPNetworkUtil.m index 3ff11b4..b4717dd 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Utility/TJPNetworkUtil.m +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Utility/TJPNetworkUtil.m @@ -10,38 +10,56 @@ #import #import -@implementation TJPNetworkUtil +#import "TJPNetworkDefine.h" +@implementation TJPNetworkUtil -+ (NSData *)buildPacketWithData:(NSData *)data type:(TJPMessageType)type sequence:(uint32_t)sequence { - - //初始化协议头 - TJPFinalAdavancedHeader header = {0}; - header.magic = htonl(kProtocolMagic); - header.version_major = kProtocolVersionMajor; - header.version_minor = kProtocolVersionMinor; - header.msgType = htons(type); - header.sequence = htonl(sequence); - header.bodyLength = htonl((uint32_t)data.length); - //crc32ForData需要转换为网络字节序 - header.checksum = htonl([self crc32ForData:data]); - - //构建完整包 - NSMutableData *packet = [NSMutableData dataWithBytes:&header length:sizeof(header)]; - [packet appendData:data]; - return packet; - -} +//+ (NSData *)buildPacketWithData:(NSData *)data type:(TJPMessageType)type sequence:(uint32_t)sequence { +// +// //初始化协议头 +// TJPFinalAdavancedHeader header = {0}; +// header.magic = htonl(kProtocolMagic); +// header.version_major = kProtocolVersionMajor; +// header.version_minor = kProtocolVersionMinor; +// header.msgType = htons(type); +// header.sequence = htonl(sequence); +// header.bodyLength = htonl((uint32_t)data.length); +// //crc32ForData需要转换为网络字节序 +// header.checksum = htonl([self crc32ForData:data]); +// +// //构建完整包 +// NSMutableData *packet = [NSMutableData dataWithBytes:&header length:sizeof(header)]; +// [packet appendData:data]; +// return packet; +// +//} + (uint32_t)crc32ForData:(NSData *)data { + if (!data || data.length == 0) { + return 0; // 对空数据的处理 + } + // 限制最大数据大小,防止DoS攻击 + if (data.length > TJPMAX_BODY_SIZE) { + TJPLOG_ERROR(@"数据大小超过限制: %lu", (unsigned long)data.length); + return 0; + } + uLong crc = crc32(0L, Z_NULL, 0); crc = crc32(crc, [data bytes], (uInt)[data length]); - NSLog(@"Calculated CRC32: %u", (uint32_t)crc); // 输出计算的 CRC32 值 +#ifdef DEBUG +NSLog(@"Calculated CRC32: %u", (uint32_t)crc); +#endif return (uint32_t)crc; } +// 未来升级成256加密 ++ (NSData *)hmacSHA256ForData:(NSData *)data withKey:(NSData *)key { + + return nil; +} + + (NSData *)compressData:(NSData *)data { if (data.length == 0) return data; diff --git "a/iOS-Network-Stack-Dive/Docs/Version/v1.2.0\347\211\210\346\234\254\345\206\205\345\256\271.md" "b/iOS-Network-Stack-Dive/Docs/Version/v1.2.0\347\211\210\346\234\254\345\206\205\345\256\271.md" new file mode 100644 index 0000000..cd48f20 --- /dev/null +++ "b/iOS-Network-Stack-Dive/Docs/Version/v1.2.0\347\211\210\346\234\254\345\206\205\345\256\271.md" @@ -0,0 +1,55 @@ +# 网络协议改造技术文档 + +## 一、协议头结构设计(TLV格式) + +### 1. C结构体定义 +```c +#pragma pack(push, 1) +typedef struct { + uint32_t magic; // 魔数 0xDECAFBAD (4字节) + uint8_t version_major; // 协议主版本 (1字节) + uint8_t version_minor; // 协议次版本 (1字节) + uint16_t msgType; // 消息类型 (2字节) + uint32_t sequence; // 序列号 (4字节) + uint32_t timestamp; // 秒级时间戳 (4字节防重放) + TJPEncryptType encrypt_type; // 加密类型枚举 (1字节) + TJPCompressType compress_type; // 压缩类型枚举 (1字节) + uint16_t session_id; // 会话ID (2字节) + uint32_t bodyLength; // Body长度 (网络字节序 4字节) + uint32_t checksum; // 安全校验码 (4字节) +} TJPFinalAdavancedHeader; +#pragma pack(pop) +``` +协议改造为TLV格式,并兼容Protobuf,协议头增加压缩类型,加密类型,时间戳 + +### 2. 兼容Protobuf设计方案 +``` +message ProtocolBody { + bytes protobuf_payload = 1; // Protobuf序列化数据 + map tlv_fields = 2; // TLV扩展字段 +} + +enum TJPEncryptType { + NONE = 0; + AES256_CBC = 1; + SM4_GCM = 2; +} + +enum TJPCompressType { + RAW = 0; + ZLIB = 1; + LZ4 = 2; +} +``` + + +crc32增加更健全的安全防护机制加盐。后续开放AES256加密接口 + + + +### 3. 新建TJPMessageBuilder类用于专门构建数据包,单一职责原则 + + + +### 4.消息重传使用更灵活的GCD定时器代替dispatch_after,添加重传计时器的生命周期管理。 + From 1ffe5ed1082cbee04a47f6bb560e24b1a86ffb85 Mon Sep 17 00:00:00 2001 From: tangjiapeng Date: Wed, 14 May 2025 17:50:28 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=AE=8C=E5=96=84=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcdebugger/Breakpoints_v2.xcbkptlist | 16 -- .../NetworkUtility/TJPErrorDefine.h | 89 ++++++++++ .../Tests/TJPMockFinalVersionTCPServer.m | 60 +++++-- .../Container/TJPConcreteSession.m | 168 +++++++++++++++++- .../V3_FinalProduct/Error/TJPErrorUtil.h | 44 +++++ .../V3_FinalProduct/Error/TJPErrorUtil.m | 60 +++++++ .../V3_FinalProduct/Parser/TJPMessageParser.m | 61 ++++++- .../V3_FinalProduct/Parser/TJPParsedPacket.m | 25 ++- .../Protocol/TJPSessionDelegate.h | 3 + .../ProtocolParseDesign.md | 112 ++++++++++++ 10 files changed, 587 insertions(+), 51 deletions(-) create mode 100644 iOS-Network-Stack-Dive/CoreNetworkStack/NetworkUtility/TJPErrorDefine.h create mode 100644 iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Error/TJPErrorUtil.h create mode 100644 iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Error/TJPErrorUtil.m diff --git a/iOS-Network-Stack-Dive.xcworkspace/xcuserdata/aarongtang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/iOS-Network-Stack-Dive.xcworkspace/xcuserdata/aarongtang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index f58a2b3..313a690 100644 --- a/iOS-Network-Stack-Dive.xcworkspace/xcuserdata/aarongtang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/iOS-Network-Stack-Dive.xcworkspace/xcuserdata/aarongtang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -24,21 +24,5 @@ - - - - diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/NetworkUtility/TJPErrorDefine.h b/iOS-Network-Stack-Dive/CoreNetworkStack/NetworkUtility/TJPErrorDefine.h new file mode 100644 index 0000000..9738ee8 --- /dev/null +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/NetworkUtility/TJPErrorDefine.h @@ -0,0 +1,89 @@ +// +// TJPErrorDefine.h +// iOS-Network-Stack-Dive +// +// Created by 唐佳鹏 on 2025/5/10. +// + + +#ifndef TJPErrorUtil_h +#define TJPErrorUtil_h + +#import + +/** + * 网络错误代码枚举 + * 错误域: "com.tjp.network.error" + */ +typedef NS_ENUM(NSInteger, TJPNetworkError) { + // 一般错误 (0-999) + TJPErrorNone = 0, // 无错误 + TJPErrorUnknown = 1, // 未知错误 + TJPErrorTimeout = 2, // 超时 + TJPErrorCancelled = 3, // 操作被取消 + + // 连接相关错误 (1000-1999) + TJPErrorConnectionFailed = 1000, // 连接失败 + TJPErrorConnectionTimeout = 1001, // 连接超时 + TJPErrorConnectionLost = 1002, // 连接丢失 + TJPErrorConnectionRefused = 1003, // 连接被拒绝 + TJPErrorNetworkUnavailable = 1004, // 网络不可用 + TJPErrorServerUnavailable = 1005, // 服务器不可用 + TJPErrorTLSHandshakeFailed = 1006, // TLS握手失败 + + // 消息传输错误 (2000-2999) + TJPErrorMessageSendFailed = 2000, // 消息发送失败 + TJPErrorMessageReceiveFailed = 2001, // 消息接收失败 + TJPErrorMessageTimeout = 2002, // 消息超时未收到响应 + TJPErrorMessageTooLarge = 2003, // 消息体过大 + TJPErrorMessageFormatInvalid = 2004, // 消息格式无效 + TJPErrorMessageACKMissing = 2005, // 未收到ACK确认 + TJPErrorMessageRetryExceeded = 2006, // 超过最大重试次数 + + // 协议解析错误 (3000-3999) + TJPErrorProtocolVersionMismatch = 3000, // 协议版本不匹配 + TJPErrorProtocolMagicInvalid = 3001, // 魔数无效 + TJPErrorProtocolChecksumMismatch = 3002, // 校验和不匹配 + TJPErrorProtocolHeaderInvalid = 3003, // 协议头无效 + TJPErrorProtocolPayloadLengthMismatch = 3004, // 负载长度不匹配 + TJPErrorProtocolUnsupportedEncryption = 3005, // 不支持的加密类型 + TJPErrorProtocolUnsupportedCompression= 3006, // 不支持的压缩类型 + TJPErrorProtocolTimestampInvalid = 3007, // 时间戳无效 + + // TLV解析错误 (4000-4999) + TJPErrorTLVParseError = 4000, // TLV解析错误 + TJPErrorTLVIncompleteTag = 4001, // 不完整的Tag + TJPErrorTLVIncompleteLength = 4002, // 不完整的Length + TJPErrorTLVIncompleteValue = 4003, // 不完整的Value + TJPErrorTLVDuplicateTag = 4004, // 重复的Tag + TJPErrorTLVNestedTooDeep = 4005, // 嵌套深度过大 + + // 安全相关错误 (5000-5999) + TJPErrorSecurityEncryptionFailed = 5000, // 加密失败 + TJPErrorSecurityDecryptionFailed = 5001, // 解密失败 + TJPErrorSecurityUnauthorized = 5002, // 未授权 + TJPErrorSecurityReplayAttackDetected = 5003, // 检测到重放攻击 + TJPErrorSecurityInvalidSignature = 5004, // 签名无效 + + // 会话相关错误 (6000-6999) + TJPErrorSessionExpired = 6000, // 会话过期 + TJPErrorSessionInvalid = 6001, // 会话无效 + TJPErrorSessionLimitExceeded = 6002, // 超出会话限制 + TJPErrorSessionHeartbeatTimeout = 6003, // 心跳超时 + TJPErrorSessionStateError = 6004, // 会话状态错误 + + // 业务逻辑错误 (7000-7999) + TJPErrorBusinessLogicFailed = 7000, // 业务逻辑失败 + + // 系统错误 (8000-8999) + TJPErrorSystemMemoryLow = 8000, // 系统内存不足 + TJPErrorSystemDiskFull = 8001, // 磁盘空间不足 + TJPErrorSystemIOFailure = 8002 // IO操作失败 +}; + +// 错误域常量 +FOUNDATION_EXPORT NSString * const TJPNetworkErrorDomain; + + + +#endif /* TJPErrorUtil_h */ diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/Tests/TJPMockFinalVersionTCPServer.m b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/Tests/TJPMockFinalVersionTCPServer.m index e2067a3..9de13bf 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/Tests/TJPMockFinalVersionTCPServer.m +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/Tests/TJPMockFinalVersionTCPServer.m @@ -83,6 +83,14 @@ - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)t uint32_t seq = ntohl(header.sequence); uint32_t bodyLength = ntohl(header.bodyLength); uint16_t msgType = ntohs(header.msgType); + TJPEncryptType encryptType = header.encrypt_type; + TJPCompressType compressType = header.compress_type; + uint16_t sessionId = ntohs(header.session_id); + uint32_t timestamp = ntohl(header.timestamp); + + NSLog(@"[MOCK SERVER] 接收到的消息: 类型=%hu, 序列号=%u, 时间戳=%u, 会话ID=%hu, 加密类型=%d, 压缩类型=%d", + msgType, seq, timestamp, sessionId, encryptType, compressType); + // 读取完整消息体 NSData *payload = [data subdataWithRange:NSMakeRange(kHeaderLength, bodyLength)]; @@ -106,7 +114,7 @@ - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)t if (self.didReceiveDataHandler) { self.didReceiveDataHandler(payload, seq); } - [self sendACKForSequence:seq toSocket:sock]; + [self sendACKForSequence:seq sessionId:sessionId toSocket:sock]; } break; @@ -116,7 +124,7 @@ - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)t if (self.didReceiveDataHandler) { self.didReceiveDataHandler(payload, seq); } - [self sendHeartbeatACKForSequence:seq toSocket:sock]; + [self sendHeartbeatACKForSequence:seq sessionId:sessionId toSocket:sock]; } break; @@ -134,46 +142,70 @@ - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err { } #pragma mark - Response Methods -- (void)sendACKForSequence:(uint32_t)seq toSocket:(GCDAsyncSocket *)socket { +- (void)sendACKForSequence:(uint32_t)seq sessionId:(uint16_t)sessionId toSocket:(GCDAsyncSocket *)socket { NSLog(@"[MOCK SERVER] 收到普通消息,序列号: %u", seq); + + // 使用与客户端相同的时间戳生成ACK响应 + uint32_t currentTime = (uint32_t)[[NSDate date] timeIntervalSince1970]; + TJPFinalAdavancedHeader header = {0}; header.magic = htonl(kProtocolMagic); - header.version_major = 1; - header.version_minor = 0; + header.version_major = kProtocolVersionMajor; + header.version_minor = kProtocolVersionMinor; header.msgType = htons(TJPMessageTypeACK); header.sequence = htonl(seq); - header.bodyLength = 0; // ACK没有数据体 + header.timestamp = htonl(currentTime); // 使用当前时间戳 + header.encrypt_type = TJPEncryptTypeNone; + header.compress_type = TJPCompressTypeNone; + header.session_id = htons(sessionId); // 保持与请求相同的会话ID + header.bodyLength = 0; // ACK没有数据体 // ACK包没有数据体,checksum设为0 header.checksum = 0; NSData *ackData = [NSData dataWithBytes:&header length:sizeof(header)]; - NSLog(@"[MOCK SERVER] 普通消息响应包字段:magic=0x%X, msgType=%hu, sequence=%u, checksum=%u", - ntohl(header.magic), ntohs(header.msgType), ntohl(header.sequence), ntohl(header.checksum)); - + NSLog(@"[MOCK SERVER] 普通消息响应包字段:magic=0x%X, msgType=%hu, sequence=%u, timestamp=%u, sessionId=%hu", + ntohl(header.magic), ntohs(header.msgType), ntohl(header.sequence), ntohl(header.timestamp), ntohs(header.session_id)); + [socket writeData:ackData withTimeout:-1 tag:0]; } -- (void)sendHeartbeatACKForSequence:(uint32_t)seq toSocket:(GCDAsyncSocket *)socket { +- (void)sendHeartbeatACKForSequence:(uint32_t)seq sessionId:(uint16_t)sessionId toSocket:(GCDAsyncSocket *)socket { NSLog(@"[MOCK SERVER] 收到心跳包,序列号: %u", seq); + + + // 使用当前时间戳 + uint32_t currentTime = (uint32_t)[[NSDate date] timeIntervalSince1970]; TJPFinalAdavancedHeader reply = {0}; reply.magic = htonl(kProtocolMagic); - reply.version_major = 1; - reply.version_minor = 0; + reply.version_major = kProtocolVersionMajor; + reply.version_minor = kProtocolVersionMinor; reply.msgType = htons(TJPMessageTypeACK); reply.sequence = htonl(seq); + reply.timestamp = htonl(currentTime); // 使用当前时间戳 + reply.encrypt_type = TJPEncryptTypeNone; + reply.compress_type = TJPCompressTypeNone; + reply.session_id = htons(sessionId); // 保持与请求相同的会话ID reply.bodyLength = 0; // 心跳ACK没有数据体,checksum设为0 reply.checksum = 0; NSData *ackData = [NSData dataWithBytes:&reply length:sizeof(reply)]; - NSLog(@"[MOCK SERVER] 心跳响应包字段:magic=0x%X, msgType=%hu, sequence=%u, checksum=%u", - ntohl(reply.magic), ntohs(reply.msgType), ntohl(reply.sequence), 0); + NSLog(@"[MOCK SERVER] 心跳响应包字段:magic=0x%X, msgType=%hu, sequence=%u, timestamp=%u, sessionId=%hu", + ntohl(reply.magic), ntohs(reply.msgType), ntohl(reply.sequence), ntohl(reply.timestamp), ntohs(reply.session_id)); [socket writeData:ackData withTimeout:-1 tag:0]; } + +//旧方法 已废弃目前单元测试在用 后续移除 +- (void)sendHeartbeatACKForSequence:(uint32_t)seq toSocket:(nonnull GCDAsyncSocket *)socket { +} + +- (void)sendACKForSequence:(uint32_t)seq toSocket:(nonnull GCDAsyncSocket *)socket { +} + @end diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Container/TJPConcreteSession.m b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Container/TJPConcreteSession.m index 8562fcc..da52a41 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Container/TJPConcreteSession.m +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Container/TJPConcreteSession.m @@ -9,7 +9,6 @@ #import #import - #import "TJPNetworkConfig.h" #import "TJPNetworkDefine.h" @@ -25,6 +24,7 @@ #import "TJPConnectStateMachine.h" #import "TJPNetworkCondition.h" #import "TJPMetricsConsoleReporter.h" +#import "TJPErrorUtil.h" @@ -39,6 +39,9 @@ @interface TJPConcreteSession () *retransmissionTimers; + /// 动态心跳 @property (nonatomic, strong) TJPDynamicHeartbeat *heartbeatManager; /// 序列号管理 @@ -57,6 +60,8 @@ @implementation TJPConcreteSession - (void)dealloc { TJPLogDealloc(); + //清理定时器 + [self cancelAllRetransmissionTimers]; } #pragma mark - Lifecycle @@ -66,6 +71,8 @@ - (instancetype)initWithConfiguration:(TJPNetworkConfig *)config { _autoReconnectEnabled = YES; _sessionId = [[NSUUID UUID] UUIDString]; + _retransmissionTimers = [NSMutableDictionary dictionary]; + // 创建专用队列(串行,中等优先级) _socketQueue = dispatch_queue_create("com.concreteSession.tjp.socketQueue", DISPATCH_QUEUE_SERIAL); dispatch_set_target_queue(_socketQueue, dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)); @@ -526,9 +533,30 @@ - (void)handleDisconnectError:(NSError *)err { - (void)cleanupAfterDisconnect { // 清理资源 [self.heartbeatManager stopMonitoring]; + + // 清理所有重传计时器 + [self cancelAllRetransmissionTimers]; + + // 清理待确认消息队列 [self.pendingMessages removeAllObjects]; } +- (void)cancelAllRetransmissionTimers { + dispatch_async(self->_socketQueue, ^{ + // 取消所有计时器 + for (NSString *key in [self.retransmissionTimers allKeys]) { + dispatch_source_t timer = self.retransmissionTimers[key]; + if (timer) { + dispatch_source_cancel(timer); + } + } + + // 清空计时器字典 + [self.retransmissionTimers removeAllObjects]; + + TJPLOG_INFO(@"已清理所有重传计时器"); + }); +} - (void)handleReconnectionAfterDisconnect { // 检查网络状态,只有在网络可达时才尝试重连 @@ -609,20 +637,136 @@ - (void)handleHeartbeatTimeout:(NSNotification *)notification { #pragma mark - Private Method //超时重传 - (void)scheduleRetransmissionForSequence:(uint32_t)sequence { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(dispatch_time(DISPATCH_TIME_NOW, kDefaultRetryInterval) * NSEC_PER_SEC)), self->_socketQueue, ^{ - if (self.pendingMessages[@(sequence)]) { - TJPLOG_INFO(@"消息 %u 超时未确认, 尝试重传", sequence); - [self resendPacket:self.pendingMessages[@(sequence)]]; - } + // 取消之前可能存在的重传计时器 + NSString *timerKey = [NSString stringWithFormat:@"retry_%u", sequence]; + dispatch_source_t existingTimer = self.retransmissionTimers[timerKey]; + if (existingTimer) { + dispatch_source_cancel(existingTimer); + [self.retransmissionTimers removeObjectForKey:timerKey]; + } + + //获取消息上下文 + TJPMessageContext *context = self.pendingMessages[@(sequence)]; + if (!context) { + TJPLOG_ERROR(@"无法为序列号 %u 安排重传! 原因:消息上下文不存在", sequence); + return; + } + + //如果已经达到最大重试次数,不再安排重传 + if (context.retryCount >= context.maxRetryCount) { + TJPLOG_WARN(@"消息 %u 已达到最大重试次数 %ld,不再重试", sequence, (long)context.maxRetryCount); + return; + } + + //创建GCD定时器 + __weak typeof(self) weakSelf = self; + dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self->_socketQueue); + + //设置定时器间隔 (默认3秒一次) + NSTimeInterval retryInterval = context.retryTimeout > 0 ? context.retryTimeout : kDefaultRetryInterval; + uint64_t intervalInNanoseconds = (uint64_t)(retryInterval * NSEC_PER_SEC); + + + dispatch_source_set_timer(timer, + dispatch_time(DISPATCH_TIME_NOW, intervalInNanoseconds), + DISPATCH_TIME_FOREVER, // 不重复 + (1ull * NSEC_PER_SEC) / 10); // 100ms的精度 + + dispatch_source_set_event_handler(timer, ^{ + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) return; + + [strongSelf handleRetransmissionForSequence:sequence]; + }); + + // 设置定时器取消处理函数 + dispatch_source_set_cancel_handler(timer, ^{ + TJPLOG_INFO(@"取消消息 %u 的重传计时器", sequence); }); + + // 保存定时器 + self.retransmissionTimers[timerKey] = timer; + + // 启动定时器 + dispatch_resume(timer); + + TJPLOG_INFO(@"为消息 %u 安排重传,间隔 %.1f 秒,当前重试次数 %ld", sequence, retryInterval, (long)context.retryCount); } -//重传消息 +// 重传处理方法 +- (void)handleRetransmissionForSequence:(uint32_t)sequence { + // 获取消息上下文 + TJPMessageContext *context = self.pendingMessages[@(sequence)]; + + // 清理计时器 + NSString *timerKey = [NSString stringWithFormat:@"retry_%u", sequence]; + dispatch_source_t timer = self.retransmissionTimers[timerKey]; + if (timer) { + dispatch_source_cancel(timer); + [self.retransmissionTimers removeObjectForKey:timerKey]; + } + + // 如果消息已确认,不需要重传 + if (!context) { + TJPLOG_INFO(@"消息 %u 已确认,不需要重传", sequence); + return; + } + + // 检查连接状态 + if (![self.stateMachine.currentState isEqualToString:TJPConnectStateConnected]) { + TJPLOG_WARN(@"当前连接状态为 %@,无法重传消息 %u", self.stateMachine.currentState, sequence); + return; + } + + // 检查重试次数是否已达上限 + if (context.retryCount >= context.maxRetryCount) { + TJPLOG_ERROR(@"消息 %u 重传失败,已达最大重试次数 %ld", sequence, (long)context.maxRetryCount); + + // 移除待确认消息 + [self.pendingMessages removeObjectForKey:@(sequence)]; + + // 通知上层应用消息发送失败 + if (self.delegate && [self.delegate respondsToSelector:@selector(session:didFailToSendMessageWithSequence:error:)]) { + NSError *error = [TJPErrorUtil errorWithCode:TJPErrorMessageRetryExceeded + description:[NSString stringWithFormat:@"消息 %u 发送失败,超过最大重试次数", sequence] + userInfo:@{@"sequence": @(sequence), @"retryCount": @(context.retryCount)}]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [self.delegate session:self didFailToSendMessageWithSequence:sequence error:error]; + }); + } + return; + } + + // 执行重传 + TJPLOG_INFO(@"重传消息 %u,第 %ld 次尝试", sequence, (long)context.retryCount + 1); + NSData *packet = [context buildRetryPacket]; + [self.socket writeData:packet withTimeout:-1 tag:sequence]; + + // 安排下一次重传 + [self scheduleRetransmissionForSequence:sequence]; +} + +// 替换现有的resendPacket方法 - (void)resendPacket:(TJPMessageContext *)context { + if (!context) return; + + // 构建重传包 NSData *packet = [context buildRetryPacket]; - [self.socket writeData:packet withTimeout:-1 tag:context.sequence]; + + // 判断当前连接状态 + if ([self.stateMachine.currentState isEqualToString:TJPConnectStateConnected]) { + // 直接发送 + [self.socket writeData:packet withTimeout:-1 tag:context.sequence]; + + // 安排下一次重传 + [self scheduleRetransmissionForSequence:context.sequence]; + } else { + TJPLOG_WARN(@"当前连接状态为 %@,无法发送消息 %u", self.stateMachine.currentState, context.sequence); + } } + //处理消息 - (void)processReceivedPacket:(TJPParsedPacket *)packet { // TJPLOG_INFO(@"接收到数据包 消息类型为 %hu", packet.messageType); @@ -672,6 +816,14 @@ - (void)handleACKForSequence:(uint32_t)sequence { // 将此ACK标记为已处理 [processedACKs addObject:ackId]; + + // 取消对应的重传计时器 + NSString *timerKey = [NSString stringWithFormat:@"retry_%u", sequence]; + dispatch_source_t timer = self.retransmissionTimers[timerKey]; + if (timer) { + dispatch_source_cancel(timer); + [self.retransmissionTimers removeObjectForKey:timerKey]; + } // 首先检查是否是普通消息的ACK if ([self.pendingMessages objectForKey:@(sequence)]) { diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Error/TJPErrorUtil.h b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Error/TJPErrorUtil.h new file mode 100644 index 0000000..3b72b8d --- /dev/null +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Error/TJPErrorUtil.h @@ -0,0 +1,44 @@ +// +// TJPErrorUtil.h +// iOS-Network-Stack-Dive +// +// Created by 唐佳鹏 on 2025/5/14. +// + +#import +#import "TJPErrorDefine.h" +#import "TJPCoreTypes.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TJPErrorUtil : NSObject + +/** + * 创建标准的网络错误对象 + * @param code 错误代码 + * @param description 错误描述 + * @param userInfo 附加信息 (可选,如果为nil则创建空字典) + * @return NSError实例 + */ ++ (NSError *)errorWithCode:(TJPNetworkError)code description:(NSString *)description userInfo:(NSDictionary *)userInfo; + +/** + * 创建带有失败原因的网络错误对象 + * @param code 错误代码 + * @param description 错误描述 + * @param failureReason 失败原因 + * @return NSError实例 + */ ++ (NSError *)errorWithCode:(TJPNetworkError)code description:(NSString *)description failureReason:(NSString *)failureReason; + +/** + * 将TLV解析错误代码转换为网络错误代码 + * @param tlvError TLV解析错误代码 + * @return 对应的网络错误代码 + */ ++ (TJPNetworkError)networkErrorFromTLVError:(TJPTLVParseError)tlvError; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Error/TJPErrorUtil.m b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Error/TJPErrorUtil.m new file mode 100644 index 0000000..642a3f6 --- /dev/null +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Error/TJPErrorUtil.m @@ -0,0 +1,60 @@ +// +// TJPErrorUtil.m +// iOS-Network-Stack-Dive +// +// Created by 唐佳鹏 on 2025/5/14. +// + +#import "TJPErrorUtil.h" + +NSString * const TJPNetworkErrorDomain = @"com.tjp.network.error"; + + +@implementation TJPErrorUtil ++ (NSError *)errorWithCode:(TJPNetworkError)code description:(NSString *)description userInfo:(NSDictionary *)userInfo { + NSMutableDictionary *errorInfo = userInfo ? [NSMutableDictionary dictionaryWithDictionary:userInfo] : [NSMutableDictionary dictionary]; + + // 确保有错误描述 + if (description) { + errorInfo[NSLocalizedDescriptionKey] = description; + } + + return [NSError errorWithDomain:TJPNetworkErrorDomain code:code userInfo:errorInfo]; +} + ++ (NSError *)errorWithCode:(TJPNetworkError)code description:(NSString *)description failureReason:(NSString *)failureReason { + NSMutableDictionary *errorInfo = [NSMutableDictionary dictionary]; + + if (description) { + errorInfo[NSLocalizedDescriptionKey] = description; + } + + if (failureReason) { + errorInfo[NSLocalizedFailureReasonErrorKey] = failureReason; + } + + return [NSError errorWithDomain:TJPNetworkErrorDomain code:code userInfo:errorInfo]; +} + ++ (TJPNetworkError)networkErrorFromTLVError:(TJPTLVParseError)tlvError { + switch (tlvError) { + case TJPTLVParseErrorNone: + return TJPErrorNone; + case TJPTLVParseErrorIncompleteTag: + return TJPErrorTLVIncompleteTag; + case TJPTLVParseErrorIncompleteLength: + return TJPErrorTLVIncompleteLength; + case TJPTLVParseErrorIncompleteValue: + return TJPErrorTLVIncompleteValue; + case TJPTLVParseErrorNestedTooDeep: + return TJPErrorTLVNestedTooDeep; + case TJPTLVParseErrorDuplicateTag: + return TJPErrorTLVDuplicateTag; + case TJPTLVParseErrorInvalidNestedTag: + return TJPErrorTLVParseError; + default: + return TJPErrorUnknown; + } +} + +@end diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Parser/TJPMessageParser.m b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Parser/TJPMessageParser.m index fdeea7f..97132ce 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Parser/TJPMessageParser.m +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Parser/TJPMessageParser.m @@ -10,6 +10,7 @@ #import "TJPParsedPacket.h" #import "TJPCoreTypes.h" #import "TJPNetworkUtil.h" +#import "TJPErrorUtil.h" @interface TJPMessageParser () { @@ -97,7 +98,9 @@ - (BOOL)parseHeaderData { [_buffer getBytes:¤tHeader length:sizeof(TJPFinalAdavancedHeader)]; // 安全验证 - if (![self validateHeader:currentHeader]) { + NSError *validationError = nil; + if (![self validateHeader:currentHeader error:&validationError]) { + TJPLOG_ERROR(@"头部验证失败: %@", validationError.localizedDescription); _state = TJPParseStateError; return NO; } @@ -162,22 +165,46 @@ - (void)reset { } #pragma mark - Private Method -- (BOOL)validateHeader:(TJPFinalAdavancedHeader)header { +- (BOOL)validateHeader:(TJPFinalAdavancedHeader)header error:(NSError **)error { //魔数校验 if (ntohl(header.magic) != kProtocolMagic) { + if (error) { + *error = [TJPErrorUtil errorWithCode:TJPErrorProtocolMagicInvalid + description:@"无效的魔数" + userInfo:@{@"receivedMagic": @(ntohl(header.magic)), + @"expectedMagic": @(kProtocolMagic)}]; + } TJPLOG_ERROR(@"魔数校验失败: 0x%X != 0x%X", ntohl(header.magic), kProtocolMagic); return NO; } //版本校验 if (header.version_major != kProtocolVersionMajor || header.version_minor > kProtocolVersionMinor) { - TJPLOG_ERROR(@"协议版本不支持: %d.%d (当前支持: %d.%d)", header.version_major, header.version_minor, kProtocolVersionMajor, kProtocolVersionMinor); + if (error) { + *error = [TJPErrorUtil errorWithCode:TJPErrorProtocolVersionMismatch + description:@"不支持的协议版本" + userInfo:@{@"receivedVersion": [NSString stringWithFormat:@"%d.%d", + header.version_major, + header.version_minor], + @"supportedVersion": [NSString stringWithFormat:@"%d.%d", + kProtocolVersionMajor, + kProtocolVersionMinor]}]; + } + TJPLOG_ERROR(@"协议版本不支持: %d.%d (当前支持: %d.%d)", + header.version_major, header.version_minor, + kProtocolVersionMajor, kProtocolVersionMinor); return NO; } //消息体长度校验 uint32_t bodyLength = ntohl(header.bodyLength); if (bodyLength > TJPMAX_BODY_SIZE) { + if (error) { + *error = [TJPErrorUtil errorWithCode:TJPErrorMessageTooLarge + description:@"消息体长度超过限制" + userInfo:@{@"bodyLength": @(bodyLength), + @"maxSize": @(TJPMAX_BODY_SIZE)}]; + } TJPLOG_ERROR(@"消息体长度超过限制: %u > %d", bodyLength, TJPMAX_BODY_SIZE); return NO; } @@ -188,7 +215,15 @@ - (BOOL)validateHeader:(TJPFinalAdavancedHeader)header { int32_t timeDiff = (int32_t)currTime - (int32_t)timestamp; if (abs(timeDiff) > TJPMAX_TIME_WINDOW) { - TJPLOG_ERROR(@"时间戳超出有效窗口: 当前时间 %u, 消息时间 %u, 差值 %d秒", currTime, timestamp, timeDiff); + if (error) { + *error = [TJPErrorUtil errorWithCode:TJPErrorProtocolTimestampInvalid + description:@"时间戳超出有效窗口" + userInfo:@{@"currentTime": @(currTime), + @"messageTime": @(timestamp), + @"difference": @(timeDiff)}]; + } + TJPLOG_ERROR(@"时间戳超出有效窗口: 当前时间 %u, 消息时间 %u, 差值 %d秒", + currTime, timestamp, timeDiff); return NO; } @@ -198,19 +233,35 @@ - (BOOL)validateHeader:(TJPFinalAdavancedHeader)header { @synchronized (_recentSequences) { if ([_recentSequences containsObject:uniqueID]) { + if (error) { + *error = [TJPErrorUtil errorWithCode:TJPErrorSecurityReplayAttackDetected + description:@"检测到重放攻击" + userInfo:@{@"sequence": @(sequence), + @"timestamp": @(timestamp)}]; + } TJPLOG_ERROR(@"检测到重放攻击: 序列号 %u, 时间戳 %u", sequence, timestamp); return NO; } [_recentSequences addObject:uniqueID]; } - // 6. 校验加密类型和压缩类型 + // 校验加密类型和压缩类型 if (![self isSupportedEncryptType:header.encrypt_type]) { + if (error) { + *error = [TJPErrorUtil errorWithCode:TJPErrorProtocolUnsupportedEncryption + description:@"不支持的加密类型" + userInfo:@{@"encryptType": @(header.encrypt_type)}]; + } TJPLOG_ERROR(@"不支持的加密类型: %d", header.encrypt_type); return NO; } if (![self isSupportedCompressType:header.compress_type]) { + if (error) { + *error = [TJPErrorUtil errorWithCode:TJPErrorProtocolUnsupportedCompression + description:@"不支持的压缩类型" + userInfo:@{@"compressType": @(header.compress_type)}]; + } TJPLOG_ERROR(@"不支持的压缩类型: %d", header.compress_type); return NO; } diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Parser/TJPParsedPacket.m b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Parser/TJPParsedPacket.m index 23678e4..680413f 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Parser/TJPParsedPacket.m +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Parser/TJPParsedPacket.m @@ -7,6 +7,7 @@ #import "TJPParsedPacket.h" #import "TJPNetworkDefine.h" +#import "TJPErrorUtil.h" static const uint16_t kTLVReservedNestedTag = 0xFFFF; @@ -44,8 +45,9 @@ + (instancetype)packetWithHeader:(TJPFinalAdavancedHeader)header { + (NSDictionary *)parseTLVFromData:(NSData *)data policy:(TJPTLVTagPolicy)policy maxNestedDepth:(NSUInteger)maxDepth currentDepth:(NSUInteger)currentDepth error:(NSError **)error { if (currentDepth > maxDepth) { if (error) { - NSString *msg = [NSString stringWithFormat:@"嵌套深度超过限制:%lu", maxDepth]; - *error = [NSError errorWithDomain:@"TLVError" code:TJPTLVParseErrorNestedTooDeep userInfo:@{NSLocalizedDescriptionKey: msg}]; + *error = [TJPErrorUtil errorWithCode:TJPErrorTLVNestedTooDeep + description:[NSString stringWithFormat:@"嵌套深度超过限制:%lu", maxDepth] + userInfo:@{@"maxDepth": @(maxDepth), @"currentDepth": @(currentDepth)}]; } return nil; } @@ -60,7 +62,9 @@ + (NSDictionary *)parseTLVFromData:(NSData *)data policy:(TJPTLVTagPolicy)policy if (offset + 2 > length) { TJPLOG_ERROR(@"TLV解析失败:Tag不完整 (offset=%lu, total=%lu)", offset, length); if (error) { - *error = [NSError errorWithDomain:@"TLVError" code:TJPTLVParseErrorIncompleteTag userInfo:nil]; + *error = [TJPErrorUtil errorWithCode:TJPErrorTLVIncompleteTag + description:@"TLV解析失败:Tag不完整" + userInfo:@{@"offset": @(offset), @"length": @(length)}]; } return nil; } @@ -72,7 +76,9 @@ + (NSDictionary *)parseTLVFromData:(NSData *)data policy:(TJPTLVTagPolicy)policy if (offset + 4 > length) { TJPLOG_ERROR(@"TLV解析失败:Length不完整 (offset=%lu)", offset); if (error) { - *error = [NSError errorWithDomain:@"TLVError" code:TJPTLVParseErrorIncompleteTag userInfo:nil]; + *error = [TJPErrorUtil errorWithCode:TJPErrorTLVIncompleteLength + description:@"TLV解析失败:Length不完整" + userInfo:@{@"offset": @(offset), @"length": @(length)}]; } return nil; } @@ -84,7 +90,9 @@ + (NSDictionary *)parseTLVFromData:(NSData *)data policy:(TJPTLVTagPolicy)policy if (offset + valueLen > length) { TJPLOG_ERROR(@"TLV解析失败:Value长度越界 (声明长度=%u, 剩余长度=%lu)", valueLen, (length - offset)); if (error) { - *error = [NSError errorWithDomain:@"TLVError" code:TJPTLVParseErrorIncompleteValue userInfo:nil]; + *error = [TJPErrorUtil errorWithCode:TJPErrorTLVIncompleteValue + description:@"TLV解析失败:Value长度越界" + userInfo:@{@"valueLen": @(valueLen)}]; } return nil; } @@ -94,10 +102,11 @@ + (NSDictionary *)parseTLVFromData:(NSData *)data policy:(TJPTLVTagPolicy)policy //查重Tag if (policy == TJPTLVTagPolicyRejectDuplicates && tlvDict[@(tag)]) { - NSString *msg = [NSString stringWithFormat:@"重复Tag:0x%04X", tag]; - TJPLOG_ERROR(@"%@", msg); + TJPLOG_ERROR(@"%@", [NSString stringWithFormat:@"重复Tag:0x%04X", tag]); if (error) { - *error = [NSError errorWithDomain:@"TLVError" code:TJPTLVParseErrorDuplicateTag userInfo:@{NSLocalizedDescriptionKey: msg}]; + *error = [TJPErrorUtil errorWithCode:TJPErrorTLVDuplicateTag + description:@"TLV解析失败:重复Tag" + userInfo:@{@"tag": @(tag)}]; } return nil; } diff --git a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Protocol/TJPSessionDelegate.h b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Protocol/TJPSessionDelegate.h index 5b27898..3773375 100644 --- a/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Protocol/TJPSessionDelegate.h +++ b/iOS-Network-Stack-Dive/CoreNetworkStack/TransportLayer/V3_FinalProduct/Protocol/TJPSessionDelegate.h @@ -43,6 +43,9 @@ NS_ASSUME_NONNULL_BEGIN // 接收自定义内容 - (void)session:(id)session didReceiveCustomData:(NSData *)data withType:(uint16_t)customType; +// 发送消息失败 +- (void)session:(id)session didFailToSendMessageWithSequence:(uint32_t)sequence error:(NSError *)error; + // === 原始数据回调(高级用户) === // 接收原始数据 - (void)session:(id)session didReceiveRawData:(NSData *)data; diff --git a/iOS-Network-Stack-Dive/Docs/CoreNetworkStackDoc/ProtocolParseDesign.md b/iOS-Network-Stack-Dive/Docs/CoreNetworkStackDoc/ProtocolParseDesign.md index 2fbf9c4..85d9a03 100644 --- a/iOS-Network-Stack-Dive/Docs/CoreNetworkStackDoc/ProtocolParseDesign.md +++ b/iOS-Network-Stack-Dive/Docs/CoreNetworkStackDoc/ProtocolParseDesign.md @@ -176,6 +176,118 @@ Socket 数据 → TJPParsedPacket → 结构化字典 → 业务对象映射 - **解析操作在独立的串行队列 (`dispatch_queue_t`) 中执行**: - 避免多个线程同时修改解析状态,确保线程安全。 - 解析过程不会影响其他会话,提高并发性能。 + +### Log完整流程分析 + + 1. 消息发送准备: + + ``` + [INFO] [TJPConcreteSession.m:242 -[TJPConcreteSession sendData:]_block_invoke] session 准备构造数据包 + ``` + + - 客户端开始准备构造数据包,准备发送"Hello World!!!!!111112223333"消息 + + 2. 计算校验和: + + ``` + Calculated CRC32: 1789856453 + ``` + + - 成功计算了消息体的CRC32校验值: 1789856453 + + 3. 安排重传计时器: + + ``` + [INFO] [TJPConcreteSession.m:693 -[TJPConcreteSession scheduleRetransmissionForSequence:]] 为消息 8 安排重传,间隔 3.0 秒,当前重试次数 0 + ``` + + - 为序列号为8的消息安排了重传计时器,超时时间3秒,当前是第一次发送(重试次数0) + + 4. 发送消息: + + ``` + [INFO] [TJPConcreteSession.m:281 -[TJPConcreteSession sendData:]_block_invoke] session 消息即将发出, 序列号: 8, 大小: 62字节 + ``` + + - 消息即将发送,序列号8,总大小62字节(包括协议头和消息体) + + 5. 服务端接收数据: + + ``` + [MOCK SERVER] 接收到客户端发送的数据 + [MOCK SERVER] 接收到的消息: 类型=0, 序列号=8, 时间戳=1747215938, 会话ID=4850, 加密类型=1, 压缩类型=1 + ``` + + - 服务端成功接收到数据 + - 正确解析出消息类型(0=普通数据)、序列号(8)、时间戳、会话ID和加密/压缩类型 + + 6. 服务端验证校验和: + + ``` + Calculated CRC32: 1789856453 + [MOCK SERVER] 接收到的校验和: 1789856453, 计算的校验和: 1789856453 + ``` + + - 服务端计算的CRC32校验值与接收到的校验值完全匹配,验证通过 + + 7. 服务端处理消息并发送ACK: + + ``` + [MOCK SERVER] 收到普通消息,序列号: 8 + [MOCK SERVER] 普通消息响应包字段:magic=0xDECAFBAD, msgType=2, sequence=8, timestamp=1747215938, sessionId=4850 + ``` + + - 服务端识别为普通消息,序列号8 + - 发送ACK响应,消息类型为2(ACK),保持相同的序列号、时间戳和会话ID + + 8. 客户端接收ACK: + + ``` + [INFO] [TJPConcreteSession.m:456 -[TJPConcreteSession socket:didReadData:withTag:]_block_invoke] 读取到数据 缓冲区准备添加数据 + [INFO] [TJPConcreteSession.m:462 -[TJPConcreteSession socket:didReadData:withTag:]_block_invoke] 开始解析数据 + ``` + + - 客户端接收到服务端的响应数据并开始解析 + + 9. 客户端解析ACK: + + ``` + [INFO] [TJPMessageParser.m:108 -[TJPMessageParser parseHeaderData]] 解析数据头部成功...魔数校验成功! + [INFO] [TJPMessageParser.m:113 -[TJPMessageParser parseHeaderData]] 解析序列号:8 的头部成功 + [INFO] [TJPMessageParser.m:144 -[TJPMessageParser parseBodyData]] 解析序列号:8 的内容成功 + ``` + + - 客户端成功解析ACK数据包的头部和内容 + - 验证魔数成功,确认序列号为8 + + 10. 客户端处理ACK: + + ``` + [INFO] [TJPConcreteSession.m:800 -[TJPConcreteSession handleACKForSequence:]] 接收到 ACK 数据包并进行处理 + [INFO] [TJPConcreteSession.m:831 -[TJPConcreteSession handleACKForSequence:]] 处理普通消息ACK,序列号: 8 + ``` + + - 客户端识别并处理ACK数据包 + - 确认这是针对序列号8的普通消息的ACK + + 11. 取消重传计时器: + + ``` + [INFO] [TJPConcreteSession.m:684 -[TJPConcreteSession scheduleRetransmissionForSequence:]_block_invoke] 取消消息 8 的重传计时器 + ``` + + - 由于已收到序列号8的ACK确认,取消相应的重传计时器 + + ### 流程评估 + + 整个流程完全符合预期,展示了一个健壮的消息发送-确认机制: + + 1. ✅ **消息构建正确**:包含了所有必要字段,校验和计算无误 + 2. ✅ **重传机制运作良好**:安排了重传计时器,并在接收到ACK后正确取消 + 3. ✅ **服务端处理正确**:验证校验和,发送正确格式的ACK响应 + 4. ✅ **客户端处理ACK正确**:识别并处理ACK,取消重传 + 5. ✅ **所有时间戳、会话ID、序列号匹配**:确保消息追踪的一致性 + 6. ✅ **日志信息完整详细**:包含了关键步骤的所有必要信息,便于调试和监控 ### **总结** - 最终会使用**会话隔离+中心管理**方案。支持统一重连策略,更细粒度的并发控制,减少内存占用;**标志位控制解析阶段,确保数据完整性,解析逻辑独立执行,提升系统吞吐量与响应速度。**