在互联网世界中,网络访问是必不可少的一部分,而对于程序员来说,网络编程却是一个比较复杂的存在,特别是socket处理方面。
构造自己的socket连接类:
- 为了简化类库中的内容,隐藏一些不关心的接口方法。
- 对类库做一层封装隔离,以个人习惯的方式呈现使用(可以方便库的更新替换,比如:asi到afn的http 库迁移)
基于以上两个原因,我们使用AsyncSocket封装自己的sokcet connection类。代码如下:
GJAsyncSocket.h:
#import <Foundation/Foundation.h>
#import "AsyncSocket.h"
@protocol GJAsyncSocketDelegate <NSObject>
//连接成功回调
-(void)GJAsyncSocketDelagateDidConnect;
//从服务器接收的数据
- (void)GJAsyncSocketDelagateDidReadData:(NSDictionary *)dic;
@end
@interface GJAsyncSocket : NSObject
@property (nonatomic, strong) AsyncSocket *socket; // socket
@property (nonatomic, strong) AsyncSocket *recvSocket; //socket地址
@property (nonatomic, copy ) NSString *socketHost; // socket的Host
//socket 端口
@property (nonatomic, assign) UInt16 socketPort; // socket的prot
@property (nonatomic,weak) id<GJAsyncSocketDelegate> delagate;
+ (ZHAsyncSocket *)sharedInstance;
/*
* 设置地址和端口
*/
-(void)setHostAndPort:(NSString *)socketHost withPort:(UInt16)socketPort;
/*
* socket连接
*/
-(void)socketConnectHost;
/*
* 主动关闭切断socket
*/
-(void)cutOffSocket;
/*
* 发送数据
*/
-(void)sendData:(NSData *)data withTag:(long)tag;
/*
* 连接前的自检
*/
-(void)openClient;
@end
GJAsyncSocket.m:
#import "GJAsyncSocket.h"
#import <CommonCrypto/CommonHMAC.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import <netdb.h>
#import <arpa/inet.h>
enum{
SocketOfflineByServer,//服务器
SocketOfflineByUser,//用户
};
#define k_DCS_IDENTIFY_FLAG -10000
#define k_DCS_HEART_FLAG -10001
@interface GJAsyncSocket()<AsyncSocketDelegate>
@property (nonatomic, strong) NSTimer *connectTimer; // 计时器
@end
@implementation ZHAsyncSocket
+(ZHAsyncSocket *) sharedInstance
{
static ZHAsyncSocket *sharedInstace = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstace = [[self alloc] init];
});
return sharedInstace;
}
-(void)setHostAndPort:(NSString *)socketHost withPort:(UInt16)socketPort {
self.socketHost = socketHost;
self.socketPort = socketPort;
}
#pragma mark - socket连接
-(void)socketConnectHost{
self.socket = [[AsyncSocket alloc] initWithDelegate:self];
NSError *error = nil;
[self.socket connectToHost:self.socketHost onPort:self.socketPort withTimeout:20 error:&error];
}
#pragma mark -- 连接前的自检
-(void)openClient{
// 确保断开后再连,如果对一个正处于连接状态的socket进行连接,会出现崩溃
// 在连接前先进行手动断开
self.socket.userData = SocketOfflineByUser;
[self cutOffSocket];
self.socket.userData = SocketOfflineByServer;
[self socketConnectHost];
}
#pragma mark - 主动关闭切断socket
-(void)cutOffSocket{
// GJLog(@"关闭socket");
self.socket.userData = SocketOfflineByUser;
[self.connectTimer invalidate];
self.connectTimer = nil;
[self.socket setDelegate:nil];
[self.socket disconnect];
self.socket = nil;
}
#pragma mark - 连接成功回调
-(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
// GJLog(@"连接成功");
[self.socket readDataWithTimeout:-1 tag:1];
if (_delagate != nil && [_delagate respondsToSelector:@selector(ZHAsyncSocketDelagateDidConnect)]) {
[_delagate ZHAsyncSocketDelagateDidConnect];
}
// 客户端向服务端进行心跳检测
// [self.connectTimer invalidate];
// self.connectTimer = nil;
// self.connectTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(longConnectToSocket) userInfo:nil repeats:YES];
// [self.connectTimer fire];
}
#pragma mark - 客户端建立心跳连接
-(void)longConnectToSocket{
//连接成功发送身份识别
NSDictionary *postDict = [[NSDictionary alloc] initWithObjectsAndKeys:@"ping",@"type", nil];
NSData *postData = [[EncryptionSocketData sharedInstance] EncryptionSocketDataWithDic:postDict];
// [self sendData:postData withTag:k_DCS_IDENTIFY_FLAG];
[self.socket writeData:postData withTimeout:10 tag:k_DCS_HEART_FLAG];
}
#pragma mark - 断线处理
-(void)onSocketDidDisconnect:(AsyncSocket *)sock
{
NSLog(@"sorry the connect is failure %ld",sock.userData);
if (sock.userData == SocketOfflineByServer) {
// 服务器掉线,重连
[self socketConnectHost];
}else if (sock.userData == SocketOfflineByUser) {
// 如果由用户断开,不进行重连
GJLog(@"用户断开");
return;
}
}
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
{
GJLog(@"Socket链接发生了错误,错误操作者:%ld--错误代码%ld",sock.userData,err.code);
}
#pragma mark - 接收数据
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
// 对得到的data值进行解析与转换即可
[self.socket readDataWithTimeout:-1 tag:tag];
NSError *err;
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData: data
options:NSJSONReadingMutableContainers
error:&err];
GJLog(@"socket收到的数据data = %@",dic);
if (_delagate != nil && [_delagate respondsToSelector:@selector(ZHAsyncSocketDelagateDidReadData:)]) {
[_delagate GJAsyncSocketDelagateDidReadData:dic];
}
}
#pragma mark - 发送数据
-(void)sendData:(NSData *)data withTag:(long)tag {
// GJLog(@"发送数据");
[self.socket writeData:data withTimeout:-1 tag:tag];
}
#pragma mark - 已经发送完成数据
-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
// GJLog(@"我已经完成数据");
}
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
{
//接受数据,死循环
[newSocket readDataWithTimeout:-1 tag:100];
// GJLog(@"接受数据,死循环");
}
@end
注:下一篇文章我会写到关于AsyncSocket☞TCP粘包与断包的问题的简单解决方案,喜欢的请关注一下我的简书:
白纸上涂鸦
未完待续...
网友评论