iOS 網絡編程 TCP/UDP HTTP

1、HTTP協議的主要特色:css

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

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

3、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鏈接中沒有數據的傳輸,爲了維持鏈接須要發送心跳消息。github

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

5、HTTP鏈接最顯著的特色是客戶端發送的每次請求都須要服務器回送響應,在請求結束後,會主動釋放鏈接。從創建鏈接到關閉鏈接的過程稱爲「一次鏈接」。數組

 

AsyncSocket是IOS下專門用於socket套接字開發的開源庫,它封裝了CFNetwork/BSD Socket.提供了異步的開發模型和簡便的開發接口。它支持TCP/UDP,支持UDP組播 

AsyncSocket支持GCD/Runloop模式 支持ARC 使用時須要增長兩個庫 CFNetwork.frame和Security.framexcode

6、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!"); } 

點解此處下載 demo 
今天主要是tcp和udp的編程 明天更新 http和流媒體

 

 

iOS中的TCP,UDP,socket的學習

先貼一下以前學習過的代碼:

加入CFNetwork.framework,去github上下載AsyncSocket

TCP

- (void)viewDidLoad
{
    [super viewDidLoad];

    recvArray = [[NSMutableArray alloc] init];
    //服務端
    recvSocket = [[AsyncSocket alloc] initWithDelegate:self];
    //客戶端
    sendSocket = [[AsyncSocket alloc] initWithDelegate:self];
    
    //服務端開始等待別的客戶端的連接
    //65535     >5000
    [recvSocket acceptOnPort:5678 error:nil];
    //那麼何時接受到了連接
}

//調用這個方法就證實接受到了一個連接
- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket{
    [recvArray addObject:newSocket];
    //等待客戶端發送數據
    [newSocket readDataWithTimeout:-1 tag:100];
    //何時讀取到了數據
}

//調用這個方法就證實接受到了客戶端發送的數據
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
    NSString* str = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
    contentView.text = [NSString stringWithFormat:@"%@\n對方說:%@",contentView.text,str];
    //繼續等待服務端發送數據
    [sock readDataWithTimeout:-1 tag:100];
}

//=============

//調用這個方法的時候,就證實連接成功
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{

}

- (void)sendText:(id)sender{
    //若是沒連接,那麼去連接服務端
    if (sendSocket.isConnected == NO) {
        [sendSocket connectToHost:ipField.text onPort:5678 withTimeout:60 error:nil];
        //何時知道連接成功
    }
    
        
    NSData* data = [sendField.text dataUsingEncoding:NSUTF8StringEncoding];
    //發送數據
    [sendSocket writeData:data withTimeout:60 tag:100];
    //何時知道數據發送成功
}


//調用這個方法,就證實數據發送成功
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag{
    
}

UDP

 

- (void)viewDidLoad
{
    [super viewDidLoad];
	
    recvSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
    //綁定端口
    [recvSocket bindToPort:5888 error:nil];
    
    sendSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
    [sendSocket bindToPort:5999 error:nil];
    
    //等待接受數據
    [recvSocket receiveWithTimeout:-1 tag:100];
    //何時接受到了數據
    
}
//調用這個方法的時候證實接收到了數據
- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port{
    
    NSString* str = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
    contentView.text = [NSString stringWithFormat:@"%@\n%@:%@",contentView.text,host,str];
    [recvSocket receiveWithTimeout:-1 tag:100];
    
    return YES;
}

- (void)sendText:(id)sender{
    NSData* data = [sendField.text dataUsingEncoding:NSUTF8StringEncoding];
    //發送數據
    [sendSocket sendData:data toHost:ipField.text port:5888 withTimeout:60 tag:100];
    //何時發送成功
}

//這裏就發送成功了
- (void)onUdpSocket:(AsyncUdpSocket *)sock didSendDataWithTag:(long)tag{
    
}
首先說一下他們直接的聯繫,UDP和TCP就像聲明的一個協議,是須要傳送的東西也就是內容,而scoket就像是一個通道,用於傳送這些內容,也就是用socket來實現。
UDP:UDP是一種面向無鏈接的用戶數據報服務(user data protocol),不須要和服務器也能交互,只須要知道ip和監聽端口,不須要連接沒有目的的socket,只是將數據報投遞出去,無論接收方是否成功接收到,所以是一種不可靠的傳輸,可能會形成數據丟包,但因爲這些特徵,傳輸效率要優於TCP。

