iOS网络篇-socket连接(基于CocoaAsyncSocket第三方)

在我们平时的开发中,大多使用的是http/https连接,是客户端主动去请求的一对一模式,请求结束后立马断开,在需要数据时需要客户端主动请求,并且是服务器不能主动向客户端发送数据
而socket连接时,一旦建立上连接,服务器和客户端都可以随时进行数据传输

我们常用的第三方,是CocoaAsyncSocket,它是封装好的一个IM框架,里面包含两种
1.GCDAsyncsocket
基于GCD

2.AsyncSocket
基于runloop
iOS的socket原生的类库是CFNetWork,但是不好用
AsyncSocket是对CFNetWork进行了封装的开源库

Demo1(使用GCDAsyncSocket实现最简单的两端通讯)

自己实现服务器和客户端(服务器是本机(IP: 127.0.0.1 断口: 自定义即可))

服务器:开两个socket,为什么要分开开两个我也不太清楚
一个socket负责开通端口,绑定IP等信息,并与客户端建立连接,并监听通讯
另一个负责与客户端进行通讯

客户端:开一个socket


下面看它们两个的连接图


下面用代码讲一下流程
注意每个代理里都有,这句代码是读取数据,读取成功后会回调接收到消息的代理方法

[self.socket readDataWithTimeout:-1 tag:0];
1.服务器端初始socket并开放端口,并监听客户端socket的链接
@property (nonatomic) GCDAsyncSocket *serverSocket;
self.serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *error = nil;
    BOOL result = [self.serverSocket acceptOnPort:8080 error:&error];
    if (result && error == nil)
    {
        //开放成功
    }

2.客户端socket初始化并连接服务器端
@property (nonatomic) GCDAsyncSocket *socket;
self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
[self.socket connectToHost:@"127.0.0.1" onPort:8080 withTimeout:-1 error:nil];

连接成功时服务器端socket和客户端socket的代理会同时收到监听

服务器代理
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
{
   //保存客户端的socket
    self.clientSocket = newSocket;
    [self showMessageWithStr:@"链接成功"];
    [self showMessageWithStr:[NSString stringWithFormat:@"服务器地址:%@ -端口: %d", newSocket.connectedHost, newSocket.connectedPort]];
    [self.clientSocket readDataWithTimeout:-1 tag:0];
}

客户端代理
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
{
    [self showMessageWithStr:@"连接成功"];
    [self showMessageWithStr:[NSString stringWithFormat:@"服务器IP : %@", host]];
    [self.socket readDataWithTimeout:-1 tag:0];
}

3.两端之间消息通讯均是用下列方法,注意是发的data数据
NSData *data = [self.messageTF.text dataUsingEncoding:NSUTF8StringEncoding];
    [self.clientSocket writeData:data withTimeout:-1 tag:0];

4.两端代理收到消息时
服务器端
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    [self showMessageWithStr:text];
    [self.clientSocket readDataWithTimeout:-1 tag:0];
}
客户端
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    [self showMessageWithStr:text];
    [self.socket readDataWithTimeout:-1 tag:0];
}

Demo2(服务器端还是Demo1的GCDAsyncSocket的例子,这次使用AsyncSocket将客户端进行自定义封装)

实际项目中不可能在页面内写请求,肯定是要封装成单例去请求,然后何时请求,去调用单例方法即可
应该是下面的使用方法

1.在外部时,调用单例连接IP和端口
    [Singleton sharedInstance].socketHost = @"127.0.0.1";
    [Singleton sharedInstance].socketPort = 8080;
    //在连接前先进行手动断开
    [Singleton sharedInstance].socket.userData = SocketOfflineByUser;
    [[Singleton sharedInstance] cutOffSocket];
    //确保断开后再连,如果对一个正处于连接状态的socket进行连接,会出现崩溃

    [Singleton sharedInstance].socket.userData = SocketOfflineByServer;
    [[Singleton sharedInstance] socketConnectHost]; 

2.单例中,在连接上的回调中,初始化计时器,每30S去跟服务器心跳连接
#import <Foundation/Foundation.h>
#import "AsyncSocket.h"
enum
{
    SocketOfflineByServer,  // 服务器掉线,默认为0
    SocketOfflineByUser,    // 用户主动cut
};

@interface Singleton : NSObject<AsyncSocketDelegate>

@property (nonatomic, strong) AsyncSocket    *socket;       // socket
@property (nonatomic, copy  ) NSString       *socketHost;   // socket的Host
@property (nonatomic, assign) UInt16         socketPort;    // socket的prot
@property (nonatomic, retain) NSTimer        *connectTimer; // 计时器

+ (Singleton *)sharedInstance;
-(void)socketConnectHost;// socket连接
-(void)cutOffSocket;// 断开socket连接
@end



m文件中
#pragma mark - 外部方法
+(Singleton *) sharedInstance
{
    
    static Singleton *sharedInstace = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
                  {
                      sharedInstace = [[self alloc] init];
                  });
    
    return sharedInstace;
}

// socket连接
-(void)socketConnectHost
{
    self.socket  = [[AsyncSocket alloc] initWithDelegate:self];
    NSError *error = nil;
    [self.socket connectToHost:self.socketHost onPort:self.socketPort withTimeout:3 error:&error];
}

// 切断socket
-(void)cutOffSocket
{
    self.socket.userData = SocketOfflineByUser;
    [self.connectTimer invalidate];
    [self.socket disconnect];
}



#pragma mark  - AsyncSocketDelegate
//连接成功
-(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    NSLog(@"socket连接成功");
    self.connectTimer = [NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(longConnectToSocket) userInfo:nil repeats:YES];
    [self.connectTimer fire];
    [self.socket readDataWithTimeout:-1 tag:0];
}

//重新连接
-(void)onSocketDidDisconnect:(AsyncSocket *)sock
{
    NSLog(@"连接失败,重连 %ld",sock.userData);
    if (sock.userData == SocketOfflineByServer)
    {
        // 服务器掉线,重连
        [self socketConnectHost];
    }
    else if (sock.userData == SocketOfflineByUser)
    {
        // 如果由用户断开,不进行重连
        return;
    }
}

//接收消息
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
     NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"接收到服务器端传来的消息%@",text);
    [self.socket readDataWithTimeout:30 tag:0];
}


#pragma mark - Other Functions

// 心跳连接
-(void)longConnectToSocket
{
    NSString *longConnect = @"心跳连接";
    NSData   *dataStream  = [longConnect dataUsingEncoding:NSUTF8StringEncoding];
    [self.socket writeData:dataStream withTimeout:1 tag:1];
}





@end

GitHub地址:
https://github.com/CarolineQian/FQSimpleSocketDemo

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

推荐阅读更多精彩内容