展会信息港展会大全

CocoaAsyncSocket网络通信使用之数据编码和解码(二),cocoaasyncsocket
来源:互联网   发布日期:2015-09-28 13:36:22   浏览:1409次  

导读: CocoaAsyncSocket网络通信使用之数据编码和解码(二),cocoaasyncsocket CocoaAsyncSocket网络通信使用之数据编码和解码(二) 在上一篇Cocoa...

CocoaAsyncSocket网络通信使用之数据编码和解码(二),cocoaasyncsocket

CocoaAsyncSocket网络通信使用之数据编码和解码(二)

在上一篇CocoaAsyncSocket网络通信使用之tcp连接(一)中,我们已经利用CocoaAsyncSocket封装了自己的socket

connection。

本篇主要是通过引入编码器和解码器,将可以共用的内容模块化。

简述:

在tcp的应用中,都是以二机制字节的形式来对数据做传输。

一般会针对业务协议构造对应的数据结构/数据对象,然后在使用的时候针对协议转换成二进制数据发送给服务端。

但是我们在不同的app中,不同的业务场景使用不同的tcp协议,这样每次socket模块的重用性就特别差,即使是完全一样的底层内容,也因为实现的时候耦合性太高,而导致需要全部重新开发。为了实现模块化的重用,我仿照mina和netty,引入编码器和解码器。

接口框架设计:

为了后续扩展和自定义实现自己的编码器/解码器,有了以下的设计接口。

数据包

数据包基本接口定义( RHSocketPacket.h):

#import <Foundation/Foundation.h>

@protocol RHSocketPacket <NSObject>

@property (nonatomic, assign, readonly) NSInteger tag;

@property (nonatomic, strong, readonly) NSData *data;

- (instancetype)initWithData:(NSData *)data;

@optional

- (void)setTag:(NSInteger)tag;

- (void)setData:(NSData *)data;

@end

数据包内容接口定义(RHSocketPacketContent.h):(增加timeout超时字段,主要是针对发送的数据包)

#import <Foundation/Foundation.h>

#import "RHSocketPacket.h"

@protocol RHSocketPacketContent <RHSocketPacket>

@property (nonatomic, readonly) NSTimeInterval timeout;

@optional

- (void)setTimeout:(NSTimeInterval)timeout;

@end

tcp编码器

编码器接口定义( RHSocketEncoderProtocol.h):

#import <Foundation/Foundation.h>

#import "RHSocketPacketContent.h"

@protocol RHSocketEncoderOutputDelegate <NSObject>

@required

- (void)didEncode:(NSData *)data timeout:(NSTimeInterval)timeout tag:(NSInteger)tag;

@end

@protocol RHSocketEncoderProtocol <NSObject>

@required

- (void)encodePacket:(id<RHSocketPacketContent>)packet encoderOutput:(id<RHSocketEncoderOutputDelegate>)output;

@end

tcp解码器

解码器接口定义( RHSocketDecoderProtocol.h):

#import <Foundation/Foundation.h>

#import "RHSocketPacketContent.h"

@protocol RHSocketDecoderOutputDelegate <NSObject>

@required

- (void)didDecode:(id<RHSocketPacketContent>)packet tag:(NSInteger)tag;

@end

@protocol RHSocketDecoderProtocol <NSObject>

@required

- (NSUInteger)decodeData:(NSData *)data decoderOutput:(id<RHSocketDecoderOutputDelegate>)output tag:(long)tag;//这里有返回值,是了为了处理数据包拼包

@end

ok,经过几次调整,程序员内心无数次纠结后,接口定义终于完成了,接下来我们看看怎么组合使用。

前面的socket connection在使用时,还是需要实现delegate的委托方法的,

在不同的app间使用还是需要copy,再实现数据[编码]、[解码]、[分发],

然后才到对应的场景。其实在[编码]、[分发]之前都是可以模块化独立的,我们就从这里入手。

架构整合调用

首先引入一个service,来帮我们做[连接]、[编码]、[解码]、[分发]的事情。

废话不多说,直接贴代码。

RHSocketService.h文件:

#import <Foundation/Foundation.h>

#import "RHSocketEncoderProtocol.h"

#import "RHSocketDecoderProtocol.h"

extern NSString *const kNotificationSocketServiceState;

extern NSString *const kNotificationSocketPacketRequest;

extern NSString *const kNotificationSocketPacketResponse;

@interface RHSocketService : NSObject <RHSocketEncoderOutputDelegate, RHSocketDecoderOutputDelegate>

@property (nonatomic, copy) NSString *serverHost;

@property (nonatomic, assign) int serverPort;

@property (nonatomic, strong) id<RHSocketEncoderProtocol> encoder;

@property (nonatomic, strong) id<RHSocketDecoderProtocol> decoder;

@property (assign, readonly) BOOL isRunning;

+ (instancetype)sharedInstance;

- (void)startServiceWithHost:(NSString *)host port:(int)port;

- (void)stopService;

- (void)asyncSendPacket:(id<RHSocketPacketContent>)packet;

@end

RHSocketService.m文件:

