新進的這家公司搞智能家居,就隨便整理一下其相關技術吧!首先,從GCDAsyncSocket的使用問題着手。
正如名稱一樣GCDAsyncSocket開源類庫是以蘋果的GCD多任務處理機制完成的一個異步交互套接字通訊。使用方法其實並不復雜。每一個GCDAsyncSocket對象(以下簡稱GCDSocket對象)都可以理解為一個socket套接字,我們的操作都是針對於這個socket執行的各種命令,可以打開一個端口偵聽,同樣也可以連接其他計算機的端口進行數據通訊等等等等。
首先我們來創建一個socket。源代碼如下:
GCDAsyncSocket *socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
代碼並不復雜,我們只需要給出一個委托對象也就是第一個參數中的self,以及一個委托運行的GCD隊列即可創建一個GCDAsyncSocket,當前代碼中我們是使用靜態全局函數取得的主消息隊列。當然也可以使用其他方法獲得其他的GCD隊列,比如:dispatch_get_global_queue()。
創建了Socket對象我們即可以理解為,當前我們的socket已經進入程序以供操作。但如果你想和服務器進行通訊,那么我們還需要和服務器進行連接。可能有的使用習慣了http協議的人會問,初始化函數中我們為何不直接指定服務器以及端口號?其實這些肯定都是需要的,但是你要理解到,你的socket對象功能不只是可以用來連接服務器,換而言之我們的socket對象一樣可以偵聽某端口來等待他人連接,所以在通過套接字編程使用TCP協議的時候是我們從http協議過度到TCP協議的一個轉變,我們要使用的是協議,並非某個類,所以我上述說明中都是說從http協議過度到TCP而不是跟大家說現在我們將從NSURLRequest和NSURLConnection過度到GCDAsyncSocket。
好了接下來我們看看如何連接服務器。源代碼如下:
NSError *error = nil;
if (![socket connectToHost:@"192.168.16.254" onPort:5001 error:&error]) {
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:nil message:@"連接發生錯誤,無法配置!" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
[alert show];
}else{
NSLog(@"連接成功");
}
我們只是先聲明了一個錯誤信息的指針,然后使用之前創建的對象調用他的連接方法,它本身就是返回一個連接成功與否的bool值,使用很方便,第一個參數不難看出是一個IP,第二個參數則是一個端口,最后一個是出參,如果連接的過程中出現了錯誤,該方法會把這根指針指向一個具體的錯誤信息,最后我們再判斷一下之前我們創建錯誤信息的指針是否還是指向空,如果並非指向空那么代表我們連接的過程中出現了錯誤,將錯誤信息打印一下吧~不過請切記,此處的錯誤信息並非你創建連接時所有的錯誤都會在此處得到反映。
說到這里我們該說一點真正有用的了,GCDAsyncSocket具有一系列完整的委托機制,我們所做的一切處理基本都是異步處理的狀態,換句話說,連接之后是否連接成功,連接成功要執行什么懂並非應該寫在此處而應該寫在相應的委托之中,同樣的道理一樣適用於發送、讀取數據等等。也就是說我們在此處讀出的錯誤只是同步執行的代碼處理一些連接時會發生的錯誤,而更多的處理我們應在相對應的委托中進行處理。首先請看下面這個方法:
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
這個方法就是在成功連接服務器之后的委托方法。在這個委托方法中,我們可以取到一個socket對象一個服務器IP和一個端口號,你可以處理一切在連接建立之后應該馬上執行的事情,比如與服務器進行通信確認連接端以免出現其他人通過IP及端口隨意的和你的服務器通信,再比如開啟心跳包的發送,讓服務器一直可以確認你的存在。不管做什么,都是你和服務器的編寫者事前約定好的,就像數據傳輸格式什么的。
不管你要在此處都做什么工作,都要處理哪些事宜,請務必記得,在此處你必須要在函數的最后加上一句:
[socket readDataWithTimeout:-1 tag:0];
這是什么?按照你看到這個函數的第一反應取理解,沒錯他就是讀取數據的方法,兩個參數也略顯簡單,一個超時時間,如果你設置成-1則認為永不超時,而第二參數則是區別該次讀取與其他讀取的標志,通常我們在設計視圖上的控件時也會有這樣的一個屬性就是tag。
現在我們的連接動作算是完整的做完了。接下來就是和服務器的交互處理了,也就是讀寫操作,通訊畢竟是為了傳輸數據的。
二、read
[asyncSocket readDataToLength:LENGTH_HEADER withTimeout:-1 tag:TAG_HEADER];
當然我們可以投遞多個讀寫操作而不必等待上一個完成。
[asyncSocket readDataToLength:LENGTH_HEADER withTimeout:-1 tag:TAG_RESPONSE_BODY];
這樣讀請求會自動進入了隊列,當socket 連接上后,請求會自動出隊被執行。這個讀操作完成后 socket:didReadData:withTag: 會被調用。
- (void)socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag { if (tag == TAG_HEADER) { int bodyLength = [self parseHeader:data]; [socket readDataToLength:bodyLength withTimeout:-1 tag:TAG_RESPONSE_BODY]; } else if (tag == TAG_RESPONSE_BODY) { // Process the response [self handleResponseBody:data]; // Start reading the next response [socket readDataToLength:headerLength withTimeout:-1 tag:TAG_FIXED_LENGTH_HEADER]; } }
三、write (和read的用法一樣。)
- (
void
)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(
long
)tag
{
if
(tag ==TAG_HEADER)
NSLog
(@
"TAG_HEADER request sent"
);
else
if
(tag ==TAG_BODY)
NSLog
(@
"TAG_BODY request sent"
);
}