利用RHSocketKit构建自定义协议通信
来源:互联网 发布:淘宝修改评价链接 编辑:程序博客网 时间:2024/06/07 22:26
利用RHSocketKit构建自定义协议通信
在网络传输中,tcp/ip协议只是网络的基础,分别属于传输层和网络层。我们各种应用之间的通信需要依赖各种各样的协议,是建立在tcp/ip之上的,比如典型的http协议。
在开发过程中,一般我们制定的协议需要两个部分,header和content。
1-header:协议头部,放置一些meta信息。
2-content:应用之间交互的信息主体。
例如:分隔符delimiter-2个字节|数据包类型type-2个字节|长度length(不含包头长度)-4个字节|数据信息主体content-length个子节
其中delimiter,type,length就是header信息,content就是交互的主体。
我们来利用RHSocketKit构造一个数据包对象。
#import "RHSocketPacketContext.h"@interface RHSocketCustomRequest : RHSocketPacketRequest//这里,我们把content数据放在夫类的object变量中。//length可以通过object数据直接获得,不再申明额外的变量。@property (nonatomic, assign) int16_t fenGeFu;@property (nonatomic, assign) int16_t dataType;@end
数据包已经创建好了,那么我们在RHSocketKit中如何去对这种自定义的协议编码(Encode)呢?
定义编码协议RHSocketEncoderProtocol和解码协议RHSocketDecoderProtocol。
RHSocketCustom0330Encoder:编码器,将发送包的数据从一种格式转换成二进制数组。
RHSocketCustom0330Decoder:解码器,将接收包的二进制数组转换成可读的变量数据。
RHSocketCustom0330Encoder文件
#import <Foundation/Foundation.h>#import "RHSocketCodecProtocol.h"@interface RHSocketCustom0330Encoder : NSObject <RHSocketEncoderProtocol>@end#import "RHSocketCustom0330Encoder.h"#import "RHSocketException.h"#import "RHSocketUtils.h"#import "RHSocketCustomRequest.h"@implementation RHSocketCustom0330Encoder- (void)encode:(id<RHUpstreamPacket>)upstreamPacket output:(id<RHSocketEncoderOutputProtocol>)output{ id object = [upstreamPacket object]; if (![object isKindOfClass:[NSData class]]) { [RHSocketException raiseWithReason:@"[Encode] object should be NSData ..."]; return; } NSData *data = object; if (data.length == 0) { return; }// RHSocketCustomRequest *req = (RHSocketCustomRequest *)upstreamPacket; NSUInteger dataLen = data.length; //<strong><span style="color:#ff0000;">注意这里,按照协议的描述,顺序把数值转成二进制填充到对应的位置,然后output输出</span></strong> NSMutableData *sendData = [[NSMutableData alloc] init]; //分隔符 2个字节 [sendData appendData:[RHSocketUtils bytesFromUInt16:req.fenGeFu]]; //数据包类型 2个字节 [sendData appendData:[RHSocketUtils bytesFromUInt16:req.dataType]]; //长度(不含包头长度) 4个字节 [sendData appendData:[RHSocketUtils bytesFromUInt32:(uint32_t)dataLen]]; //数据包 dataLen个字节 [sendData appendData:data]; NSTimeInterval timeout = [upstreamPacket timeout]; RHSocketLog(@"timeout: %f, sendData: %@", timeout, sendData); [output didEncode:sendData timeout:timeout];}@end
RHSocketCustom0330Decoder文件
#import <Foundation/Foundation.h>#import "RHSocketCodecProtocol.h"@interface RHSocketCustom0330Decoder : NSObject <RHSocketDecoderProtocol>@end#import "RHSocketCustom0330Decoder.h"#import "RHSocketException.h"#import "RHSocketUtils.h"#import "RHSocketCustomResponse.h"@interface RHSocketCustom0330Decoder (){ NSUInteger _countOfLengthByte;}@end@implementation RHSocketCustom0330Decoder- (instancetype)init{ if (self = [super init]) { _countOfLengthByte = 8; } return self;}- (NSInteger)decode:(id<RHDownstreamPacket>)downstreamPacket output:(id<RHSocketDecoderOutputProtocol>)output{ id object = [downstreamPacket object]; if (![object isKindOfClass:[NSData class]]) { [RHSocketException raiseWithReason:@"[Decode] object should be NSData ..."]; return -1; } NSData *downstreamData = object; NSUInteger headIndex = 0; while (downstreamData && downstreamData.length - headIndex >= _countOfLengthByte) { NSData *headData = [downstreamData subdataWithRange:NSMakeRange(headIndex, _countOfLengthByte)]; //去第4个字节开始的4个字节长度 NSData *lenData = [headData subdataWithRange:NSMakeRange(4, 4)]; //长度字节数据,可能存在高低位互换,通过数值转换工具处理 NSUInteger frameLen = [RHSocketUtils uint32FromBytes:lenData]; //剩余数据,不是完整的数据包,则break继续读取等待 if (downstreamData.length - headIndex < _countOfLengthByte + frameLen) { break; } //数据包(长度+内容) NSData *frameData = [downstreamData subdataWithRange:NSMakeRange(headIndex, _countOfLengthByte + frameLen)]; //去除数据长度后的数据内容 RHSocketCustomResponse *rsp = [[RHSocketCustomResponse alloc] init]; rsp.fenGeFu = [RHSocketUtils uint16FromBytes:[headData subdataWithRange:NSMakeRange(0, 2)]]; rsp.dataType = [RHSocketUtils uint16FromBytes:[headData subdataWithRange:NSMakeRange(2, 2)]]; rsp.object = [frameData subdataWithRange:NSMakeRange(_countOfLengthByte, frameLen)]; [output didDecode:rsp]; //调整已经解码数据 headIndex += frameData.length; }//while return headIndex;}@end
codec组件已经准备就绪,接下来我们增加调用控制。RHSocketKit中已经提供了一个单例的service,已经提取和封装了可重用代码。只需要重载编解码器就可以了。
//这里的服务器对应RHSocketServerDemo,连接之前,需要运行RHSocketServerDemo开启服务端监听。 //RHSocketServerDemo服务端只是返回数据,收到的数据是原封不动的,用来模拟发送给客户端的数据。 NSString *host = @"127.0.0.1"; int port = 20162; //变长编解码。包体=包头(包体的长度)+包体数据 RHSocketCustom0330Encoder *encoder = [[RHSocketCustom0330Encoder alloc] init]; RHSocketCustom0330Decoder *decoder = [[RHSocketCustom0330Decoder alloc] init]; [RHSocketService sharedInstance].encoder = encoder; [RHSocketService sharedInstance].decoder = decoder; [[RHSocketService sharedInstance] startServiceWithHost:host port:port];
<span style="font-size:18px;">编写一个服务端对这个通信协议作测试,我这里使用简单的echo服务器测试。代码很简单,我就不贴了。</span>
<span style="font-size:18px;">可以直接查看源码<a target=_blank href="https://github.com/zhu410289616/RHSocketKit/tree/master/RHSocketServerDemo">RHSocketServerDemo</a>。</span>
客户端的过程是,先连接服务器,然后发送数据包。然后服务端返回相同的数据包给客户端,客户端做解码,然后输出展示。
测试代码如下:
#import "ViewController.h"#import "RHSocketService.h"#import "RHSocketCustom0330Encoder.h"#import "RHSocketCustom0330Decoder.h"#import "RHSocketCustomRequest.h"#import "RHSocketCustomResponse.h"@interface ViewController (){ UIButton *_serviceTestButton;}@end@implementation ViewController- (void)loadView{ [super loadView]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(detectSocketServiceState:) name:kNotificationSocketServiceState object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(detectSocketPacketResponse:) name:kNotificationSocketPacketResponse object:nil];}- (void)dealloc{ [[NSNotificationCenter defaultCenter] removeObserver:self];}- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. _serviceTestButton = [UIButton buttonWithType:UIButtonTypeCustom]; _serviceTestButton.frame = CGRectMake(20, 120, 250, 40); _serviceTestButton.layer.borderColor = [UIColor blackColor].CGColor; _serviceTestButton.layer.borderWidth = 0.5; _serviceTestButton.layer.masksToBounds = YES; [_serviceTestButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal]; [_serviceTestButton setTitle:@"Test Custom Codec" forState:UIControlStateNormal]; [_serviceTestButton addTarget:self action:@selector(doTestServiceButtonAction) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:_serviceTestButton]; }- (void)doTestServiceButtonAction{ //方便多次观察,先停止之前的连接 [[RHSocketService sharedInstance] stopService]; //这里的服务器对应RHSocketServerDemo,连接之前,需要运行RHSocketServerDemo开启服务端监听。 //RHSocketServerDemo服务端只是返回数据,收到的数据是原封不动的,用来模拟发送给客户端的数据。 NSString *host = @"127.0.0.1"; int port = 20162; //变长编解码。包体=包头(包体的长度)+包体数据 RHSocketCustom0330Encoder *encoder = [[RHSocketCustom0330Encoder alloc] init]; RHSocketCustom0330Decoder *decoder = [[RHSocketCustom0330Decoder alloc] init]; [RHSocketService sharedInstance].encoder = encoder; [RHSocketService sharedInstance].decoder = decoder; [[RHSocketService sharedInstance] startServiceWithHost:host port:port];}- (void)detectSocketServiceState:(NSNotification *)notif{ //NSDictionary *userInfo = @{@"host":host, @"port":@(port), @"isRunning":@(_isRunning)}; //对应的连接ip和状态数据。_isRunning为YES是连接成功。 //没有心跳超时后会自动断开。 NSLog(@"detectSocketServiceState: %@", notif); id state = notif.object; if (state && [state boolValue]) { //连接成功后,发送数据包 RHSocketCustomRequest *req = [[RHSocketCustomRequest alloc] init]; req.fenGeFu = 0x24; req.dataType = 1234; req.object = [@"变长编码器和解码器测试数据包1" dataUsingEncoding:NSUTF8StringEncoding]; [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationSocketPacketRequest object:req]; req = [[RHSocketCustomRequest alloc] init]; req.fenGeFu = 0x24; req.dataType = 12; req.object = [@"变长编码器和解码器测试数据包20" dataUsingEncoding:NSUTF8StringEncoding]; [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationSocketPacketRequest object:req]; req = [[RHSocketCustomRequest alloc] init]; req.fenGeFu = 0x24; req.dataType = 0; req.object = [@"变长编码器和解码器测试数据包300" dataUsingEncoding:NSUTF8StringEncoding]; [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationSocketPacketRequest object:req]; } else { // }//if}- (void)detectSocketPacketResponse:(NSNotification *)notif{ NSLog(@"detectSocketPacketResponse: %@", notif); //这里结果,记得观察打印的内容 NSDictionary *userInfo = notif.userInfo; RHSocketCustomResponse *rsp = userInfo[@"RHSocketPacket"]; NSLog(@"detectSocketPacketResponse data: %@", [rsp object]); NSLog(@"detectSocketPacketResponse string: %@", [[NSString alloc] initWithData:[rsp object] encoding:NSUTF8StringEncoding]);}
源码RHSocketCustomCodecDemo已经上传到github中,可以自行下载。
(demo中是通过pod引入RHSocketKit库的,已经包含了Podfile文件,pod install后就可以正常运行)
------------------------
转载请注明出处,谢谢
email: zhu410289616@163.com
qq: 410289616
qq群:330585393
- 利用RHSocketKit构建自定义协议通信
- 自定义协议通信
- CocoaAsyncSocket 网络通信使用之RHSocketKit框架(四)
- CocoaAsyncSocket 网络通信使用之RHSocketKit框架(四)
- java 利用UDP协议 通信
- 基于memcached协议构建自定义协议
- android利用websocket协议与服务器通信
- 利用UDP协议实现广播通信
- 利用飞鸽协议实现通信功能
- Mina实现自定义协议的通信
- Mina实现自定义协议的通信
- Mina实现自定义协议的通信
- Mina实现自定义协议的通信
- Java - Apache Mina 自定义协议通信
- 系统间通信:通过自定义协议通信加深HTTP理解
- 通过HTTP协议利用VC++ POST通信开发
- 利用smack实现基于XMPP协议的简单通信demo
- 利用VTS与bacnet协议栈进行通信---ReadProperty
- 我在这世上活着,手无寸铁
- C语言中各种基本数据类型在内存中的存储方式
- 【DP_背包专辑】
- 大数据面试题
- 快速实现ARM和DSP的通信和协同工作(一)
- 利用RHSocketKit构建自定义协议通信
- perl学习记录
- HDU 1097 A hard puzzle(快速幂取模)
- HDU4260
- SSH框架整合
- linux窗口管理器学习
- 设计模式----适配器模式
- swift 属性的几个写法
- TableView 小技巧 (一)