#import "RHSocketService.h"

#import "RHSocketConnection.h"

#import "RHSocketDelimiterEncoder.h"

#import "RHSocketDelimiterDecoder.h"

NSString *const kNotificationSocketServiceState = @"kNotificationSocketServiceState";

NSString *const kNotificationSocketPacketRequest = @"kNotificationSocketPacketRequest";

NSString *const kNotificationSocketPacketResponse = @"kNotificationSocketPacketResponse";

@interface RHSocketService () <RHSocketConnectionDelegate>

{

RHSocketConnection *_connection;

}

@end

@implementation RHSocketService

+ (instancetype)sharedInstance

{

static id sharedInstance = nil;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

sharedInstance = [[self alloc] init];

});

return sharedInstance;

}

- (instancetype)init

{

if (self = [super init]) {

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(detectSocketPacketRequest:) name:kNotificationSocketPacketRequest object:nil];

_encoder = [[RHSocketDelimiterEncoder alloc] init];

_decoder = [[RHSocketDelimiterDecoder alloc] init];

}

return self;

}

- (void)dealloc

{

[[NSNotificationCenter defaultCenter] removeObserver:self];

}

- (void)startServiceWithHost:(NSString *)host port:(int)port

{

NSAssert(_encoder, @"error, _encoder is nil...");

NSAssert(_decoder, @"error, _decoder is nil...");

NSAssert(host.length > 0, @"error, host is nil...");

if (_isRunning) {

return;

}

_serverHost = host;

_serverPort = port;

[self openConnection];

}

- (void)stopService

{

_isRunning = NO;

[self closeConnection];

}

- (void)asyncSendPacket:(id<RHSocketPacketContent>)packet

{

if (!_isRunning) {

NSDictionary *userInfo = @{@"msg":@"Send packet error. Service is stop!"};

NSError *error = [NSError errorWithDomain:@"RHSocketService" code:1 userInfo:userInfo];

[self didDisconnectWithError:error];

return;

}

[_encoder encodePacket:packet encoderOutput:self];

}

#pragma mar -

#pragma mark recevie response data

- (void)detectSocketPacketRequest:(NSNotification *)notif

{

id object = notif.object;

[self asyncSendPacket:object];

}

#pragma mark -

#pragma mark RHSocketConnection method

- (void)openConnection

{

[self closeConnection];

_connection = [[RHSocketConnection alloc] init];

_connection.delegate = self;

[_connection connectWithHost:_serverHost port:_serverPort];

}

- (void)closeConnection

{

if (_connection) {

_connection.delegate = nil;

[_connection disconnect];

_connection = nil;

}

}

#pragma mark -

#pragma mark RHSocketConnectionDelegate method

- (void)didDisconnectWithError:(NSError *)error

{

RHSocketLog(@"didDisconnectWithError: %@", error);

_isRunning = NO;

NSDictionary *userInfo = @{@"isRunning":@(_isRunning), @"error":error};

[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationSocketServiceState object:@(_isRunning) userInfo:userInfo];

}

- (void)didConnectToHost:(NSString *)host port:(UInt16)port

{

_isRunning = YES;

NSDictionary *userInfo = @{@"host":host, @"port":@(port), @"isRunning":@(_isRunning)};

[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationSocketServiceState object:@(_isRunning) userInfo:userInfo];

}

- (void)didReceiveData:(NSData *)data tag:(long)tag

{

NSUInteger remainDataLen = [_decoder decodeData:data decoderOutput:self tag:tag];

if (remainDataLen > 0) {

[_connection readDataWithTimeout:-1 tag:tag];

} else {

[_connection readDataWithTimeout:-1 tag:0];

}

}

#pragma mark -

#pragma mark RHSocketEncoderOutputDelegate method

- (void)didEncode:(NSData *)data timeout:(NSTimeInterval)timeout tag:(NSInteger)tag

{

[_connection writeData:data timeout:timeout tag:tag];

}

#pragma mark -

#pragma mark RHSocketDecoderOutputDelegate method

- (void)didDecode:(id<RHSocketPacketContent>)packet tag:(NSInteger)tag

{

NSDictionary *userInfo = @{@"RHSocketPacketBody":packet, @"tag":@(tag)};

[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationSocketPacketResponse object:nil userInfo:userInfo];

}

@end

测试代码如下:

NSString *host = @"www.baidu.com";

int port = 80;

[[RHSocketService sharedInstance] startServiceWithHost:host port:port];

[RHSocketHttpService sharedInstance].encoder = [[RHSocketHttpEncoder alloc] init];

[RHSocketHttpService sharedInstance].decoder = [[RHSocketHttpDecoder alloc] init];

[[RHSocketHttpService sharedInstance] startServiceWithHost:host port:port];

代码调用方法和过程说明:

1-通过startServiceWithHost方法,可实现对服务器的连接。

2-客户端给服务端发送数据,在连接成功后,可以调用asyncSendPacket方法发送数据包。也可以通过notification发送kNotificationSocketPacketRequest通知,发送数据包。在发送数据的过程中,会通过编码器,编码完成后通过connection发送给服务端。

