Socket&GCDAsyncSocket(異步Socket)

Socket數組

*********************************************服務器

簡單理解Socket 就是網絡鏈接,能夠實現兩個點之間的數據通信。網絡

 

Socket容許使用長鏈接,容許應用程序運行在異步模式(提升效率),只有在須要的時候才接收數據

 

使用Socket,能夠只傳送數據自己而不用進行XML封裝,大大下降數據傳輸的開銷      在(JSON)以前出現的

iOS中經常使用的兩種Socket類型多線程

 

Ø流式Socket(SOCK_STREAM):流式是一種面向鏈接的Socket,針對於面向鏈接的TCP服務應用
Ø數據報式Socket(SOCK_DGRAM):數據報式Socket是一種無鏈接的Socket,對應於無鏈接的UDP服務應用
 
•在iOS中以NSStream(流)來發送和接收數據
開發步驟
 
{
網絡鏈接設置
  1.設置網絡鏈接,綁定到主機和端口
  2.設置輸入流和輸出流的代理,監聽數據流的狀態
  3.將輸入輸出流添加至運行循環
  4.打開輸入流和輸出流
發送消息給服務器
有可讀取字節時,讀取服務器返回的內容
到達流末尾時,關閉流,同時並從主運行循環中刪除
}
 

 經過Scoket能夠實現全部的網絡功能:包括:GET、POST、PUT、DELETE框架

文件讀取、寫入(I/O)方式是以(二進制)流的方式讀取的
 最主要的應用場景是:自定義的協議,編寫自由的網絡應用!
 
 Socket 的難點
 1. 由於全部的(I/O)輸入輸出都是在一個代理方法中調用,隨着自定義協議的複雜度的提升,
    程序編寫難度勢必要大幅度提高。
 2. 多線程的處理!
   輸入流和輸出流都添加到了主運行循環,若是應用過於複雜,將影響主線程程序的性能
    所以,須要使用另一個運行循環,專門管理輸入輸出流。
   代理方法的工做是對數據的輸入輸出流進行「解析」,解析工做一樣不須要影響到主線程的工做。
 
 3.多線程方面的處理,是Socket的一大難點!可使用第三方框架GCDAsyncSocket來解決多線程問題。
異步

鏈接服務器socket

