網(wǎng)絡(luò)層次簡(jiǎn)介
網(wǎng)絡(luò)七層由下往上分別為物理層、數(shù)據(jù)鏈路層、網(wǎng)絡(luò)層已卸、傳輸層佛玄、會(huì)話層、表示層和應(yīng)用層累澡。
其中物理層梦抢、數(shù)據(jù)鏈路層和網(wǎng)絡(luò)層通常被稱(chēng)作媒體層,是網(wǎng)絡(luò)工程師所研究的對(duì)象愧哟;
傳輸層奥吩、會(huì)話層、表示層和應(yīng)用層則被稱(chēng)作主機(jī)層翅雏,是用戶所面向和關(guān)心的內(nèi)容圈驼。
- http協(xié)議對(duì)應(yīng)于應(yīng)用層
- tcp協(xié)議對(duì)應(yīng)于傳輸層
- ip協(xié)議對(duì)應(yīng)于網(wǎng)絡(luò)層
三者本質(zhì)上沒(méi)有可比性。 何況HTTP協(xié)議是基于TCP連接的望几。
TCP/IP是傳輸層協(xié)議绩脆,主要解決數(shù)據(jù)如何在網(wǎng)絡(luò)中傳輸;而HTTP是應(yīng)用層協(xié)議橄抹,主要解決如何包裝數(shù)據(jù)靴迫。
我們?cè)趥鬏敂?shù)據(jù)時(shí),可以只使用傳輸層(TCP/IP)楼誓,但是那樣的話玉锌,由于沒(méi)有應(yīng)用層,便無(wú)法識(shí)別數(shù)據(jù)內(nèi)容疟羹,如果想要使傳輸?shù)臄?shù)據(jù)有意義主守,則必須使用應(yīng)用層協(xié)議,應(yīng)用層協(xié)議很多榄融,有HTTP参淫、FTP、TELNET等等愧杯,也可以自己定義應(yīng)用層協(xié)議涎才。WEB使用HTTP作傳輸層協(xié)議,以封裝HTTP文本信息力九,然后使用TCP/IP做傳輸層協(xié)議將它發(fā)送到網(wǎng)絡(luò)上耍铜。Socket是對(duì)TCP/IP協(xié)議的封裝,Socket本身并不是協(xié)議跌前,而是一個(gè)調(diào)用接口(API)棕兼,通過(guò)Socket,我們才能使用TCP/IP協(xié)議抵乓。
一程储、HTTP協(xié)議的主要特點(diǎn):
- CS模式
- 簡(jiǎn)單快速:只需要傳送請(qǐng)求方法和路徑蹭沛。(常用方法有GET,HEAD,POST)
- 靈活:任意對(duì)象都可以,類(lèi)型由Content-Type加以標(biāo)記章鲤。
- 無(wú)連接摊灭、無(wú)狀態(tài) 即每次連接只處理一個(gè)請(qǐng)求,對(duì)于事務(wù)處理沒(méi)有記憶能力败徊。
- http表示要通過(guò)HTTP協(xié)議來(lái)定位網(wǎng)絡(luò)資源帚呼;
- host表示合法的Internet主機(jī)域名或者IP地址;
- port制定一個(gè)端口號(hào)皱蹦,為空時(shí)使用缺省端口號(hào)80煤杀;
- abs_path指定請(qǐng)求資源的URL;如果URL中沒(méi)有給出abs_path,那么當(dāng)它作為請(qǐng)求URI時(shí)沪哺,必須以“/”的形式給出(此過(guò)程由瀏覽器完成)沈自。
二、TCP/UDP區(qū)別和聯(lián)系
1.TCP是面向連接的可靠的傳輸控制協(xié)議辜妓,UDP是面向非連接的用戶數(shù)據(jù)報(bào)協(xié)議枯途。
2.TCP(三次握手保證相對(duì)可靠性)可傳大量數(shù)據(jù),速度相對(duì)比較慢籍滴,UDP一次性傳輸少量對(duì)可靠性要求不高的數(shù)據(jù)酪夷,速度比較快。
tcp一般用于音頻孽惰、視頻等數(shù)據(jù)的傳輸晚岭,資源能耗比較小,對(duì)可靠性要求不高勋功,即使丟失一兩條數(shù)據(jù)也不會(huì)產(chǎn)生太大影響坦报。
三、Socket連接和Http連接的區(qū)別
1.http是基于socket之上的狂鞋,socket是一套完成tcp和udp協(xié)議的接口
2.HTTP協(xié)議:簡(jiǎn)單對(duì)象訪問(wèn)協(xié)議片择,對(duì)應(yīng)于應(yīng)用層 ,HTTP協(xié)議是基于TCP連接的
3.tcp協(xié)議: 對(duì)應(yīng)于傳輸層
4.ip協(xié)議: 對(duì)應(yīng)于網(wǎng)絡(luò)層
- TCP/IP是傳輸層協(xié)議要销,主要解決數(shù)據(jù)如何在網(wǎng)絡(luò)中傳輸;
- HTTP是應(yīng)用層協(xié)議夏块,主要解決如何包裝數(shù)據(jù)疏咐。
- Socket是對(duì)TCP/IP協(xié)議的封裝,Socket本身并不是協(xié)議脐供,而是一個(gè)調(diào)用接口(API)浑塞,通過(guò)Socket,我們才能使用TCP/IP協(xié)議政己。
- http是短連接酌壕,客戶端向服務(wù)端發(fā)送一次請(qǐng)求,服務(wù)端響應(yīng)后連接即斷掉;
- socket是長(zhǎng)連接卵牍,一般情況下果港,如果服務(wù)器端或者客戶端主機(jī)down了,網(wǎng)絡(luò)故障糊昙,或者兩者長(zhǎng)時(shí)間沒(méi)有數(shù)據(jù)傳輸辛掠,連接可能會(huì)斷。所以當(dāng)以個(gè)socket連接中沒(méi)有數(shù)據(jù)的傳輸释牺,為了維持連接需要發(fā)送心跳消息萝衩。
四、Socket建立網(wǎng)絡(luò)連接的步驟
建立socket連接至少需要一堆套接字没咙,其中一個(gè)運(yùn)行于客戶端猩谊,另一個(gè)運(yùn)行于服務(wù)端(ClientSocket、ServerSocket)
套接字建立連接的過(guò)程分為三步:服務(wù)器監(jiān)聽(tīng)祭刚、客戶端請(qǐng)求牌捷、連接確認(rèn)
- 服務(wù)器監(jiān)聽(tīng):服務(wù)器端套接字并不定位具體的客戶端套接字,而是處于等待連接的狀態(tài)袁梗,實(shí)時(shí)監(jiān)控網(wǎng)絡(luò)狀態(tài)宜鸯,等待客戶端的連接請(qǐng)求。
- 客戶端請(qǐng)求:指客戶端的套接字提出連接請(qǐng)求遮怜,要連接的目標(biāo)是服務(wù)器端的套接字淋袖。為此,客戶端的套接字必須首先描述它要連接的服務(wù)器的套接字锯梁,指出服務(wù)器端套接字的地址和端口號(hào)即碗,然后就向服務(wù)器端套接字提出連接請(qǐng)求。
- 連接確認(rèn):當(dāng)服務(wù)器端套接字監(jiān)聽(tīng)到或者說(shuō)接收到客戶端套接字的連接請(qǐng)求時(shí)陌凳,就響應(yīng)客戶端套接字的請(qǐng)求剥懒,建立一個(gè)新的線程场仲,把服務(wù)器端套接字的描述發(fā)給客戶端稳衬,一旦客戶端確認(rèn)了此描述,雙方就正式建立連接幢炸。而服務(wù)器端套接字繼續(xù)處于監(jiān)聽(tīng)狀態(tài)充岛,繼續(xù)接收其他客戶端套接字的連接請(qǐng)求保檐。
五、HTTP特點(diǎn)
HTTP連接最顯著的特點(diǎn)是客戶端發(fā)送的每次請(qǐng)求都需要服務(wù)器回送響應(yīng)崔梗,在請(qǐng)求結(jié)束后夜只,會(huì)主動(dòng)釋放連接。從建立連接到關(guān)閉連接的過(guò)程稱(chēng)為“一次連接”蒜魄。
/在進(jìn)行編程前扔亥,先了解下AsyncSocket/
AsyncSocket是IOS下專(zhuān)門(mén)用于socket套接字開(kāi)發(fā)的開(kāi)源庫(kù)场躯,它封裝了CFNetwork/BSD Socket.提供了異步的開(kāi)發(fā)模型和簡(jiǎn)便的開(kāi)發(fā)接口。它支持TCP/UDP,支持UDP組播
AsyncSocket支持GCD/Runloop模式 支持ARC 使用時(shí)需要增加兩個(gè)庫(kù) CFNetwork.frame和Security.frame
六旅挤、UDP編程
-
server端流程:
- socket創(chuàng)建套接字
- bind綁定port
- recv接收踢关、send發(fā)送數(shù)據(jù)
- close關(guān)閉socket套接字
-
client端流程
- socket創(chuàng)建套接字
- bind綁定port端口(可選)
- recv接收 send發(fā)送數(shù)據(jù)
- close關(guān)閉socket套機(jī)制
UDP編程涉及到 ip和字符串的轉(zhuǎn)化如下
/*綁定ip地址 */
inet_pton(AF_INET,"192.168.101.2",&addr.sin_addr);
/*把地址sin_addr轉(zhuǎn)化成字符串ipBuf*/
inet_ntop(AF_INET,&srcaddr.sin_addr,ipBuf,16);
- UDP基本API
1. 創(chuàng)建UDP的套接字int sd = socket(AF_INET,SOCK_DGRAM,0);
2. 設(shè)置端口可以重用 int nOptval = 1;
ret = setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,(const void*)&nOptval,sizeof(int));
3. 綁定端口 ret = bind(sd,(struct sockaddr *)&addr,addrlen);
4. 給某個(gè)地址發(fā)送數(shù)據(jù)ssize_t sendLen = sendto(sd,res,strlen(res),0,(struct sockaddr *)&srcaddr,sizeof(srcaddr));
5. 接收來(lái)自sd的數(shù)據(jù)ssize_t recvLen = recvfrom(sd,&info,sizeof(info),0,(struct sockaddr *)&srcaddr,&addrlen);
6. 關(guān)閉套接字 Close(sd);
- UDP 編程示例
server端 : 新建工程 Cococa Application 引入AsyncSocket
引入頭文件 #import “AsyncUdpSocket.h” 以及代理 AsyncUdpSocketDelegate
_udpSocketServer = [[AsyncUdpSocket alloc] initWithDelegate:self];
//綁定端口 用于標(biāo)識(shí)socket對(duì)象
if(![_udpSocketServer bindToPort:5678 error:nil]){
NSLog(@"綁定失敗");
}
//監(jiān)聽(tīng)狀態(tài) 是否有客戶端發(fā)送來(lái)的消息
[_udpSocketServer receiveWithTimeout:-1 tag:100];
- 代理回調(diào)
#pragma maek- socketDelegate
//收到消息時(shí)的回調(diào)
-(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;
}
上述代碼即可實(shí)現(xiàn)簡(jiǎn)單的UDPServer端
- UDPClient 代碼(簡(jiǎn)單示例)
同樣的要聲明UDP對(duì)象 然后發(fā)送消息
NSString* messege = @"UDPClient 消息";
NSData* data = [messege dataUsingEncoding:NSUTF8StringEncoding];
/*
*發(fā)送消息
host: 制定服務(wù)器的ip地址
port: 服務(wù)器的端口
-1 不限時(shí)發(fā)送
tag 對(duì)此次操作的標(biāo)記
*/
[_clientSocket sendData:data toHost:@"127.0.0.1" port:5678 withTimeout:-1 tag:0];
/*發(fā)送成功的回調(diào)方法是*/
- (void)onUdpSocket:(AsyncUdpSocket *)sock didSendDataWithTag:(long)tag{
NSLog(@"發(fā)送成功!");
}
七、TCP編程
- server端流程
- socket創(chuàng)建socket套接字
- bind綁定port端口
- listen監(jiān)聽(tīng)端口
- close關(guān)閉socket套接字
- client端流程
- socket創(chuàng)建套接字
- bind綁定port端口(可選)
- connect服務(wù)器端口
- close關(guān)閉socket套接字
了解下 tcp的重傳策略: TCP用于控制數(shù)據(jù)段是否需要重傳的依據(jù)是設(shè)立重發(fā)定時(shí)器谦铃。在發(fā)送一個(gè)數(shù)據(jù)段的同時(shí)啟動(dòng)一個(gè)重發(fā)定時(shí)器耘成,AC(Ackonowlegement)就關(guān)閉該定時(shí)器,如果在定時(shí)器超時(shí)前沒(méi)有收到確認(rèn)驹闰,則重傳該數(shù)據(jù)段瘪菌。在選擇重發(fā)時(shí)間的過(guò)程中,TCP必須具有自適應(yīng)性嘹朗。它需要根據(jù)互聯(lián)網(wǎng)當(dāng)時(shí)的通信情況师妙,給出合適的數(shù)據(jù)重發(fā)。
- TCP編程實(shí)例
server編程
- 引入頭文件 #import “AsyncSocket.h” AsyncSocketDelegate
_tcpServer = [[AsyncSocket alloc]initWithDelegate:self];
//服務(wù)端對(duì)應(yīng)的ip地址和端口屹培,_serverSocket負(fù)責(zé)監(jiān)聽(tīng)是否有客戶端接入
//[_tcpServer acceptOnInterface:@"127.0.0.1" port:5678 error:nil];
//服務(wù)端負(fù)責(zé)監(jiān)聽(tīng)是否有客戶端接入此端口默穴,host可以缺省不寫(xiě)
[_tcpServer acceptOnPort:5678 error:nil];
- 發(fā)現(xiàn)有客戶端接入時(shí) 響應(yīng)
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket{
NSLog(@"new socket host:%@ port:%d",newSocket.connectedHost,newSocket.connectedPort);
//將新生成的socket對(duì)象加入數(shù)組中
[_array addObject:newSocket];
//負(fù)責(zé)不限時(shí)的監(jiān)聽(tīng)客戶端是否發(fā)送消息過(guò)來(lái)
[newSocket readDataWithTimeout:-1 tag:1];
}
- 收到客戶端發(fā)送過(guò)來(lái)的消息時(shí)
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
NSString *message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"receive:%@",message);
//告訴sock,繼續(xù)監(jiān)聽(tīng)客戶端
[sock readDataWithTimeout:-1 tag:2];
}
- 連接的客戶端長(zhǎng)時(shí)間不活躍時(shí) 觸發(fā)下面的方法
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err{
NSLog(@"willDisconnect!");
}
- 斷開(kāi)連接時(shí)
//已經(jīng)斷開(kāi)連接
- (void)onSocketDidDisconnect:(AsyncSocket *)sock{
NSLog(@"%s",__FUNCTION__);//__FUNCTION__ 能夠打印出當(dāng)前函數(shù)的名稱(chēng)褪秀,一般用于對(duì)程序進(jìn)行暴力調(diào)試時(shí)
}
- client編程
- 初始化一個(gè)AsyncSocket對(duì)象
_clientSocket = [[AsyncSocket alloc] initWithDelegate:self];
- 與指定的服務(wù)器進(jìn)行連接
if (!_clientSocket) {
//創(chuàng)建一個(gè)客戶端對(duì)象蓄诽,并設(shè)置delegate
_clientSocket = [[AsyncSocket alloc] initWithDelegate:self];
}
//先判斷是否與指定服務(wù)器連接
if ([_clientSocket isConnected]) {
//斷開(kāi)連接
[_clientSocket disconnect];
}
//與指定服務(wù)器連接
//connectToHost 服務(wù)端的ip地址
//port 服務(wù)端的端口:端口的值可隨意約定
[_clientSocket connectToHost:@"127.0.0.1" onPort:5678 error:nil];
- 發(fā)送消息
NSString *message = @"hello server!";
//將數(shù)據(jù)轉(zhuǎn)換成data
NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding];
//將data發(fā)給服務(wù)器
//data 發(fā)送的數(shù)據(jù), timeout:-1 不限時(shí)發(fā)送, tag媒吗,對(duì)此次交互的標(biāo)記
[_clientSocket writeData:data withTimeout:-1 tag:0];
- 回調(diào)方法
//當(dāng)客戶端與服務(wù)端連接成功時(shí)仑氛,調(diào)用此方法
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{
NSLog(@"與服務(wù)器:%@ %d 相連接",host,port);
}
//消息發(fā)送成功后,調(diào)用此方法
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag{
NSLog(@"send!");
}