iOS Socket編程

來源:互聯網
上載者:User

iOS Socket編程

一、網路各個協議:TCP/IP、SOCKET、HTTP等
網路七層由下往上分別為物理層、資料連結層、網路層、傳輸層、會話層、展示層和應用程式層。
其中物理層、資料連結層和網路層通常被稱作媒體層,是網路工程師所研究的對象;
傳輸層、會話層、展示層和應用程式層則被稱作主機層,是使用者所面向和關心的內容。
http協議 對應於應用程式層
tcp協議 對應於傳輸層
ip協議 對應於網路層
三者本質上沒有可比性。 何況HTTP協議是基於TCP串連的。
TCP/IP是傳輸層協議,主要解決資料如何在網路中傳輸;而HTTP是應用程式層協議,主要解決如何封裝資料。
我 們在傳輸資料時,可以只使用傳輸層(TCP/IP),但是那樣的話,由於沒有應用程式層,便無法識別資料內容,如果想要使傳輸的資料有意義,則必須使用應用程式層 協議,應用程式層協議很多,有HTTP、FTP、TELNET等等,也可以自己定義應用程式層協議。WEB使用HTTP作傳輸層協議,以封裝HTTP文本資訊,然 後使用TCP/IP做傳輸層協議將它發送到網路上。Socket是對TCP/IP協議的封裝,Socket本身並不是協議,而是一個調用介面(API),通過Socket,我們才能使用TCP/IP協議。
二、Http和Socket串連區別
相信不少初學手機連網開發的朋友都想知道Http與Socket串連究竟有什麼區別,希望通過自己的淺顯理解能對初學者有所協助。
2.1、TCP串連
要想明白Socket串連,先要明白TCP串連。手機能夠使用連網功能是因為手機底層實現了TCP/IP協議,可以使手機終端通過無線網路建立TCP串連。TCP協議可以對上層網路提供介面,使上層網路資料的傳輸建立在“無差別”的網路之上。
建立起一個TCP串連需要經過“三向交握”:
第一次握手:用戶端發送syn包(syn=j)到伺服器,並進入SYN_SEND狀態,等待伺服器確認;
第二次握手:伺服器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(syn=k),即SYN+ACK包,此時伺服器進入SYN_RECV狀態;
第三向交握:用戶端收到伺服器的SYN+ACK包,向伺服器發送確認包ACK(ack=k+1),此包發送完畢,用戶端和伺服器進入ESTABLISHED狀態,完成三向交握。
握 手過程中傳送的包裡不包含資料,三向交握完畢後,用戶端與伺服器才正式開始傳送資料。理想狀態下,TCP串連一旦建立,在通訊雙方中的任何一方主動關閉連 接之前,TCP 串連都將被一直保持下去。中斷連線時伺服器和用戶端均可以主動發起斷開TCP串連的請求,斷開過程需要經過“四次握手”(過程就不細寫了,就是伺服器和客 戶端互動,最終確定斷開)
2.2、HTTP串連
HTTP協議即超文本傳送協議(HypertextTransfer Protocol ),是Web連網的基礎,也是手機連網常用的協議之一,HTTP協議是建立在TCP協議之上的一種應用。
HTTP串連最顯著的特點是用戶端發送的每次請求都需要伺服器回送響應,在請求結束後,會主動釋放串連。從建立串連到關閉串連的過程稱為“一次串連”。
1)在HTTP 1.0中,用戶端的每次請求都要求建立一次單獨的串連,在處理完本次請求後,就自動釋放串連。
2)在HTTP 1.1中則可以在一次串連中處理多個請求,並且多個請求可以重疊進行,不需要等待一個請求結束後再發送下一個請求。
由 於HTTP在每次請求結束後都會主動釋放串連,因此HTTP串連是一種“短串連”,要保持用戶端程式的線上狀態,需要不斷地向伺服器發起串連請求。通常的 做法是即時不需要獲得任何資料,用戶端也保持每隔一段固定的時間向伺服器發送一次“保持串連”的請求,伺服器在收到該請求後對用戶端進行回複,表明知道客 戶端“線上”。若伺服器長時間無法收到用戶端的請求,則認為用戶端“下線”,若用戶端長時間無法收到伺服器的回複,則認為網路已經斷開。
三、SOCKET原理
3.1、通訊端(socket)概念
通訊端(socket)是通訊的基石,是支援TCP/IP協議的網路通訊的基本操作單元。它是網路通訊過程中端點的抽象表示,包含進行網路通訊必須的五種資訊:串連使用的協議,本地主機的IP地址,本地進程的協議連接埠,遠地主機的IP地址,遠地進程的協議連接埠。
應 用層通過傳輸層進行資料通訊時,TCP會遇到同時為多個應用程式進程提供並發服務的問題。多個TCP串連或多個應用程式進程可能需要通過同一個 TCP協議連接埠傳輸資料。為了區別不同的應用程式進程和串連,許多電腦作業系統為應用程式與TCP/IP協議互動提供了通訊端(Socket)介面。應 用層可以和傳輸層通過Socket介面,區分來自不同應用程式進程或網路連接的通訊,實現資料轉送的並發服務。
3.2 、建立socket串連
建立Socket串連至少需要一對通訊端,其中一個運行於用戶端,稱為ClientSocket,另一個運行於伺服器端,稱為ServerSocket。
通訊端之間的串連過程分為三個步驟:伺服器監聽,用戶端請求,串連確認。
伺服器監聽:伺服器端通訊端並不定位具體的用戶端通訊端,而是處於等待串連的狀態,即時監控網路狀態,等待用戶端的串連請求。
用戶端請求:指用戶端的通訊端提出串連請求,要串連的目標是伺服器端的通訊端。為此,用戶端的通訊端必須首先描述它要已連線的服務器的通訊端,指出伺服器端通訊端的地址和連接埠號碼,然後就向伺服器端通訊端提出串連請求。
連 接確認:當伺服器端通訊端監聽到或者說接收到用戶端通訊端的串連請求時,就響應用戶端通訊端的請求,建立一個新的線程,把伺服器端通訊端的描述發給客戶 端,一旦用戶端確認了此描述,雙方就正式建立串連。而伺服器端通訊端繼續處於監聽狀態,繼續接收其他用戶端通訊端的串連請求。
3.3、SOCKET串連與TCP串連
建立Socket串連時,可以指定使用的傳輸層協議,Socket可以支援不同的傳輸層協議(TCP或UDP),當使用TCP協議進行串連時,該Socket串連就是一個TCP串連。
3.4、Socket串連與HTTP串連
由 於通常情況下Socket串連就是TCP串連,因此Socket串連一旦建立,通訊雙方即可開始相互發送資料內容,直到雙方串連斷開。但在實際網路應用 中,用戶端到伺服器之間的通訊往往需要穿越多個中間節點,例如路由器、網關、防火牆等,大部分防火牆預設會關閉長時間處於非活躍狀態的串連而導致 Socket 串連斷連,因此需要通過輪詢告訴網路,該串連處於活躍狀態。
而HTTP串連使用的是“請求—響應”的方式,不僅在請求時需要先建立串連,而且需要用戶端向伺服器發出請求後,伺服器端才能回複資料。
很 多情況下,需要伺服器端主動向用戶端推送資料,保持用戶端與伺服器資料的即時與同步。此時若雙方建立的是Socket串連,伺服器就可以直接將資料傳送給 用戶端;若雙方建立的是HTTP串連,則伺服器需要等到用戶端發送一次請求後才能將資料傳回給用戶端,因此,用戶端定時向伺服器端發送串連請求,不僅可以 保持線上,同時也是在“詢問”伺服器是否有新的資料,如果有就將資料傳給用戶端。
這裡我們使用Socket實現一個聊天室的功能,關於伺服器這裡的就不介紹了。
一:再標頭檔中第一輸入資料流和輸出資料流和一個訊息數組

