1. 程式人生 > >iOS用GCDAsyncSocket開發Socket通訊

iOS用GCDAsyncSocket開發Socket通訊

GCDAsyncSocket是CocoaAsyncSocket第三方庫中的其中一個類,本文介紹的就是基於這一個類來做快速的socket通訊開發,而且該庫已經支援IPv4和IPv6

        首先,介紹一下CocoaAsyncSocket第三方庫的用途

CocoaAsyncSocket為Mac和iOS提供了易於使用且強大的非同步通訊庫

      在Podfile檔案中,只要加上這句話就可以使用了

pod 'CocoaAsyncSocket', '7.4.1'

        簡單的Socket通訊包括了建連、斷開連線、傳送socket業務請求、重連這四個基本功能

      下面,我就按照這個四個基本功能來講一下,怎麼來使用CocoaAsyncSocket中GCDAsyncSocket這個類來開發Socket通訊

       首先,Socket在第一步時,需要建連才能開始通訊

        在GCDAsyncSocket中提供了四種初始化的方法

/**

 * GCDAsyncSocket uses the standard delegate paradigm,

 * but executes all delegate callbacks on a given delegate dispatch queue.

 * This allows for maximum concurrency, while at the same time providing easy thread safety.

 * 

 * You MUST set a delegate AND delegate dispatch queue before attempting to

 * use the socket, or you will get an error.

 * 

 * The socket queue is optional.

 * If you pass NULL, GCDAsyncSocket will automatically create it's own socket queue.

 * If you choose to provide a socket queue, the socket queue must not be a concurrent queue.

 * If you choose to provide a socket queue, and the socket queue has a configured target queue,

 * then please see the discussion for the method markSocketQueueTargetQueue.

 * 

 * The delegate queue and socket queue can optionally be the same.

**/

- (instancetype)init;

- (instancetype)initWithSocketQueue:(nullabledispatch_queue_t)sq;

- (instancetype)initWithDelegate:(nullableid<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullabledispatch_queue_t)dq;

- (instancetype)initWithDelegate:(nullableid<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullabledispatch_queue_t)dq socketQueue:(nullabledispatch_queue_t)sq;

#pragma mark Configuration

@property (atomic,weak, readwrite,nullable) id<GCDAsyncSocketDelegate> delegate;

#if OS_OBJECT_USE_OBJC

@property (atomic,strong, readwrite,nullable) dispatch_queue_t delegateQueue;

#else

@property (atomic,assign, readwrite,nullable) dispatch_queue_t delegateQueue;

#endif

      sq是socket的執行緒,這個是可選的設定,如果你寫null,GCDAsyncSocket內部會幫你建立一個它自己的socket執行緒,如果你要自己提供一個socket執行緒的話,千萬不要提供一個併發執行緒,在頻繁socket通訊過程中,可能會阻塞掉,個人建議是不用建立

aDelegate就是socket的代理

       dq是delegate的執行緒

必須要需要設定socket的代理以及代理的執行緒,否則socket的回撥你壓根兒就不知道了,

比如:

self.socket = [[GCDAsyncSocketalloc]initWithDelegate:selfdelegateQueue:dispatch_get_main_queue()];

接著,在設定代理之後,你需要嘗試連線到相應的地址來確定你的socket是否能連通了

/**

 * Connects to the given host and port with an optional timeout.

 * 

 * This method invokes connectToHost:onPort:viaInterface:withTimeout:error: and uses the default interface.

**/

- (BOOL)connectToHost:(NSString *)host

               onPort:(uint16_t)port

          withTimeout:(NSTimeInterval)timeout

                error:(NSError **)errPtr;

host是主機地址,port是埠號

如果建連成功之後,會收到socket成功的回撥,在成功裡面你可以做你需要做的一些事情,我這邊的話,是做了心跳的處理

/**

 * Called when a socket connects and is ready for reading and writing.

 * The host parameter will be an IP address, not a DNS name.

**/

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