TCP:TCP是一種面向鏈接的傳輸控制協議(transform contorl protocol),必需要和服務器交互,具備高安全性,可靠性,須要和服務器進行三次握手,能根據具體網絡擁堵狀況進行延時。
Socket:Socket有兩種鏈接操做方式,面向鏈接的和麪向無鏈接的。使用UDP就是面向無鏈接的,使用TCP就是面向鏈接的。使用UDP無須要指定一個socket目的地,而是用TCP必需要指定一個socket目的地,須要進行預連接,不然鏈接不到。
socket就像是API,二UDP/TCP就是協議,使用scoket來實現內容的傳送。

 TCP的3次握手:

創建起一個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鏈接的請求,斷開過程須要通過「四次握手」(過程就不細寫了,就是服務器和客戶端交互,最終肯定斷開)

 

網絡上已經有編寫好的開源類庫GCDAsyncSocket 和GCDAsyncUdpSocket  這是GCD版的  比AsyncSocket 和AsyncUdpSocket估計要好用點 用法也很簡單,跟http很相似  只要指定服務器的ip和端口 而後再實現各類回調就行,原生態實現正在摸索。。。。。socket默認狀況下就是採用TCP協議,建立以後通訊雙方的socket會一直保持鏈接,除非手動close或由於網絡緣由close,因此,此種情況對服務器而言是有必定資源消耗的,這種模式只適應與對服務器小規模的訪問,特別是對於實時性很高的應用,如視頻直播、呼叫系統等,而http通常都是短鏈接的,一次請求完以後客戶端便會於服務端端開鏈接
   http是凌駕於socket之上的高級協議,而socket是比較底層的通信方式,只是創建了一個鏈接通道,具體上面傳輸什麼樣的數據,按照什麼格式傳輸,須要你本身定義,因此這就須要從新編寫定義服務端與客戶端的所應遵循的規定,而http已經被前人們定義使用過了

先去github的網站下載最新的包,而後先看看介紹。寫的比較詳細了

https://github.com/robbiehanson/CocoaAsyncSocket/wiki/Intro_GCDAsyncSocket

網上不少都是老版本的帖子。官方已經推出了GCDAsyncSocket來代替之前老的AsyncSocket。

個人項目是no-ARC的,這個框架只有arc的版本。因此引入GCDAsyncSocket的.h和.m文件後,修改xcode中項目的屬性。

1)targets中「build settings」中找到Compiler for c/c++/Objective-c的選項。改成Apple LLVM compiler 3.0 只要是3.0或以上就能夠;

2)在「build phases」中「compile sources」中找到GCDAsyncSocket.m,增長參數-fobj-arc;

3)引入GCDAsyncSocket所須要的框架,CFNetwork和security這兩個。

知識補充:

使用Socket進行C/S結構編程,鏈接過程

 1

服 務器端監聽某個端口是否有鏈接請求。服務器端程序處於堵塞狀態,直到客戶端向服務器端發出鏈接請求,服務器端接受請求程序才能向下運行。一旦鏈接創建起 來,經過Socket能夠得到輸入輸出流對象。藉助於輸入輸出流對象就能夠實現與客戶端的通信,最後不要忘記關閉Socket和釋放一些資源(包括:關閉 輸入輸出流)。

客戶端流程是先指定要通信的服務器IP地址、端口和採用的傳輸協議(TCP或UDP),向服務器發出鏈接請求,服務器有應答請求以後,就會創建鏈接。以後與服務器端是同樣的了。

在iOS中,客戶端Socket編程可使用的技術有三種:

1 使用NSStream。面向Objective-C語言的實現,由蘋果提供的Foundation框架提供的API;

2 使用CFStream。面向C語言的實現,由蘋果提供的Core Foundation框架提供的API;

BSD Socket。 也叫伯克利套接字(Berkeley Socket),是Unix平臺下普遍使用的Socket編程。它是面向C語言實現 的,徹底使用C編寫,使用起來比較麻煩。它是伯克利加州大學(University of California, Berkeley)的學生開發的。

在iOS中,服務器端Socket編程可使用技術有二種:

1 使用CFStream。面向C語言的實現,由蘋果提供的Core Foundation框架提供的API;

2 BSD Socket。 也叫伯克利套接字(Berkeley Socket),是Unix平臺下普遍使用的Socket編程。它是面向C語言實 現的,徹底使用C編寫的,使用起來比較麻煩。它是伯克利加州大學(University of California, Berkeley)的學生開發的。

 
相關文章
相關標籤/搜索