@interface ViewController (){NSInputStream *_inputStream;//對應輸入資料流NSOutputStream *_outputStream;//對應輸出資料流}@property (weak, nonatomic) IBOutlet NSLayoutConstraint *inputViewConstraint;@property (weak, nonatomic) IBOutlet UITableView *tableView;@property (nonatomic, strong) NSMutableArray *chatMsgs;//聊天訊息數組@end

懶載入這個訊息數組

-(NSMutableArray *)chatMsgs{if (!_chatMsgs) {   _chatMsgs = [NSMutableArray array]; } return _chatMsgs;}

二:實現輸入輸出資料流的監聽

-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{ NSLog(@"%@",[NSThread currentThread]);//    NSStreamEventOpenCompleted = 1UL << 0,//輸入輸出資料流開啟完成//    NSStreamEventHasBytesAvailable = 1UL << 1,//有位元組可讀//    NSStreamEventHasSpaceAvailable = 1UL << 2,//可以發放位元組//    NSStreamEventErrorOccurred = 1UL << 3,// 串連出現錯誤//    NSStreamEventEndEncountered = 1UL << 4// 串連結束    switch (eventCode) {      case NSStreamEventOpenCompleted:           NSLog(@"輸入輸出資料流開啟完成");          break;      case NSStreamEventHasBytesAvailable:            NSLog(@"有位元組可讀");          [self readData];          break;      case NSStreamEventHasSpaceAvailable:         NSLog(@"可以發送位元組");           break;      case NSStreamEventErrorOccurred:                       NSLog(@" 串連出現錯誤");          break;      case NSStreamEventEndEncountered:          NSLog(@"串連結束");          // 關閉輸入輸出資料流          [_inputStream close];          [_outputStream close];          // 從主運行迴圈移除            [_inputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];          [_outputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];           break;        default:           break;   }}

三:連結的伺服器

- (IBAction)connectToHost:(id)sender {    // 1.建立串連  NSString *host = @"127.0.0.1";    int port = 12345;   // 定義C語言輸入輸出資料流   CFReadStreamRef readStream;   CFWriteStreamRef writeStream;   CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream);    // 把C語言的輸入輸出資料流轉化成OC對象    _inputStream = (__bridge NSInputStream *)(readStream);     _outputStream = (__bridge NSOutputStream *)(writeStream);     // 設定代理     _inputStream.delegate = self;    _outputStream.delegate = self;    // 把輸入輸入資料流添加到主運行迴圈     // 不添加主運行迴圈 代理有可能不工作     [_inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];    [_outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];  // 開啟輸入輸出資料流     [_inputStream open];   [_outputStream open]; }

四:登陸

- (IBAction)loginBtnClick:(id)sender {    // 登入    // 發送使用者名稱和密碼    // 在這裡做的時候,只發使用者名稱,密碼就不用發送    // 如果要登入,發送的資料格式為 "iam:zhangsan";    // 如果要發送聊天訊息,資料格式為 "msg:did you have dinner";    //登入的指令    NSString *loginStr = @"iam:zhangsan";    //把Str轉成NSData    NSData *data = [loginStr dataUsingEncoding:NSUTF8StringEncoding];    [_outputStream write:data.bytes maxLength:data.length]; }

五:讀取伺服器資料

#pragma mark 讀了伺服器返回的資料 -(void)readData{    //建立一個緩衝區 可以放1024個位元組    uint8_t buf[1024];    // 返回實際裝的位元組數     NSInteger len = [_inputStream read:buf maxLength:sizeof(buf)];    // 把位元組數組轉化成字串     NSData *data = [NSData dataWithBytes:buf length:len];     // 從伺服器接收到的資料     NSString *recStr =  [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];    NSLog(@"%@",recStr);     [self reloadDataWithText:recStr]; }

六:發送資料

 -(BOOL)textFieldShouldReturn:(UITextField *)textField{    NSString *text = textField.text;   NSLog(@"%@",text);    // 聊天資訊    NSString *msgStr = [NSString stringWithFormat:@"msg:%@",text];    //把Str轉成NSData    NSData *data = [msgStr dataUsingEncoding:NSUTF8StringEncoding];     // 重新整理表格    [self reloadDataWithText:msgStr];    // 發送資料    [_outputStream write:data.bytes maxLength:data.length];     // 發送完資料,清空textField    textField.text = nil;    return YES;}

七:實現資料的顯示,並且每發送一次訊息都會滾動到對應的位置

 -(void)reloadDataWithText:(NSString *)text{    [self.chatMsgs addObject:text];    [self.tableView reloadData];    // 資料多,應該往上滾動    NSIndexPath *lastPath = [NSIndexPath indexPathForRow:self.chatMsgs.count - 1 inSection:0];    [self.tableView scrollToRowAtIndexPath:lastPath atScrollPosition:UITableViewScrollPositionBottom animated:YES]; } #pragma mark 表格的資料來源 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{     return self.chatMsgs.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {     static NSString *ID = @"Cell";     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];     cell.textLabel.text = self.chatMsgs[indexPath.row];     return cell; } -(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{    [self.view endEditing:YES]; }

八:監聽鍵盤的改變

  // 監聽鍵盤    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kbFrmWillChange:) name:UIKeyboardWillChangeFrameNotification object:nil]; }-(void)kbFrmWillChange:(NSNotification *)noti{    NSLog(@"%@",noti.userInfo);   // 擷取視窗的高度    CGFloat windowH = [UIScreen mainScreen].bounds.size.height;    // 鍵盤結束的Frm    CGRect kbEndFrm = [noti.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];      // 擷取鍵盤結束的y值    CGFloat kbEndY = kbEndFrm.origin.y;    self.inputViewConstraint.constant = windowH - kbEndY; }

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.