首先理一下網(wǎng)絡(luò)通訊三要素
- IP地址(主機(jī)名)
- 網(wǎng)絡(luò)設(shè)備標(biāo)示
- 本地回環(huán)地址:127.0.0.1 主機(jī)名:localHost
- 端口號(hào)
- 用于標(biāo)識(shí)進(jìn)程的邏輯地址鸥滨,不同進(jìn)程的標(biāo)示
- 有效端口:0 ~ 65535
- 其中 0 ~ 1024 由系統(tǒng)使用或者保留端口
- 傳輸協(xié)議(通訊的規(guī)則)
- TCP
- UDP
1.Socket到底是什么東西烦感?以下這幅流程圖表示運(yùn)行過(guò)程
屏幕快照 2018-07-10 下午5.43.01.png
總結(jié):socket其實(shí)就是:用來(lái)操作某個(gè)IP上的某個(gè)端口來(lái)達(dá)到點(diǎn)對(duì)點(diǎn)通訊效果丛忆,本身就是一個(gè)抽象的東東鲁猩,通訊就是通過(guò)socket來(lái)交互,可以面向TCP和UDP兩種連接
TCP 采用三次握手來(lái)創(chuàng)建連接,是可靠連接,是長(zhǎng)連接
分手是四次分手雏婶,客戶端向服務(wù)器發(fā)送分手,會(huì)經(jīng)過(guò)兩次分手白指;服務(wù)器端向客戶端發(fā)送分手留晚,也會(huì)經(jīng)過(guò)兩次分手UDP 是不可靠連接,發(fā)送的東西是不管到達(dá)的
2.Socket終端使用案例:
- 新建兩個(gè)終端控制臺(tái)
- 第一臺(tái)輸入命令: nc -l 6969 (開(kāi)啟nc命令告嘲,使用6969端口倔丈,充當(dāng)服務(wù)器端)
- 第二臺(tái)輸入命令:nc 127.0.0.1 6969 (兩臺(tái)的端口保持一致,充當(dāng)客戶端)
- 最后兩臺(tái)終端就可以實(shí)現(xiàn)通訊了状蜗,可以相互發(fā)送消息
以上案列采用的 “nc”命令,可以用來(lái)檢測(cè)本地GCP連接
3.Socket 代碼實(shí)際運(yùn)用
socket是成雙成對(duì)的出現(xiàn)
- 導(dǎo)入框架
#import <sys/socket.h>
#import <arpa/inet.h>
#import <netinet/in.h>
- 定義端口號(hào)和ip
static const char *serve_ip = "127.0.0.1"; //ip
static const short serve_port = 6969; //port
3.定義socket
@property (nonatomic,assign) int clientSockt; //定義socket
/** */
@property (nonatomic,strong) UITextField *msgTextFiled; //創(chuàng)建消息文本
/** */
@property (nonatomic,strong) NSThread *thread;
- 創(chuàng)建socket , 連接服務(wù)器端
//創(chuàng)建socket
-(void)initSockt{
/**
參數(shù)
domain : 協(xié)議域 ,AF_INET ->IPV4
type: Socket 類型,SOCK_STREAM(流/TCP)/SOCK_DGRAM(報(bào)文/UDP)
protocol: IPPROTO_TCP(如果寫(xiě)0鹉动,那么就會(huì)自動(dòng)選擇)
返回值
socket
*/
_clientSockt = socket(AF_INET, SOCK_STREAM, 0);
//連接IP地址和服務(wù)端
struct sockaddr_in serverAddr;
//賦值長(zhǎng)度
serverAddr.sin_len = sizeof(serverAddr);
serverAddr.sin_family = AF_INET;
//IP地址
inet_aton(serve_ip, &serverAddr.sin_addr);
//端口號(hào)
serverAddr.sin_port = htons(serve_port);
int connetResult = connect(_clientSockt,(const struct sockaddr *)&serverAddr, sizeof(serverAddr));
if (connetResult == 0) {
NSLog(@"連接成功");
//開(kāi)啟一個(gè)線程模擬
self.thread = [[NSThread alloc]initWithTarget:self selector:@selector(receveAction) object:nil];
[self.thread start];
}
else{
NSLog(@"連接失敗 %d",connetResult);
return;
}
}
- 接收消息
-(void)receveAction{
/**
參數(shù)
1.客戶端socket
2.接受內(nèi)容緩沖區(qū)地址
3.接受內(nèi)容緩存區(qū)長(zhǎng)度
4.接受方式轧坎,0表示阻塞,必須等待服務(wù)器返回?cái)?shù)據(jù)
返回值
如果超過(guò)泽示,則返回讀入的字節(jié)數(shù)缸血,失敗則返回SOCKET_ERROR
*/
while (1) {
uint8_t buffer[1024];//空間,準(zhǔn)備數(shù)據(jù)
size_t recvlen = recv(_clientSockt, buffer, sizeof(buffer), 0);//等待著獲的數(shù)據(jù)
NSLog(@"接受到多少數(shù)據(jù)%ld",recvlen);
//解析數(shù)據(jù)(獲取到的服務(wù)器數(shù)據(jù))
NSData *date = [NSData dataWithBytes:buffer length:recvlen];
NSString *str = [[NSString alloc]initWithData:date encoding:NSUTF8StringEncoding];
NSLog(@"接收到的內(nèi)容:%@",str);
}
}
- 接下來(lái)械筛,我們來(lái)利用終端來(lái)模擬服務(wù)器捎泻,向我們的app發(fā)送消息,上面我們已經(jīng)說(shuō)過(guò),怎么使用終端來(lái)建立一個(gè)服務(wù)器端口埋哟,建立連接笆豁。好了,這個(gè)時(shí)候赤赊,把我們的App跑起來(lái)吧闯狱,看看有什么神奇的效果
在終端輸入:
nc -l 6969 //這是建立服務(wù)器端口,再把APP跑起來(lái)抛计,這時(shí)應(yīng)該就會(huì)發(fā)現(xiàn)控制臺(tái)打印“連接成功”哄孤。這時(shí),就可以任意發(fā)送消息了
123456 //這是發(fā)送的消息
1213131SAS //這是發(fā)送的消息
這時(shí)吹截,在app控制臺(tái)打印出效果
2018-07-12 11:26:12.459423+0800 Socket[1232:116451] 連接成功
2018-07-12 11:26:23.504420+0800 Socket[1232:116570] 接受到多少數(shù)據(jù)7
2018-07-12 11:26:23.504659+0800 Socket[1232:116570] 接收到的內(nèi)容:123456
2018-07-12 11:26:26.944561+0800 Socket[1232:116570] 接受到多少數(shù)據(jù)11
2018-07-12 11:26:26.944768+0800 Socket[1232:116570] 接收到的內(nèi)容:1213131SAS
ok,一切盡在不言中
- 接受消息瘦陈,我們這邊已經(jīng)搞定,那么接下來(lái)我們?cè)賮?lái)搞定發(fā)送消息
首先創(chuàng)建一個(gè)按鈕波俄、一個(gè)輸入文本框晨逝,點(diǎn)擊按鈕,發(fā)送文本框內(nèi)的字符串弟断,調(diào)用以下方法咏花,實(shí)現(xiàn)發(fā)送
-(void)sendAction:(NSString *)msg{
//3.發(fā)送數(shù)據(jù)
/**
參數(shù)
1.客戶端socket
2.發(fā)送內(nèi)容地址(指針)
3.發(fā)送內(nèi)容長(zhǎng)度
4.發(fā)送內(nèi)容標(biāo)志,一般為0
返回值
如果成功,則返回發(fā)送的字節(jié)數(shù)昏翰,失敗則返回SOCKET_ERROR
*/
//void * 表示萬(wàn)能指針(以下兩種都可以)
// NSString *msg = @"hello";
// const char * msg = @"hello";
ssize_t sendLen = send(_clientSockt, msg.UTF8String, strlen(msg.UTF8String), 0);
NSLog(@"發(fā)送了 %ld字節(jié)",sendLen);
}
發(fā)送成功后苍匆,這時(shí)在終端上就顯示出了,剛剛發(fā)送的消息
- 正所謂棚菊,有始就有終浸踩,我們還剩下最后一步,那就是斷開(kāi)連接统求,如果不用了检碗,就得把鏈接斷開(kāi),節(jié)約資源
-(void)disConnection{
//關(guān)閉數(shù)據(jù)連接
[self.thread cancel];
close(_clientSockt);
}
4. socket 實(shí)際運(yùn)用
是實(shí)際開(kāi)發(fā)過(guò)程中码邻,對(duì)于一般的小型聊天折剃,都采用“環(huán)信、融云像屋、LeanClould”,但是對(duì)于主供消息聊天的就不可能采用這些三方公司的怕犁,因?yàn)閮r(jià)錢(qián)太高,所以就會(huì)引用一些三方框架:
- 基本socket 原生己莺, CocoaAsyncSocket
- 就是BSD Socket奏甫,處理了很多協(xié)議
- 是基于傳輸層封裝的一套框架
- 基于WebSocket , SocketRocket,SRWebSocket
- 用于瀏覽器的長(zhǎng)連接協(xié)議
- 基于MQIT, MQTTKit 凌受、MQTTClient
- 適用訂閱的消息機(jī)制,列如:滴滴打車(chē)地圖上的小車(chē)軌跡
- 適用于物聯(lián)網(wǎng)阵子,傳輸層更小,低帶寬胜蛉,性能更優(yōu)
- 位于應(yīng)用層的聊天協(xié)議
- 基于XMPP,XMPPFrameWork
- 缺點(diǎn)挠进,是基于XML的數(shù)據(jù)來(lái)傳輸?shù)?/li>
- 位于應(yīng)用層的聊天協(xié)議