3-服务端向客户端推送数据,会触发didReceiveData方法的回调,通过解码器,解码完成后发出通知给对应的分发器(分发器针对业务调整实现)

代码中是默认的解码器和编码器,针对数据中的分隔符处理,也可以自定义分隔符和数据帧的最大值,代码如下:

分隔符编码器

RHSocketDelimiterEncoder.h文件:

#import <Foundation/Foundation.h>

#import "RHSocketEncoderProtocol.h"

/**

*针对数据包分隔符编码器

*默认数据包中每帧最大值为8192(maxFrameSize == 8192)

*默认数据包每帧分隔符为0xff(delimiter == 0xff)

*/

@interface RHSocketDelimiterEncoder : NSObject <RHSocketEncoderProtocol>

@property (nonatomic, assign) NSUInteger maxFrameSize;

@property (nonatomic, assign) uint8_t delimiter;

@end

RHSocketDelimiterEncoder.m文件:

#import "RHSocketDelimiterEncoder.h"

#import "RHSocketConfig.h"

@implementation RHSocketDelimiterEncoder

- (instancetype)init

{

if (self = [super init]) {

_maxFrameSize = 8192;

_delimiter = 0xff;

}

return self;

}

- (void)encodePacket:(id<RHSocketPacketContent>)packet encoderOutput:(id<RHSocketEncoderOutputDelegate>)output

{

NSData *data = [packet data];

NSMutableData *sendData = [NSMutableData dataWithData:data];

[sendData appendBytes:&_delimiter length:1];

NSAssert(sendData.length < _maxFrameSize, @"Encode frame is too long...");

NSTimeInterval timeout = [packet timeout];

NSInteger tag = [packet tag];

RHSocketLog(@"tag:%ld, timeout: %f, data: %@", (long)tag, timeout, data);

[output didEncode:data timeout:timeout tag:tag];

}

@end

分隔符解码器

RHSocketDelimiterDecoder.h文件:

#import <Foundation/Foundation.h>

#import "RHSocketDecoderProtocol.h"

/**

*针对数据包分隔符解码器

*默认数据包中每帧最大值为8192(maxFrameSize == 8192)

*默认数据包每帧分隔符为0xff(delimiter == 0xff)

*/

@interface RHSocketDelimiterDecoder : NSObject <RHSocketDecoderProtocol>

@property (nonatomic, assign) NSUInteger maxFrameSize;

@property (nonatomic, assign) uint8_t delimiter;

@end

RHSocketDelimiterDecoder.m文件:

#import "RHSocketDelimiterDecoder.h"

#import "RHPacketBody.h"

@interface RHSocketDelimiterDecoder ()

{

NSMutableData *_receiveData;

}

@end

@implementation RHSocketDelimiterDecoder

- (instancetype)init

{

if (self = [super init]) {

_maxFrameSize = 8192;

_delimiter = 0xff;

}

return self;

}

- (NSUInteger)decodeData:(NSData *)data decoderOutput:(id<RHSocketDecoderOutputDelegate>)output tag:(long)tag

{

@synchronized(self) {

if (_receiveData) {

[_receiveData appendData:data];

} else {

_receiveData = [NSMutableData dataWithData:data];

}

NSUInteger dataLen = _receiveData.length;

NSInteger headIndex = 0;

for (NSInteger i=0; i<dataLen; i++) {

NSAssert(i < _maxFrameSize, @"Decode frame is too long...");

uint8_t byte;

[_receiveData getBytes:&byte range:NSMakeRange(i, 1)];

if (byte == _delimiter) {

NSInteger packetLen = i - headIndex;

NSData *packetData = [_receiveData subdataWithRange:NSMakeRange(headIndex, packetLen)];

RHPacketBody *body = [[RHPacketBody alloc] initWithData:packetData];

[output didDecode:body tag:0];

headIndex = i + 1;

}

}

NSData *remainData = [_receiveData subdataWithRange:NSMakeRange(headIndex, dataLen-headIndex)];

[_receiveData setData:remainData];

return _receiveData.length;

}//@synchronized

}

@end

总结:

目前没来得及提供服务器代码,不过socket框架已经基本完整,可以根据不同的协议扩展实现自定义的编码器和解码器。

测试代码中访问的是百度的首页,为http协议,需要额外实现http协议的编码器和解码器。

下一篇,我们来实现针对http的简单编码器和解码器。

--------------------

转载请注明出处,谢谢

email: zhu410289616@163.com

qq: 410289616

版权声明:本文为博主原创文章,未经博主允许不得转载。

http://www.bkjia.com/Androidjc/1026261.htmlwww.bkjia.comtruehttp://www.bkjia.com/Androidjc/1026261.htmlTechArticleCocoaAsyncSocket网络通信使用之数据编码和解码(二),cocoaasyncsocket CocoaAsyncSocket 网络通信使用之数据编码和解码(二) 在上一篇CocoaAsyncSo...

赞助本站

人工智能实验室

相关热词: android开发 android教程

AiLab云推荐
推荐内容
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港