如果建連失敗了,會收到失敗的回撥,我這邊在失敗裡面做了重連的操作

/**

 * Called when a socket disconnects with or without error.

 * 

 * If you call the disconnect method, and the socket wasn't already disconnected,

 * then an invocation of this delegate method will be enqueued on the delegateQueue

 * before the disconnect method returns.

 * 

 * Note: If the GCDAsyncSocket instance is deallocated while it is still connected,

 * and the delegate is not also deallocated, then this method will be invoked,

 * but the sock parameter will be nil. (It must necessarily be nil since it is no longer available.)

 * This is a generally rare, but is possible if one writes code like this:

 * 

 * asyncSocket = nil; // I'm implicitly disconnecting the socket

 * 

 * In this case it may preferrable to nil the delegate beforehand, like this:

 * 

 * asyncSocket.delegate = nil; // Don't invoke my delegate method

 * asyncSocket = nil; // I'm implicitly disconnecting the socket

 * 

 * Of course, this depends on how your state machine is configured.

**/

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(nullableNSError *)err;

重連操作其實比較簡單,只需要再呼叫一次建連請求,我這邊設定的重連規則是重連次數為5次,每次的時間間隔為2的n次方,超過次數之後,就不再去重連了

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(nullableNSError *)err {

self.status= -1;

if(self.reconnection_time>=0 && self.reconnection_time <= kMaxReconnection_time) {

[self.timer invalidate];

self.timer=nil;

int time =pow(2,self.reconnection_time);

self.timer= [NSTimer scheduledTimerWithTimeInterval:time target:selfselector:@selector(reconnection) userInfo:nil repeats:NO];

self.reconnection_time++;

NSLog(@"socket did reconnection,after %ds try again",time);

} else {

self.reconnection_time=0;

NSLog(@"socketDidDisconnect:%p withError: %@", sock, err);

}

}

     這裡我用status來標記socket的連線狀態

       那麼socket已經建連了,該怎麼發起socket通訊呢?

      你需要和後端開發人員商定好socket協議格式,比如:

[NSString stringWithFormat:@"{\"version\":%d,\"reqType\":%d,\"body\":\"%@\"}\r\n",PROTOCOL_VERSION,reqType,reqBody];

       中間應該大家都能看得懂,那為什麼後面需要加上\r\n呢?

       這個\r\n是socket訊息的邊界符,是為了防止發生訊息黏連,沒有\r\n的話,可能由於某種原因,後端會收到兩條socket請求,但是後端不知道怎麼拆分這兩個請求

       同理,在收到socket請求回撥時,也會根據這個邊界符去拆分

       那為什麼要用\r\n呢?

       而且GCDAsyncSocket不支援自定義邊界符,它提供了四種邊界符供你使用\r\n、\r、\n、空字串

在拼裝好socket請求之後,你需要呼叫GCDAsyncSocket的寫方法,來發送請求,然後在寫完成之後你會收到寫的回撥

[self.socket writeData:requestData withTimeout:-1 tag:0];

Timeout是超時時間,這個根據實際的需要去設定

這個是寫的回撥

/**

 * Called when a socket has completed writing the requested data. Not called if there is an error.

**/

- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag;

在寫之後,需要再呼叫讀方法,這樣才能收到你發出請求後從伺服器那邊收到的資料

[self.socket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:10 maxLength:50000 tag:0];

[GCDAsyncSocket CRLFData]這裡是設定邊界符,maxLength是設定你收到的請求資料內容的最大值

在讀回撥裡面,你可以根據不同業務來執行不同的操作

/**

 * Called when a socket has completed reading the requested data into memory.

 * Not called if there is an error.

**/

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

最後一個則是斷開連線,這個只需要呼叫

[self.socket disconnect];

最簡單基礎的Socket通訊,已經大致能完成了~

轉自:http://zeeyang.com/2016/01/17/GCDAsyncSocket-socket/