基于GCDAsyncSocket的连接

本篇文章主要针对聊天室的Socket连接,基于GCDAsyncSocket实现.对不太了解GCDAsyncSocket的同学请自行咨询度娘.当然一个聊天室的实现并不只有一个简单的Socket,后续还有聊天室的管理,一时整理不好头绪,之后会和大家分享一下思路.

本类只负责了Socket的连接,记录连接状态,收到数据,发送数据,具体的解析数据等在其他的类里.依旧是废话不多说,撸代码,看思路.

SocketLinker.h文件
//首先是定义枚举,记录连接的状态
typedef NS_ENUM(NSUInteger, LINKSTATE)
{
    LINKSTATE_UNLINK   = 0, // 未连接
    LINKSTATE_LINKING  = 1, // 连接中
    LINKSTATE_LINKED   = 2, // 连接成功了
    LINKSTATE_LOGOUT   = 3 // 退出登录(退出软件用户时的情况,不需要重连)
};
//设置代理方法
@protocol SocketLinkerDelegate <NSObject>
//连接成功
- (void)socketDidConnectToHost:(NSString *)host port:(uint16_t)port;
//连接失败的代理,外界操作处理,比如停止发送心跳包,申请重连
- (void)socketDidDisconnectWithError:(NSError *)error;
//读取Socket数据
- (void)socketDidResponse:(NSData *)data;
@end

@protocol ConnectStateDelegate <NSObject>
//连接状态改变
- (void)connectDidChangeConnectState:(LINKSTATE)newState;
@end
@interface SocketLinker : NSObject
//连接状态,LINKSTATE
@property (nonatomic,assign) LINKSTATE linkState;
@property (nonatomic, weak) id<SocketLinkerDelegate> delegate;
@property (nonatomic, weak) id <ConnectStateDelegate> connectStateDelegate;
//根据服务器主机和端口初始化
- (id)initWithHost:(NSString *)host port:(uint16_t)port;
//连接
- (void)connect;
//断开连接
- (void)disconnect;
// 发送数据包
- (void)sendMsgPacket:(NSData *)packet;
// 读取消息包
- (void)readMsgPacket;
//判断Socket连接状态供外界调用
- (BOOL)isSocketConnected;
@end

以上就是.h中的所有代码了,之后在.m中

@interface SocketLinker()<GCDAsyncSocketDelegate>//遵循GCDAsyncSocketDelegate
//两个线程锁
@property (nonatomic, strong) NSObject *linkLock;
@property (nonatomic, strong) NSObject *lockObject;
//socket连接对象
@property (atomic, strong) GCDAsyncSocket *asyncSocket;
//记录服务器
@property (nonatomic, strong) NSString *host;
//记录端口
@property (nonatomic, assign) uint16_t port;
@end

初始化

- (id)initWithHost:(NSString*)host port:(uint16_t)port
{
    self = [super init];
    if (self)
    {
        self.linkLock = [[NSObject alloc] init];
        self.lockObject = [[NSObject alloc]init];
        self.host = host;
        self.port = port;
        /**
         *  LQ~ 初始状态为未连接
         */
        self.linkState = LINKSTATE_UNLINK;
    }
    return self;
}

连接

- (void)connect
{
    /**
     * LQ~ 由于很可能同时多次进行socket连接,在这里使用线程锁,确保只进行一次连接
     */
    @synchronized (self.linkLock){
        if (LINKSTATE_LINKING != self.linkState)
        {
            // 把当前状态改为链接建立中,这里我们让所有的回调执行都发生在主线程的queue里,当然我们可以传一个专用的queue
            self.asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
            NSError *error = nil;
            /*LQ~ 连接服务器 */
           //CONNECT_TIMEOUT是一个宏定义,定义超时的时间,我用的是30秒
            if (![self.asyncSocket connectToHost:self.host onPort:self.port withTimeout:CONNECT_TIMEOUT error:&error])
            {
                /*LQ~ 如果socktet连接失败,返回NO,记录连接为LINKSTATE_UNLINK */
                self.linkState = LINKSTATE_UNLINK;
            }
            if (error != nil)
            {  
                //当有错误的时候抛出异常错误信息
                @throw [NSException exceptionWithName:@"GCDAsyncSocket" reason:[error localizedDescription] userInfo:nil];
            }
            //当socktet连接成功的时候,记录连接的状态,连接中
            self.linkState = LINKSTATE_LINKING;
        }
    }
}

主动的断开连接

- (void)disconnect
{
    @synchronized (self.linkLock)
    {
        if (self.asyncSocket != nil)
        {
            [self.asyncSocket disconnect];
        }
        self.linkState = LINKSTATE_LOGOUT;
    }
}

连接成功后GCDAsyncSocket的代理回调

