文章标题

刚做完一个项目,用到了socket通讯,在这里分享一下,新手初写,欢迎提供意见,有不对之处,请及时向作者反应,以免对大家产生误导
用到的是GCDAsyncSocket框架,包括 心跳处理 拆包合包 自动手动重连
AsyncSocket和GCDAsyncSocket一个是同步线程一个是异步线程
GCDAsyncSocket有自动手动的重连对象,但是我处理时候赋值是有问题(AsyncSocket就没有问题,可能是我处理有问题吧),所以就自己用了一个bool类型

心跳包的作用:socket有时候虽然没有断开,但是实际上是断开状态(Wi-Fi在中途没有连上互联网时)socket是不会进入“断开”的代理方法的,这个时候就需要心跳来判断
在连接上socket后,会不断的向服务器发送心跳包(心跳包的内容一般比较简单),服务器接到心跳包后,会有相应,接到响应,说明socket是可以正常通讯的,如果在一点的时间 内没有接到心跳响应,那么就说明socket断掉了,需要重新连接服务器
1.首先自然是创建一个单利类

+ (instancetype)stateConnectAction{
    SocketConnect *socketConnect = [SocketConnect sharedManager];
    return socketConnect;
}
+ (SocketConnect *)sharedManager
{
    static SocketConnect *sharedSocketConnect = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        sharedSocketConnect = [[SocketConnect alloc] init];
    });
    return sharedSocketConnect;
}

2.初始化socket,由于我这边是要快速的连接服务器,所以,会查找上次登录过的服务器

- (instancetype)init
{
    if (self = [super init]) {
        self.clientSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
        _server = CONF_GET(@"server");
        _port   = CONF_GET(@"port");

        if (_server == nil || _port == nil) {
            _server = [[self.serverArr firstObject] objectForKey:@"server"];
            _port = [[self.serverArr firstObject] objectForKey:@"port"];
        }
    }
    return self;
}

3.当然是连接服务器

//socket连接
-(void)creatSocket{
    if (self.clientSocket == nil || [self.clientSocket isDisconnected]) {
        //初始化 GCDAsyncSocket
        self.clientSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
        //如果能连接成功
        NSError *error = nil;
        if (![self.clientSocket connectToHost:_server onPort:[_port intValue] withTimeout:10 error:&error]) {
        }
    }
    self.needConnect = YES;
}

4.连接服务器会几个有代理方法
4.1 socket断开连接。
当err是空的时候,说明服务器是可以连接的,但是服务器是断开状态,你可以选择重新连接此服务器
当err不是空的时候,会有错误信息的时候,说明服务器是连接有问题的,但是服务器连接不上,你这个时候就要重新选择其它的服务器了

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(nullable NSError *)err{
    if (err == nil) {
        NSLog(@"socket准备连接");
        if (_delegate && [_delegate respondsToSelector:@selector(getConnect:)]) {
            [_delegate getConnect:@"连接中..."];
        }
        [self creatSocket];
    }else{
        NSLog(@"socket准备切换服务器");
        if (_delegate && [_delegate respondsToSelector:@selector(getConnect:)]) {
            [_delegate getConnect:@"连接失败"];
        }
        //换掉服务器
        [self changeServer];
    }
}

4.2 socket连接成功
socket连接成功会调用此方法,这个时候你就要接受消息了
readDataWithTimeout:tag: Timeout是你接受消息的时长,-1表示一直接受消息
tag 个人理解为你要接受消息的标记,比如发一个消息tag为1,你就可以接受tag为1的信息
连接成功后,你可以发送心跳包了,完整代码,稍后会贴出的

- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{
    NSLog(@"%@,%@",_server,_port);
    //读取Socket通讯内容
    [self.clientSocket readDataWithTimeout:-1 tag:0];
    NSLog(@"连接成功");
    CONF_SET(@"server", _server);
    CONF_SET(@"port", _port);
    if (_delegate && [_delegate respondsToSelector:@selector(getConnect:)]){
        [_delegate getConnect:@"连接成功"];
    }
}

5.socket接受到消息
一般的socket接收到之后会有如下处理
1.判断收到的信息是否完整,socket一般会发一串信息,前面的四个字节指定要发消息的长度,后面的就将收到的信息按长度处理
2.一条信息可能不是一次就能收到的,要分几次收
3.可能会有粘包的情况,反正我没见过

