1愿吹、套接字(socket)概念
套接字(socket)是通信的基石钳垮,是支持TCP/IP協(xié)議的網(wǎng)絡(luò)通信的基本操作單元吭练。它是網(wǎng)絡(luò)通信過程中端點(diǎn)的抽象表示冕茅,包含進(jìn)行網(wǎng)絡(luò)通信必須的五種信息:連接使用的協(xié)議,本地主機(jī)的IP地址店茶,本地進(jìn)程的協(xié)議端口蝶棋,遠(yuǎn)地主機(jī)的IP地址,遠(yuǎn)地進(jìn)程的協(xié)議端口忽妒。
應(yīng)用層通過傳輸層進(jìn)行數(shù)據(jù)通信時(shí),TCP會(huì)遇到同時(shí)為多個(gè)應(yīng)用程序進(jìn)程提供并發(fā)服務(wù)的問題兼贸。多個(gè)TCP連接或多個(gè)應(yīng)用程序進(jìn)程可能需要通過同一個(gè) TCP協(xié)議端口傳輸數(shù)據(jù)。為了區(qū)別不同的應(yīng)用程序進(jìn)程和連接,許多計(jì)算機(jī)操作系統(tǒng)為應(yīng)用程序與TCP/IP協(xié)議交互提供了套接字(Socket)接口在跳。應(yīng)用層可以和傳輸層通過Socket接口拧篮,區(qū)分來自不同應(yīng)用程序進(jìn)程或網(wǎng)絡(luò)連接的通信,實(shí)現(xiàn)數(shù)據(jù)傳輸?shù)牟l(fā)服務(wù)螺垢。
簡單來說喧务,Socket更像是對傳輸層的簡單封裝赖歌,使的開發(fā)者可以更好的使用一些協(xié)議從而實(shí)現(xiàn)不用進(jìn)程和應(yīng)用之間的通訊。
以下是通訊示意圖
這里關(guān)于TCP/UDP協(xié)議不過多介紹功茴,TCP需要通過著名的三次握手進(jìn)行連接庐冯,不懂的朋友可以看看這篇文章->計(jì)算機(jī)網(wǎng)絡(luò)傳輸層知識(shí)點(diǎn)全覆蓋
2、iOS Socket應(yīng)用場景
Socket 使用長連接進(jìn)行通訊坎穿,凡是一個(gè)來回網(wǎng)絡(luò)請求解決不了問題的都會(huì)用socket展父。例如常見的IM類應(yīng)用、音視頻通話等玲昧。
與之對應(yīng)的是HTTP連接栖茉,HTTP連接使用的是“請求—響應(yīng)”的方式,不僅在請求時(shí)需要先建立連接孵延,而且需要客戶端向服務(wù)器發(fā)出請求后吕漂,服務(wù)器端才能回復(fù)數(shù)據(jù)。例如我們下載圖片尘应、瀏覽微信朋友圈等惶凝。
3、常用的socket類型
常用的Socket類型有兩種:流式Socket(SOCK_STREAM)和數(shù)據(jù)報(bào)式Socket(SOCK_DGRAM)菩收。
流式是一種面向連接的Socket梨睁,針對于面向連接的TCP服務(wù)應(yīng)用;
數(shù)據(jù)報(bào)式Socket是一種無連接的Socket娜饵,對應(yīng)于無連接的UDP服務(wù)應(yīng)用坡贺。
4、iOS使用的socket編程的方式有哪些?
第一種: BSD Socket
BSD Socket 是UNIX系統(tǒng)中通用的網(wǎng)絡(luò)接口箱舞,它不僅支持各種不同的網(wǎng)絡(luò)類型遍坟,而且也是一種內(nèi)部進(jìn)程之間的通信機(jī)制。而iOS系統(tǒng)其實(shí)本質(zhì)就是UNIX晴股,所以可以用愿伴,但是比較復(fù)雜。
第二種:CFSocket
CFSocket是蘋果提供給我們的使用Socket的方式电湘,但是用起來還是會(huì)不太順手隔节。當(dāng)然想使用的話,可以細(xì)細(xì)研究一下寂呛。
第三種:CocoaAsyncSocket
CocoaAsyncSocket是谷歌的開發(fā)者怎诫,基于BSD-Socket寫的一個(gè)IM框架,它給Mac和iOS提供了易于使用的贷痪、強(qiáng)大的異步套接字庫幻妓,向上封裝出簡單易用OC接口。省去了我們面向Socket以及數(shù)據(jù)流Stream等繁瑣復(fù)雜的編程劫拢。
5肉津、使用CocoaAsyncSocket
首先點(diǎn)擊上面的鏈接下載CocoaAsyncSocket的源碼强胰,把以下文件拷貝到自己的項(xiàng)目中:
我們這里只是簡單講解GCDAsyncSocket的使用,它主要用于開發(fā)TCP的連接妹沙。由于TCP是長連接偶洋,我們需要在自己的項(xiàng)目中使GCDAsyncSocket保持全局唯一性,所以我們在項(xiàng)目里聲明一個(gè)單例類 AsyncSocketManager以下是AsyncSocketManager.h的結(jié)構(gòu)
@interface AsyncSocketManager : NSObject<GCDAsyncSocketDelegate>
+(AsyncSocketManager *)shareAsyncSocket;
@property (nonatomic,strong) GCDAsyncSocket *asyncSocket;
@property (nonatomic,copy) NSString *socketHost;//注意是ip地址初烘,并不是DNS域名
@property (nonatomic,assign) UInt16 socketPort;
@property (nonatomic,strong) NSTimer *connectTimer;
-(void)socketConnectHost;//連接socket
-(void)cutoffSocket;//斷開socket
@end
5.1 Socket的初始化連接
-(void)socketConnectHost{
//創(chuàng)建socket并指定代理對象為self,代理隊(duì)列必須為主隊(duì)列
self.asyncSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *error;
[self.asyncSocket connectToHost:self.socketHost onPort:self.socketPort error:&error];
}
連接成功的代理方法
//連接成功的回調(diào)
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{
[self addTimer];// 連接成功開啟定時(shí)器
[self.asyncSocket readDataWithTimeout:- 1 tag:0];// 連接后,可讀取服務(wù)端的數(shù)據(jù)
}
讀取數(shù)據(jù)成功后的代理涡真,連接成功之后需要開啟一個(gè)定時(shí)器規(guī)定一定的時(shí)間后需要給服務(wù)器發(fā)送心跳包,用于檢測是否服務(wù)器在線
// 添加定時(shí)器
- (void)addTimer
{
// 長連接定時(shí)器
self.connectTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(longConnectToSocket) userInfo:nil repeats:YES];
// 把定時(shí)器添加到當(dāng)前運(yùn)行循環(huán),并且調(diào)為通用模式
[[NSRunLoop currentRunLoop] addTimer:self.connectTimer forMode:NSRunLoopCommonModes];
}
/** 注意:
心跳連接中發(fā)送給服務(wù)端的數(shù)據(jù)只是作為測試代碼,根據(jù)你們公司需求,
或者和后臺(tái)商定好心跳包的數(shù)據(jù)以及發(fā)送心跳的時(shí)間間隔.
服務(wù)端也需要有對應(yīng)的心跳檢測,以此檢測客戶端是否在線.
*/
-(void)longConnectToSocket{
NSLog(@"我發(fā)送了一次心跳包");
NSString *longConnect = @"longConnect";//需要和服務(wù)器商量好
NSData *dataStream = [longConnect dataUsingEncoding:NSUTF8StringEncoding];
[self.asyncSocket writeData:dataStream withTimeout:1 tag:1];
}
/**
讀取數(shù)據(jù)成功的回調(diào)
@param sock 客戶端socket
@param data 讀取到的數(shù)據(jù)
@param tag 本次讀取的標(biāo)記
*/
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
NSString *text = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"讀取的數(shù)據(jù)==%@",text);
// 讀取到服務(wù)端數(shù)據(jù)值后,能再次讀取
[self.asyncSocket readDataWithTimeout:- 1 tag:0];//這里還需要進(jìn)行讀取操作肾筐,否則只能讀取一次
}
5.2 斷開連接
sokect斷開連接時(shí),需要清空代理和客戶端本身的socket.
[self.asyncSocket disconnect];//斷開連接
/**
客戶端socket斷開后的回調(diào)
@param sock 客戶端socket
@param err 錯(cuò)誤描述
*/
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{
self.asyncSocket.delegate = nil;
self.asyncSocket = nil;
[self.connectTimer invalidate];
}
6哆料、總結(jié)
CocoaAsyncSocket 是前人幫我們封裝好的Socket,如果你想了解的更深的話也可以去了解Apple提供的BSD Socket吗铐、CFSocket等接口东亦。
Socket都是用于應(yīng)用之間進(jìn)行與服務(wù)器之間需要長時(shí)間的連接,需要時(shí)刻的保持連接唬渗,已到達(dá)數(shù)據(jù)時(shí)效性典阵。比較常用的就是IM類的應(yīng)用,不過每一個(gè)IM類的應(yīng)用企業(yè)都是自己封裝的Socket以適應(yīng)自己公司的不同業(yè)務(wù)功能需求镊逝。
雖然市面上有非常多的各種各樣的Socket框架可以用壮啊,但我認(rèn)為作為一名開發(fā)者,我們有責(zé)任去搞懂Socket通訊的原理撑蒜,至少是需要了解歹啼。
好了,這篇文章就到這里啦~~~
我最近在逼著自己寫文章座菠,以前總是認(rèn)為像這樣的文章網(wǎng)上到處都是而且有一些大神寫的非常好狸眼,so,我干嘛要寫呢……不過最后想想還是認(rèn)為有必要自己記錄下來浴滴,別人的永遠(yuǎn)是別人的拓萌,只有自己消化了,理解了升略,那才能是你自己的東西微王!