iOS經典講解之網絡編程 TCP/UDP HTTP

一、HTTP協議的主要特點:(摘自 仰望星空 的博客)重點內容 
1. CS模式 
2. 簡單快速:只需要傳送請求方法和路徑。(常用方法有GET,HEAD,POST) 
3. 靈活:任意對象都可以,類型由Content-Type加以標記 
4. 無連接、無狀態 即每次連接只處理一個請求,對於事務處理沒有記憶能力 
http表示要通過HTTP協議來定位網絡資源;host表示合法的Internet主機域名或者IP地址;port制定一個端口號,爲空時使用缺省端口號80;abs_path指定請求資源的URI;如果URL中沒有給出abs_path,那麼當它作爲請求URI時,必須以“/”的形式給出(此過程由瀏覽器完成)。

二、TCP/UDP區別和聯繫 
1.TCP是面向連接的可靠的傳輸控制協議,UDP是面向非連接的用戶數據報協議. 
2.TCP(三次握手保證相對可靠性)可傳大量數據,速度相對比較慢,UDP一次性傳輸少量對可靠性要求不高的數據,速度比較快 
tcp一般用於音頻、視頻等數據的傳輸,資源能耗比較小,對可靠性要求不高,即使丟失一兩條數據也不會產生太大影響。

三、Socket連接和Http連接的區別 
1.http是基於socket之上的,socket是一套完成tcp和udp協議的接口 
2.HTTP協議:簡單對象訪問協議,對應於應用層 ,HTTP協議是基於TCP連接的 
3.tcp協議: 對應於傳輸層 
4.ip協議: 對應於網絡層 
TCP/IP是傳輸層協議,主要解決數據如何在網絡中傳輸;而HTTP是應用層協議,主要解決如何包裝數據。 
Socket是對TCP/IP協議的封裝,Socket本身並不是協議,而是一個調用接口(API),通過Socket,我們才能使用TCP/IP協議。 
http是短連接,客戶端向服務端發送一次請求,服務端響應後連接即斷掉;socket是長連接,一般情況下,如果服務器端或者客戶端主機down了,網絡故障,或者兩者長時間沒有數據傳輸,連接可能會斷。所以當以個socket連接中沒有數據的傳輸,爲了維持連接需要發送心跳消息。

四、三次握手的過程不再贅述,主要來了解下socket建立網絡連接的步驟 
建立socket連接至少需要一堆套接字,其中一個運行於客戶端,另一個運行於服務端(ClientSocket、ServerSocket) 
套接字建立連接的過程分爲三步:服務器監聽、客戶端請求、連接確認 
1。服務器監聽:服務器端套接字並不定位具體的客戶端套接字,而是處於等待連接的狀態,實時監控網絡狀態,等待客戶端的連接請求。 
2。客戶端請求:指客戶端的套接字提出連接請求,要連接的目標是服務器端的套接字。爲此,客戶端的套接字必須首先描述它要連接的服務器的套接字,指出服務器端套接字的地址端口號,然後就向服務器端套接字提出連接請求。 
3。連接確認:當服務器端套接字監聽到或者說接收到客戶端套接字的連接請求時,就響應客戶端套接字的請求,建立一個新的線程,把服務器端套接字的描述發給客戶端,一旦客戶端確認了此描述,雙方就正式建立連接。而服務器端套接字繼續處於監聽狀態,繼續接收其他客戶端套接字的連接請求。

五、HTTP連接最顯著的特點是客戶端發送的每次請求都需要服務器回送響應,在請求結束後,會主動釋放連接。從建立連接到關閉連接的過程稱爲“一次連接”。

/在進行編程前,先了解下AsyncSocket/

AsyncSocket是IOS下專門用於socket套接字開發的開源庫,它封裝了CFNetwork/BSD Socket.提供了異步的開發模型和簡便的開發接口。它支持TCP/UDP,支持UDP組播 
AsyncSocket支持GCD/Runloop模式 支持ARC 使用時需要增加兩個庫 CFNetwork.frame和Security.frame

六、UDP編程 
server端流程: 
1. socket創建套接字 
2. bind綁定port 
3. recv接收、send發送數據 
4. close關閉socket套接字 
client端流程 
1.socket創建套接字 
2.bind綁定port端口(可選) 
3. recv接收 send發送數據 
4. close關閉socket套機制

UDP編程涉及到 ip和字符串的轉化如下
/*綁定ip地址 */
inet_pton(AF_INET,"192.168.101.2",&addr.sin_addr);
/*把地址sin_addr轉化成字符串ipBuf*/
inet_ntop(AF_INET,&srcaddr.sin_addr,ipBuf,16);

UDP基本API 
1. 創建UDP的套接字int sd = socket(AF_INET,SOCK_DGRAM,0); 
2. 設置端口可以重用 int nOptval = 1; 
ret = setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,(const void*)&nOptval,sizeof(int));
 
3. 綁定端口 ret = bind(sd,(struct sockaddr *)&addr,addrlen); 
4. 給某個地址發送數據ssize_t sendLen = sendto(sd,res,strlen(res),0,(struct sockaddr *)&srcaddr,sizeof(srcaddr)); 
5. 接收來自sd的數據ssize_t recvLen = recvfrom(sd,&info,sizeof(info),0,(struct sockaddr *)&srcaddr,&addrlen); 
6. 關閉套接字 Close(sd);