// 收到消息
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    //将得到的数据进行处理
    [self hangleBufferData:data];
    [self.clientSocket readDataWithTimeout:-1 tag:0];
}
- (void)hangleBufferData:(NSData *)newData{

    [self.bufferData appendData:newData];//合包
    //取前面四个字节的长度
    int i = 1;
    [_bufferData getBytes:&i length:4];

    if (i == ([[_bufferData copy] length] - 4)) {//刚好
        //处理接受的数据
        [self checkIsHeart:i];
        //将_bufferData初始化
        _bufferData = [NSMutableData data];
    }else if(i < [_bufferData length] - 4){//需要拆包
        //处理接受的数据
        [self checkIsHeart:i];
        //将剩余的数据继续进行判断
        NSData *buffer = [_bufferData subdataWithRange:NSMakeRange(i + 4, _bufferData.length- i - 4)];
        _bufferData = [NSMutableData data];
        [_bufferData appendData:buffer];

        //继续进行判断处理
        [self hangleBufferData:nil];
    }
}
//处理接受的数据
- (void)checkIsHeart:(int)userDataLenght{
    //截取有用的数据
    NSData *contentData = [_bufferData subdataWithRange:NSMakeRange(4, userDataLenght)];
    //对有用的数据进行接收(有些数据进行了一些加密措施)
    NSString *strData = [NSString stringChangInfo:contentData];
    //将得到的数据传递出去
    if (_delegate && [_delegate respondsToSelector:@selector(getBackData:)]) {
        [_delegate getBackData:strData];
    }
}

完整代码如下
.h文件

#import <Foundation/Foundation.h>
#import "GCDAsyncSocket.h"
@protocol SocketConnectDelegate <NSObject>
- (void)getBackData:(NSString *)strData;
- (void)getConnect:(NSString *)connectState;
@end
@interface SocketConnect:NSObject
// 客户端socket
@property (strong, nonatomic) GCDAsyncSocket *clientSocket;
@property (nonatomic, weak) id<SocketConnectDelegate> delegate;
+ (instancetype)stateConnectAction;
- (void)disMissConnect;//手动断开连接,不需要重连
- (void)disMissNeedConnect;//手动断开连接,需要重连
- (void)sendMessage:(NSString *)message;
@end

.m文件

#import "SocketConnect.h"
#import "Golbal.h"
#import "HeartModel.h"

#import "NSString+ChangeFormat.h"
#import <UIKit/UIKit.h>
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <unistd.h>

@interface SocketConnect ()<GCDAsyncSocketDelegate>
@property(nonatomic,strong)SocketConnect * socketConnect;
@property(nonatomic,strong)NSMutableData * bufferData;//接收到的数据
@property (nonatomic, assign) BOOL    needConnect; //需不需要重新连接
@property (nonatomic, strong) NSArray    *serverArr; //服务器数组
@property (nonatomic, copy) NSString    *server; //
@property (nonatomic, copy) NSString    *port; //

@property (nonatomic, strong) NSTimer    *beforTimer; // 计时器
@property (nonatomic, strong) NSTimer    *heartTimer; // 心跳计时器
@end
@implementation SocketConnect

