一、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是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!"); }
點解此處下載 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連接的請求,斷開過程需要經過“四次握手”(過程就不細寫了,就是服務器和客戶端交互,最終確定斷開)
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結構編程,連接過程
服 務器端監聽某個端口是否有連接請求。服務器端程序處於堵塞狀態,直到客戶端向服務器端發出連接請求,服務器端接受請求程序才能向下運行。一旦連接建立起 來,通過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)的學生開發的。