//连接成功
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
{  
    //记录连接成功状态
    self.linkState = LINKSTATE_LINKED;
    if (self.delegate && [self.delegate respondsToSelector:@selector(socketDidConnectToHost:port:)])
    {  
        //代理执行连接成功后的操作
        [self.delegate socketDidConnectToHost:host port:port];
    }
    //对读数据进行设置
    [self readMsgPacket];
}

对读取数据进行设置

- (void)readMsgPacket
{
    if (self.linkState == LINKSTATE_LINKED && self.asyncSocket.isConnected == YES)
    {  
        //当确认连接成功后,就可以开始读服务器发送的数据了,设置超时的时间和tag值
        //告诉asyncSocket可以准备好读信息了
        [self.asyncSocket readDataWithTimeout:TIMEOUT_WRITE tag:TAG_READ_STREAM];
    }
    else
    {
       //如果连接失败了,要传给外界信号,我连接出错了,其余的事自己搞定
        if (self.delegate && [self.delegate respondsToSelector:@selector(socketDidDisconnectWithError:)])
        {
            [self.delegate socketDidDisconnectWithError:nil];
        }
    }
}

当收到服务器数据后的GCDAsyncSocket的代理回调

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    //收到了服务器传给的数据,相对应的读数据的类来处理收到的数据
    if (self.delegate && [self.delegate respondsToSelector:@selector(socketDidResponse:)])
    {
        [self.delegate socketDidResponse:[data mutableCopy]];
    }
}

给服务器发送数据

- (void)sendMsgPacket:(NSData *)packet
{
    if ((packet.length && self.linkState == LINKSTATE_LINKED) && self.asyncSocket.isConnected == YES)
    {
        //确定当前Socket是连接着的,发送数据
        [self.asyncSocket writeData:packet withTimeout:TIMEOUT_WRITE tag:TAG_WRITE_STREAM];
    }
    else
    {
        if (self.delegate && [self.delegate respondsToSelector:@selector(socketDidDisconnectWithError:)])
        {
            /*LQ~ 说明连接失败,要传给外界信号,我连接出错了,其余的事自己搞定
            [self.delegate socketDidDisconnectWithError:nil];
        }
    }
   //每次发完之后都要对读取消息进行一次设置
    [self readMsgPacket];
}

写完数据之后的回调的GCDAsyncSocket的代理回调

- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    //在这里我并没有做其他的操作,各位可以根据需要自己做相应的设置
}

查看scoket的连接状态,供外界调用查看

- (BOOL)isSocketConnected
{
    return self.asyncSocket.isConnected;
}

重写linkState的setter方法

- (void)setLinkState:(LINKSTATE)linkState
{
    _linkState = linkState;
    if (self.connectStateDelegate && [self.connectStateDelegate respondsToSelector:@selector(connectDidChangeConnectState:)])
    {
        //连接状态发生了改变,外界代理去做相应的处理
        [self.connectStateDelegate connectDidChangeConnectState:linkState];
    }
}

当socket连接断开的时候GCDAsyncSocket的代理回调

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
    self.asyncSocket = nil;
    // 判断连接状态不是主动断开连接的时候
    if(self.linkState != LINKSTATE_LOGOUT)
    {
        self.linkState = LINKSTATE_UNLINK;
        if (self.delegate && [self.delegate respondsToSelector:@selector(socketDidDisconnectWithError:)])
        {
            /*LQ~ 说明连接失败,要传给外界信号,我连接出错了,其余的事自己搞定
            [self.delegate socketDidDisconnectWithError:err];
        }
    }
}

以上就是聊天室的Socket连接管理类了,大体思路就是:连接服务器->连接成功了告诉自己代理->准备读写数据,之间加入了对连接状态的判断,确保socket连接.
之后会有通讯管理类的相关写法思路.如果各位同学发现问题,请在下方留言指点,相互帮助提高.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容

  • 转载:http://www.cocoachina.com/ios/20170615/19529.html 参考:h...
    F麦子阅读 3,999评论 3 2
  • 本人有若干成套学习视频, 可试看! 可试看! 可试看, 重要的事情说三遍 包含Java, 数据结构与算法, iOS...
    小冰山口阅读 1,073评论 5 4
  • 点击查看原文 Web SDK 开发手册 SDK 概述 网易云信 SDK 为 Web 应用提供一个完善的 IM 系统...
    layjoy阅读 13,724评论 0 15
  • 前言: WebRTC,名称源自网页实时通信(Web Real-Time Communication)的缩写,简而言...
    涂耀辉阅读 50,686评论 134 430
  • 结婚与恋爱毫无关系,人们老以为恋爱成熟后便自然而然的结婚,却不知结婚只是一种生活方式,人人可以结婚,简单得很。而爱...
    宏红阅读 154评论 0 0