#pragma mark 鏈接到服務器
- (void)connectToServer:(NSString *)hostName port:(NSInteger)port
{
    // 設置網絡
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
  
    // CF框架是C語言的框架
    // 此方法能夠鏈接到服務器,並分配輸入流和輸出流的內存空間
    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)hostName, port, &readStream, &writeStream);
    
    // 記錄已經分配的輸入流和輸出流
    _inputStream = (__bridge NSInputStream *)readStream;
    _outputStream = (__bridge NSOutputStream *)writeStream;
    
    // 設置代理,監聽輸入流和輸出流中的變化
    _inputStream.delegate = self;
    _outputStream.delegate = self;
    
    // Scoket是創建的長鏈接,須要將輸入輸出流添加到主運行循環
    // 若是不將流加入主運行循環,delegate拒絕工做
    [_inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    [_outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    
    // 打開輸入流和輸出流,準備開始文件讀寫操做
    [_inputStream open];
    [_outputStream open];
}

 登陸到聊天室async

 1 #pragma mark 登陸到聊天室
 2 - (IBAction)login
 3 {
 4     NSString *hostName = _hostName.text;
 5     NSInteger port = [_portText.text integerValue];
 6     
 7     [self connectToServer:hostName port:port];
 8     
 9     // 發送登陸消息
10     NSString *msg = [NSString stringWithFormat:@"iam:%@",      _nickNameText.text];
11     // 在網絡上發送的是二進制數據
12     NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
13     
14     // 發送數據,直接往輸入流寫數據
15     [_outputStream write:data.bytes maxLength:data.length];
16 }

 NSStream的代理方法

/**
 NSStreamEventNone = 0,                         // 無事件
 NSStreamEventOpenCompleted = 1UL << 0,         // 創建鏈接完成
 NSStreamEventHasBytesAvailable = 1UL << 1,     // 有可讀的字節,接收到了數據,能夠讀了
 NSStreamEventHasSpaceAvailable = 1UL << 2,     // 可使用輸出流的空間,此時能夠發送數據給服務器
 NSStreamEventErrorOccurred = 1UL << 3,         // 發生錯誤
 NSStreamEventEndEncountered = 1UL << 4         // 流結束事件,在此事件中負責作銷燬工做
 */
#pragma mark NSStream的代理方法

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
    NSLog(@"%d", eventCode);
    
    switch (eventCode) {
        case NSStreamEventOpenCompleted:
            NSLog(@"鏈接完成");
            break;
        case NSStreamEventHasBytesAvailable:
            NSLog(@"有可讀字節");
            // 讀從服務器接收到得數據,從輸入流中讀取
            // 先開闢一段緩衝區以讀取數據,用空間來換取程序的簡單
            uint8_t buffer[1024];
            
            // read返回的是輸入流緩衝區中實際存儲的字節數
            NSInteger len = [_inputStream read:buffer maxLength:sizeof(buffer)];
            
            if (len > 0) { // 讀到數據
                // 將buffer中的數據,轉換成字符串,輸出
                NSString *str = [[NSString alloc] initWithBytes:buffer length:len encoding:NSUTF8StringEncoding];
                
                // 將接收到的內容添加到數組
                [_dataList addObject:str];
                
                // 刷新表格
                [_tableView reloadData];
            }
            
            break;
        case NSStreamEventHasSpaceAvailable:
            NSLog(@"能夠寫入數據");
            break;
        case NSStreamEventErrorOccurred:
            NSLog(@"發生錯誤");
            break;
        case NSStreamEventEndEncountered:
            NSLog(@"流結束");
            // 作善後工做
            // 關閉流的同時,將流從主運行循環中刪除
            [aStream close];
            [aStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
        default:
            break;
    }
}

 GCDAsyncSocket(異步Socket)ide

須要導入Security.framework & CFNetwork.framework框架oop

// 1. Socket通信的第一件事情——先建立一個長鏈接
    _socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
建立一個長鏈接
 1 #pragma mark - Socket代理方法
 2 #pragma mark 鏈接到主機
 3 - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
 4 {
 5     NSLog(@"鏈接創建 %@ %@", host, [NSThread currentThread]);
 6     
 7     // 長鏈接創建完成後,給服務器發送 iam:暱稱 通知服務器用戶登陸
 8     NSString *str = [NSString stringWithFormat:@"iam:%@", _nickNameText.text];
 9     // 網絡通信中,全部的數據都是以二進制流的模式傳輸的
10     NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
11     
12     // 將數據寫入到輸出流,告訴服務器用戶登陸
13     [_socket writeData:data withTimeout:-1 tag:100];
14 }
15 
16 #pragma mark 讀數據
17 - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
18 {
19     NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
20     
21     if (tag == 100) {
22         NSLog(@"登陸消息 %@", str);
23     } else {
24         NSLog(@"聊天消息 %@->%ld", str, tag);
25     }
26     
27     NSLog(@"%@", [NSThread currentThread]);
28     
29     // 剩下的工做 綁定表格數據
30     [_dataList addObject:str];
31     
32     // 在主線程刷新表格
33     dispatch_async(dispatch_get_main_queue(), ^{
34         [_tableView reloadData];
35     });
36 }
37 
38 #pragma mark 寫數據
39 #pragma mark Socket已經把數據寫給了服務器
40 - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
41 {
42     // 經過Log,咱們發如今給服務器寫入數據時,若是指定了tag,根據tag就知道發送(寫給)的是哪一類的數據
43     // 發現了讀數據的代理方法沒有被觸發
44     NSLog(@"寫數據 %ld", tag);
45     
46     // 嘗試讓socket讀一下數據,讀取服務器返回的內容
47     [_socket readDataWithTimeout:-1 tag:tag];
48 }
Socket代理方法
 1 #pragma mark - Action
 2 - (IBAction)connect:(id)sender
 3 {
 4     NSString *hostName = _hostName.text;
 5     int port = [[_portText text] intValue];
 6     
 7     // 鏈接到主機
 8     NSError *error = nil;
 9     if (![_socket connectToHost:hostName onPort:port error:&error]) {
10         NSLog(@"%@", error.localizedDescription);
11     } else {
12         NSLog(@"OK");
13     }
14 }
Action
相關文章
相關標籤/搜索