美文网首页
GCDAsynSocket之TCP简析

GCDAsynSocket之TCP简析

作者: alvin_wang | 来源:发表于2018-01-06 09:39 被阅读69次

GCDAsynSocket是一个开源的基于GCD的异步的socket库。它支持IPV4和IPV6地址,TLS/SSL协议。同时它支持iOS端和Mac端。本篇主要介绍一下GCDAsynSocket中的TCP用法和实现。

首先通过下面这个方法初始化一个GCDAsynSocket对象。

- (id)initWithDelegate:(id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq;

这里面需要传入代理的对象,代理队列以及socket队列。其中socket队列不能是一个并发的队列,不然读写就乱了。同时为了防止socket队列死锁,通过dispatch_queue_set_specific来为这个队列添加key值。

dispatch_queue_set_specific(socketQueue, IsOnSocketQueueOrTargetQueueKey, nonNullUnusedPointer, NULL);

同时这里面初始化了readQueue、writeQueue数组,和一个4K数据缓冲区,后面读写的数据都会先经过这个缓冲区。

readQueue = [[NSMutableArray alloc] initWithCapacity:5];
currentRead = nil;
    
writeQueue = [[NSMutableArray alloc] initWithCapacity:5];
currentWrite = nil;
    
preBuffer = [[GCDAsyncSocketPreBuffer alloc] initWithCapacity:(1024 * 4)];

接着通过下面这个方法建立一个tcp连接:

- (BOOL)connectToHost:(NSString *)host
           onPort:(uint16_t)port
     viaInterface:(nullable NSString *)interface
      withTimeout:(NSTimeInterval)timeout
            error:(NSError **)errPtr;

你需要传入host,port,timeout等信息。其中interface是一个备用的port,绝大多数情况下只需传nil。它会把里面的操作都放入上面的socketQueue中。
在这方法里面,先做了一个地址检测。

NSMutableArray *addresses = [[self class] lookupHost:hostCpy port:port error:&lookupErr];

同时在里面会做一个超时计时器,超时时间为一开始传入的时间。

- (void)startConnectTimeout:(NSTimeInterval)timeout
{
    if (timeout >= 0.0)
    {
        connectTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, socketQueue);
    
        __weak GCDAsyncSocket *weakSelf = self;
    
        dispatch_source_set_event_handler(connectTimer, ^{ @autoreleasepool {
        #pragma clang diagnostic push
        #pragma clang diagnostic warning "-Wimplicit-retain-self"
    
            __strong GCDAsyncSocket *strongSelf = weakSelf;
            if (strongSelf == nil) return_from_block;
        
            [strongSelf doConnectTimeout];
        
        #pragma clang diagnostic pop
        }});
        dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC));
        dispatch_source_set_timer(connectTimer, tt, DISPATCH_TIME_FOREVER, 0);
    
        dispatch_resume(connectTimer);
    }
}

然后尝试去连接这个地址,中间先会做一些ipv4地址和ipv6地址的转换,接着会并发的发送connect()连接。一旦连接成功,就会在didConnect方法中开启读写流连接。一旦进入didConnect方法,就会关闭前面的超时计时器,因为已经建立tcp握手连接。另外通过CFStreamCreatePairWithSocket的读写流连接也都是放在socketQueue中执行的。接着通过registerForStreamCallbacksIncludingReadWrite注册读写的回调。注册完之后会把读写放在一个cfstreamThread线程中进行执行,并且在cfstreamThread加入了通过计时器激活的runloop,用来不停的循环检测。

[strongSelf lookup:aStateIndex didSucceedWithAddress4:address4 address6:address6];
--[self connectSocket:socketFD address:address stateIndex:aStateIndex];
----connect(socketFD, (const struct sockaddr *)[address bytes], (socklen_t)[address length]);
------[strongSelf didConnect:aStateIndex];
--------createReadAndWriteStream
----------CFStreamCreatePairWithSocket(NULL, (CFSocketNativeHandle)socketFD, &readStream, &writeStream);
------------registerForStreamCallbacksIncludingReadWrite
--------------CFReadStreamSetClient(readStream, readStreamEvents, &CFReadStreamCallback, &streamContext)
--------------CFWriteStreamSetClient(writeStream, writeStreamEvents, &CFWriteStreamCallback, &streamContext)
----------------startCFStreamThreadIfNeeded
------------------CFReadStreamScheduleWithRunLoop(asyncSocket->readStream, runLoop, kCFRunLoopDefaultMode);
------------------CFWriteStreamScheduleWithRunLoop(asyncSocket->writeStream, runLoop, kCFRunLoopDefaultMode);

这样一个连接就建立了。如果连接建立就会回调到这个代理方法中。你可以在里面读写数据。

- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port;

一旦收到服务端返回的数据,就会回调到这个方法。

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;

参考:CocoaAsyncSocket

相关文章

  • GCDAsynSocket之TCP简析

    GCDAsynSocket是一个开源的基于GCD的异步的socket库。它支持IPV4和IPV6地址,TLS/SS...

  • GCDAsynSocket文档翻译

    GCDAsynSocket是一个建立在GCD上的TCP socket库, 这个项目同样包含基于Runloop的版本...

  • TCP状态简析

    三次握手 LISTEN服务端状态,应用程序打开相应的端口,等待客户端程序连接 SYN_SEND客户端状态,客户端发...

  • TCP 三次握手与四次分手

    主要参考这篇文章: 简析TCP的三次握手与四次分手 首先是TCP的包结构: 然后是TCP状态变化:

  • 简析TCP的三次握手与四次挥手

    简析TCP的三次握手与四次挥手 TCP是什么? 具体的关于TCP是什么,我不打算详细的说了;当你看到这篇...

  • Spring Boot启动流程简析

    Spring Boot启动流程简析 木叶之荣,2017年12月 Spring Boot启动流程简析(一) Spri...

  • TCP报文结构和功能简析

    TCP:传输、控制、协议。 TCP与UDP最大却别就在那个C上面,它充分实现了数据传输时各种控制功能。可以进行丢包...

  • mybatis-spring解析

    1、概述 原生Mybatis源码简析(上)原生Mybatis源码简析(下)在介绍原生Mybatis源码简析文章中,...

  • 简析 Swift 的模块系统

    简析 Swift 的模块系统 简析 Swift 的模块系统

  • 简析Swift和C的交互

    简析Swift和C的交互 简析Swift和C的交互

网友评论

      本文标题:GCDAsynSocket之TCP简析

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