UDP 編程示例 
server端 : 新建工程 Cococa Application 引入AsyncSocket 
引入頭文件 #import “AsyncUdpSocket.h” 以及代理 AsyncUdpSocketDelegate

  _udpSocketServer = [[AsyncUdpSocket alloc] initWithDelegate:self];
    //綁定端口 用於標識socket對象
    if(![_udpSocketServer bindToPort:5678 error:nil]){

        NSLog(@"綁定失敗");
    }

    //監聽狀態 是否有客戶端發送來的消息
    [_udpSocketServer receiveWithTimeout:-1 tag:100];

代理回調

#pragma maek- socketDelegate
//收到消息時的回調
-(BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port{

    NSLog(@"port:%d",port);
    NSString* messege = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"recieve:%@",messege);
    [sock receiveWithTimeout:-1 tag:200];
    return YES;

}

上述代碼即可實現簡單的UDPServer端

UDPClient 代碼(簡單示例) 
同樣的要聲明UDP對象 然後發送消息

NSString* messege = @"UDPClient 消息";
    NSData* data = [messege dataUsingEncoding:NSUTF8StringEncoding];
    /*
     *發送消息
     host: 制定服務器的ip地址
     port: 服務器的端口
     -1 不限時發送
     tag 對此次操作的標記
     */
    [_clientSocket sendData:data toHost:@"127.0.0.1" port:5678 withTimeout:-1 tag:0];
/*發送成功的回調方法是*/
- (void)onUdpSocket:(AsyncUdpSocket *)sock didSendDataWithTag:(long)tag{
    NSLog(@"發送成功!");
}

TCP編程 
server端流程 
1. socket創建socket套接字 
2. bind綁定port端口 
3. listen監聽端口 
4. close關閉socket套機制 
client端流程 
1.socket創建套接字 
2.bind綁定port端口(可選) 
3.connect服務器端口 
4.close關閉socket套機制

瞭解下 tcp的重傳策略: TCP用於控制數據段是否需要重傳的依據是設立重發定時器。在發送一個數據段的同時啓動一個重發定時器,AC(Ackonowlegement)就關閉該定時器,如果在定時器超時前沒有收到確認,則重傳該數據段。在選擇重發時間的過程中,TCP必須具有自適應性。它需要根據互聯網當時的通信情況,給出合適的數據重發。

TCP編程實例 
server 
1.引入頭文件 #import “AsyncSocket.h” AsyncSocketDelegate

   _tcpServer = [[AsyncSocket alloc]initWithDelegate:self];
    //服務端對應的ip地址和端口,_serverSocket負責監聽是否有客戶端接入
    //[_tcpServer acceptOnInterface:@"127.0.0.1" port:5678 error:nil];
    //服務端負責監聽是否有客戶端接入此端口,host可以缺省不寫

    [_tcpServer acceptOnPort:5678 error:nil];

2.發現有客戶端接入時 響應

-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket{

    NSLog(@"new socket host:%@ port:%d",newSocket.connectedHost,newSocket.connectedPort);
    //將新生成的socket對象加入數組中
    [_array addObject:newSocket];
    //負責不限時的監聽客戶端是否發送消息過來
    [newSocket readDataWithTimeout:-1 tag:1];

}

3 收到客戶端發送過來的消息時

-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{

    NSString *message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"receive:%@",message);
    //告訴sock,繼續監聽客戶端
    [sock readDataWithTimeout:-1 tag:2];
 }

4連接的客戶端長時間不活躍時 觸發下面的方法

- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err{
    NSLog(@"willDisconnect!");
}

5 斷開連接時

//已經斷開連接
- (void)onSocketDidDisconnect:(AsyncSocket *)sock{
    NSLog(@"%s",__FUNCTION__);//__FUNCTION__ 能夠打印出當前函數的名稱,一般用於對程序進行暴力調試時
}

client編程 
1。初始化一個AsyncSocket對象 
_clientSocket = [[AsyncSocket alloc] initWithDelegate:self]; 
2. 與指定的服務器進行連接

if (!_clientSocket) {
        //創建一個客戶端對象,並設置delegate
        _clientSocket = [[AsyncSocket alloc] initWithDelegate:self];
    }
    //先判斷是否與指定服務器連接
    if ([_clientSocket isConnected]) {
        //斷開連接
        [_clientSocket disconnect];
    }
    //與指定服務器連接
    //connectToHost 服務端的ip地址
    //port 服務端的端口:端口的值可隨意約定
    [_clientSocket connectToHost:@"127.0.0.1" onPort:5678 error:nil];

3.發送消息

 NSString *message = @"hello server!";
    //將數據轉換成data
    NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding];
    //將data發給服務器
    //data 發送的數據, timeout:-1  不限時發送, tag,對此次交互的標記
    [_clientSocket writeData:data withTimeout:-1 tag:0];

4.回調方法

//當客戶端與服務端連接成功時,調用此方法
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{
    NSLog(@"與服務器:%@ %d 相連接",host,port);
}
//消息發送成功後,調用此方法
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag{
    NSLog(@"send!");
}
發佈了60 篇原創文章 · 獲贊 65 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章