-(NSArray *)serverArr{
    if (_serverArr == nil) {
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"serverInfo" ofType:@"plist"];
        _serverArr = [[NSArray alloc] initWithContentsOfFile:filePath];
    }
    return _serverArr;
}
+ (SocketConnect *)sharedManager
{
    static SocketConnect *sharedSocketConnect = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        sharedSocketConnect = [[SocketConnect alloc] init];
    });
    return sharedSocketConnect;
}
- (instancetype)init
{
    if (self = [super init]) {
        self.clientSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
        _server = CONF_GET(@"server");
        _port   = CONF_GET(@"port");

        if (_server == nil || _port == nil) {
            _server = [[self.serverArr firstObject] objectForKey:@"server"];
            _port = [[self.serverArr firstObject] objectForKey:@"port"];
        }
    }
    return self;
}
//socket连接
-(void)creatSocket{
    if (self.clientSocket == nil || [self.clientSocket isDisconnected]) {
        //初始化 GCDAsyncSocket
        self.clientSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
        //如果能连接成功
        NSError *error = nil;
        if (![self.clientSocket connectToHost:_server onPort:[_port intValue] withTimeout:10 error:&error]) {
        }
    }
    self.needConnect = YES;
}
//切换服务器
- (void)changeServer{
    if (_serverArr == nil) {
        [self serverArr];
    }
    for (int i = 0; i < _serverArr.count; i ++) {
        NSDictionary *serverDic = _serverArr[i];
        if ([_server isEqualToString:[serverDic objectForKey:@"server"]]) {
            if ((i+1) >=_serverArr.count) {
                _server = [_serverArr[0] objectForKey:@"server"];
                _port = [_serverArr[0] objectForKey:@"port"];
            }else{
                _server = [_serverArr[i+1] objectForKey:@"server"];
                _port = [_serverArr[i+1] objectForKey:@"port"];
            }
            [self creatSocket];
            break;
        }
    }
}
//开始连接
+ (instancetype)stateConnectAction{

    SocketConnect *socketConnect = [SocketConnect sharedManager];
    [socketConnect creatSocket];
    //将_bufferData初始化
    socketConnect.bufferData = [NSMutableData data];

    return socketConnect;
}
//手动断开连接,不需要重连
- (void)disMissConnect{
    self.needConnect = NO;
    [self.clientSocket disconnect];
}
//手动断开连接,需要重连
- (void)disMissNeedConnect{
    self.needConnect = YES;
    [self.clientSocket disconnect];
}
//发送数据
- (void)sendMessage:(NSString *)message{
    NSMutableData *changeData = [NSString dataChangeString:message];
    [self.clientSocket writeData:[changeData copy] withTimeout:-1 tag:0];
}
// 心跳连接
-(void)heartbeat{

    // 根据服务器要求发送固定格式的数据,但是一般是简单的指令
    NSLog(@"发送心跳包");
    NSString *message = ......;
    [self sendMessage:message];

    [self.heartTimer invalidate];
    self.heartTimer = nil;
    if (self.heartTimer == nil) {
        self.heartTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(heartCloce) userInfo:nil repeats:NO];
    }
}
#pragma mark - GCDAsyncSocketDelegate
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(nullable NSError *)err{
    [self.beforTimer invalidate];
    self.beforTimer = nil;
    [self.heartTimer invalidate];
    self.heartTimer = nil;

    NSLog(@"断开连接 err:%@",err);
    if (!self.needConnect) {
        return;
    }
    if (err == nil) {
        NSLog(@"socket准备连接");
        if (_delegate && [_delegate respondsToSelector:@selector(getConnect:)]) {
            [_delegate getConnect:@"连接中..."];
        }
        [self creatSocket];
    }else{
        NSLog(@"socket准备切换服务器");
        if (_delegate && [_delegate respondsToSelector:@selector(getConnect:)]) {
            [_delegate getConnect:@"连接失败"];
        }
        //换掉服务器
        [self changeServer];
    }
}
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{
    NSLog(@"%@,%@",_server,_port);
    //读取Socket通讯内容
    [self.clientSocket readDataWithTimeout:-1 tag:0];
    NSLog(@"连接成功");
    CONF_SET(@"server", _server);
    CONF_SET(@"port", _port);
    if (_delegate && [_delegate respondsToSelector:@selector(getConnect:)]){
        [_delegate getConnect:@"连接成功"];
    }
    // 每隔30s像服务器发送心跳包
    if (self.beforTimer == nil) {
        self.beforTimer = [NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(heartbeat) userInfo:nil repeats:YES];
    }
    [self.beforTimer fire];
}
- (void)heartCloce{
    NSLog(@"socket心跳断开");
    [self.beforTimer invalidate];
    self.beforTimer = nil;
    [self.heartTimer invalidate];
    self.heartTimer = nil;
    if (_delegate && [_delegate respondsToSelector:@selector(getConnect:)]) {
        [_delegate getConnect:@"连接中..."];
    }
    //断开连接
    [self disMissNeedConnect];
}
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{

}
// 收到消息
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    //将得到的数据进行处理
    [self hangleBufferData:data];
    [self.clientSocket readDataWithTimeout:-1 tag:0];
}
- (void)hangleBufferData:(NSData *)newData{

    [self.bufferData appendData:newData];//合包
    //取前面四个字节的长度
    int i = 1;
    [_bufferData getBytes:&i length:4];

    if (i == ([[_bufferData copy] length] - 4)) {//刚好
        //处理接受的数据
        [self checkIsHeart:i];
        //将_bufferData初始化
        _bufferData = [NSMutableData data];

    }else if(i < [_bufferData length] - 4){//需要拆包
        //处理接受的数据
        [self checkIsHeart:i];
        //将剩余的数据继续进行判断
        NSData *buffer = [_bufferData subdataWithRange:NSMakeRange(i + 4, _bufferData.length- i - 4)];
        _bufferData = [NSMutableData data];
        [_bufferData appendData:buffer];

        //继续进行判断处理
        [self hangleBufferData:nil];
    }
}
//处理接受的数据
- (void)checkIsHeart:(int)userDataLenght{
    //截取有用的数据
    NSData *contentData = [_bufferData subdataWithRange:NSMakeRange(4, userDataLenght)];
    //对有用的数据进行接收(有些数据进行了一些加密措施)
    NSString *strData = [NSString stringChangInfo:contentData];

    //查看是否是心跳包(看个人的数据如何解析的)
    NSArray * staArr = [strData componentsSeparatedByString:@"|"];
    if ([[staArr firstObject] isEqualToString:@"C_Heart"]) {
        NSLog(@"接受心跳包");
        [self.heartTimer invalidate];
        self.heartTimer = nil;
    }
    //将得到的数据传递出去
    if (_delegate && [_delegate respondsToSelector:@selector(getBackData:)]) {
        [_delegate getBackData:strData];
    }
}
@end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值