美文网首页技术
iOS网络篇-socket连接(基于CocoaAsyncSock

iOS网络篇-socket连接(基于CocoaAsyncSock

作者: 亲爱的大倩倩 | 来源:发表于2019-06-20 18:17 被阅读0次

    在我们平时的开发中,大多使用的是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

    相关文章

      网友评论

        本文标题:iOS网络篇-socket连接(基于CocoaAsyncSock

        本文链接:https://www.haomeiwen.com/subject/asojqctx.html