Socket數組
*********************************************服務器
簡單理解Socket 就是網絡鏈接,能夠實現兩個點之間的數據通信。網絡
iOS中經常使用的兩種Socket類型多線程
經過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 }
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 }