关于我和WebSocket的缘:我从大二在计算机网络课上听老师讲过之后,第一次使用就到了毕业之后的第一份工作。直到最近换了工作,到了一家是含有IM社交聊天功能的app的时候,我觉得我现在可以谈谈我对WebSocket/Socket的一些看法了。要想做IM聊天app,就不得不理解WebSocket和Socket的原理了,听我一一道来。
目录
1.WebSocket使用场景
2.WebSocket诞生由来
3.谈谈WebSocket协议原理
4.WebSocket和Socket的区别与联系
5.iOS平台有哪些WebSocket和Socket的开源框架
6.iOS平台如何实现WebSocket协议
一.WebSocket的使用场景
1.社交聊天
2.弹幕
说到这里,大家一定里面想到了A站和B站了。确实,他们的弹幕一直是一种特色。而且弹幕对于一个视频来说,很可能弹幕才是精华。发弹幕需要实时显示,也需要和聊天一样,需要即时。
3.多玩家游戏
4.协同编辑
现在很多开源项目都是分散在世界各地的开发者一起协同开发,此时就会用到版本控制系统,比如Git,SVN去合并冲突。但是如果有一份文档,支持多人实时在线协同编辑,那么此时就会用到比如WebSocket了,它可以保证各个编辑者都在编辑同一个文档,此时不需要用到Git,SVN这些版本控制,因为在协同编辑界面就会实时看到对方编辑了什么,谁在修改哪些段落和文字。
5.股票基金实时报价
6.体育实况更新
全世界的球迷,体育爱好者特别多,当然大家在关心自己喜欢的体育活动的时候,比赛实时的赛况是他们最最关心的事情。这类新闻中最好的体验就是利用Websocket达到实时的更新!
7.视频会议/聊天
8.基于位置的应用
越来越多的开发者借用移动设备的GPS功能来实现他们基于位置的网络应用。如果你一直记录用户的位置(比如运行应用来记录运动轨迹),你可以收集到更加细致化的数据。
9.在线教育
在线教育近几年也发展迅速。优点很多,免去了场地的限制,能让名师的资源合理的分配给全国各地想要学习知识的同学手上,Websocket是个不错的选择,可以视频聊天、即时聊天以及其与别人合作一起在网上讨论问题…
10.智能家居
这也是我一毕业加入的一个伟大的物联网智能家居的公司。考虑到家里的智能设备的状态必须需要实时的展现在手机app客户端上,毫无疑问选择了
Websocket。
11.总结
从上面我列举的这些场景来看,一个共同点就是,高实时性!
二.WebSocket诞生由来
1.最开始的轮询Polling阶段
2.改进版的长轮询Longpolling阶段
3.WebSocket诞生
现在急需的需求是能支持客户端和服务器端的双向通信,而且协议的头部又没有HTTP的Header那么大,于是,Websocket就诞生了!
上图就是Websocket和Polling的区别,从图中可以看到Polling里面客户端发送了好多Request,而下图,只有一个Upgrade,非常简洁高效。至于消耗方面的比较就要看下图了
上图中,我们先看蓝色的柱状图,是Polling轮询消耗的流量,
UsecaseA:1,000clientspollingeverysecond:Networkthroughputis(871x1,000)=871,000bytes=6,968,000bitspersecond(6.6Mbps)
UsecaseB:10,000clientspollingeverysecond:Networkthroughputis(871x10,000)=8,710,000bytes=69,680,000bitspersecond(66Mbps)
UsecaseC:100,000clientspollingevery1second:Networkthroughputis(871x100,000)=87,100,000bytes=696,800,000bitspersecond(665Mbps)
而Websocket的Frame是justtwobytesofoverheadinsteadof871,仅仅用2个字节就代替了轮询的871字节!
UsecaseA:1,000clientsreceive1messagepersecond:Networkthroughputis(2x1,000)=2,000bytes=16,000bitspersecond(0.015Mbps)
UsecaseB:10,000clientsreceive1messagepersecond:Networkthroughputis(2x10,000)=20,000bytes=160,000bitspersecond(0.153Mbps)
UsecaseC:100,000clientsreceive1messagepersecond:Networkthroughputis(2x100,000)=200,000bytes=1,600,000bitspersecond(1.526Mbps)
相同的每秒客户端轮询的次数,当次数高达10W/s的高频率次数的时候,Polling轮询需要消耗665Mbps,而Websocket仅仅只花费了1.526Mbps,将近435倍!!
三.谈谈WebSocket协议原理
Websocket是应用层第七层上的一个应用层协议,它必须依赖HTTP协议进行一次握手,握手成功后,数据就直接从TCP通道传输,与HTTP无关了。
Websocket的数据传输是frame形式传输的,比如会将一条消息分为几个frame,按照先后顺序传输出去。这样做会有几个好处:
1大数据的传输可以分片传输,不用考虑到数据大小导致的长度标志位不足够的情况。
四.WebSocket和Socket的区别与联系
首先,
Socket其实并不是一个协议。它工作在OSI模型会话层(第5层),是为了方便大家直接使用更底层协议(一般是TCP或UDP)而存在的一个抽象层。Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API)。
Socket通常也称作”套接字”,用于描述IP地址和端口,是一个通信链的句柄。网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket,一个Socket由一个IP地址和一个端口号唯一确定。应用程序通常通过”套接字”向网络发出请求或者应答网络请求。
Socket在通讯过程中,服务端监听某个端口是否有连接请求,客户端向服务端发送连接请求,服务端收到连接请求向客户端发出接收消息,这样一个连接就建立起来了。客户端和服务端也都可以相互发送消息与对方进行通讯,直到双方连接断开。
所以基于WebSocket和基于Socket都可以开发出IM社交聊天类的app
五.iOS平台有哪些WebSocket和Socket的开源框架
Socket开源框架有:CocoaAsyncSocket,socketio/socket.io-client-swift
WebSocket开源框架有:facebook/SocketRocket,tidwall/SwiftWebSocket
六.iOS平台如何实现WebSocket协议
Talkischeap。Showmethecode——LinusTorvalds
我们今天来看看facebook/SocketRocket的实现方法
首先这是SRWebSocket定义的一些成员变量
@property(nonatomic,weak)iddelegate;
/**
Adispatchqueueforschedulingthedelegatecalls.Thequeuedoesn'tneedbeaserialqueue.
If`nil`and`delegateOperationQueue`is`nil`,thesocketusesmainqueueforperformingalldelegatemethodcalls.
*/
@property(nonatomic,strong)dispatch_queue_tdelegateDispatchQueue;
Anoperationqueueforschedulingthedelegatecalls.
@property(nonatomic,strong)NSOperationQueue*delegateOperationQueue;
@property(nonatomic,readonly)SRReadyStatereadyState;
@property(nonatomic,readonly,retain)NSURL*url;
@property(nonatomic,readonly)CFHTTPMessageRefreceivedHTTPHeaders;
//Optionalarrayofcookies(NSHTTPCookieobjects)toapplytotheconnections
@property(nonatomic,copy)NSArray*requestCookies;
//Thisreturnsthenegotiatedprotocol.
//Itwillbeniluntilafterthehandshakecompletes.
@property(nonatomic,readonly,copy)NSString*protocol;
下面这些是SRWebSocket的一些方法
//ProtocolsshouldbeanarrayofstringsthatturnintoSec-WebSocket-Protocol.
-(instancetype)initWithURLRequest:(NSURLRequest*)request;
-(instancetype)initWithURLRequest:(NSURLRequest*)requestprotocols:(NSArray*)protocols;
-(instancetype)initWithURLRequest:(NSURLRequest*)requestprotocols:(NSArray*)protocolsallowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;
//Somehelperconstructors.
-(instancetype)initWithURL:(NSURL*)url;
-(instancetype)initWithURL:(NSURL*)urlprotocols:(NSArray*)protocols;
-(instancetype)initWithURL:(NSURL*)urlprotocols:(NSArray*)protocolsallowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;
//Bydefault,itwillscheduleitselfon+[NSRunLoopSR_networkRunLoop]usingdefaultModes.
-(void)scheduleInRunLoop:(NSRunLoop*)aRunLoopforMode:(NSString*)mode;
-(void)unscheduleFromRunLoop:(NSRunLoop*)aRunLoopforMode:(NSString*)mode;
//SRWebSocketsareintendedforone-time-useonly.Openshouldbecalledonceandonlyonce.
-(void)open;
-(void)close;
-(void)closeWithCode:(NSInteger)codereason:(NSString*)reason;
///--------------------------------------
#pragmamarkSend
//下面是4个发送的方法
SendaUTF-8stringorbinarydatatotheserver.
@parammessageUTF-8StringorDatatosend.
@deprecatedPleaseuse`sendString:`or`sendData`instead.
-(void)send:(id)message__attribute__((deprecated("Pleaseuse`sendString:`or`sendData`instead.")));
-(void)sendString:(NSString*)string;
-(void)sendData:(NSData*)data;
-(void)sendPing:(NSData*)data;
@end
对应5种状态的代理方法
#pragmamark-SRWebSocketDelegate
@protocolSRWebSocketDelegate
-(void)webSocket:(SRWebSocket*)webSocketdidReceiveMessage:(id)message;
@optional
-(void)webSocketDidOpen:(SRWebSocket*)webSocket;
-(void)webSocket:(SRWebSocket*)webSocketdidFailWithError:(NSError*)error;
-(void)webSocket:(SRWebSocket*)webSocketdidCloseWithCode:(NSInteger)codereason:(NSString*)reasonwasClean:(BOOL)wasClean;
-(void)webSocket:(SRWebSocket*)webSocketdidReceivePong:(NSData*)pongPayload;
//ReturnYEStoconvertmessagessentasTexttoanNSString.ReturnNOtoskipNSData->NSStringconversionforTextmessages.DefaultstoYES.
-(BOOL)webSocketShouldConvertTextFrameToString:(SRWebSocket*)webSocket;
didReceiveMessage方法是必须实现的,用来接收消息的。
下面4个did方法分别对应着Open,Fail,Close,ReceivePong不同状态的代理方法
方法就上面这些了,我们实际来看看代码怎么写
先是初始化Websocket连接,注意此处ws://或者wss://连接有且最多只能有一个,这个是Websocket协议规定的
self.ws=[[SRWebSocketalloc]initWithURLRequest:[NSURLRequestrequestWithURL:[NSURLURLWithString:[NSStringstringWithFormat:@"%@://%@:%zd/ws",serverProto,serverIP,serverPort]]]];
self.ws.delegate=delegate;
[self.wsopen];
发送消息
[self.wssend:message];
接收消息以及其他3个代理方法
//这个就是接受消息的代理方法了,这里接受服务器返回的数据,方法里面就应该写处理数据,存储数据的方法了。
-(void)webSocket:(SRWebSocket*)webSocketdidReceiveMessage:(id)message
{
NSDictionary*data=[NetworkUtilsdecodeData:message];
if(!data)
return;
}
-(void)webSocketDidOpen:(SRWebSocket*)webSocket
//Open=silentping
[self.wsreceivedPing];
//这是关闭Websocket的代理方法
-(void)webSocket:(SRWebSocket*)webSocketdidCloseWithCode:(NSInteger)codereason:(NSString*)reasonwasClean:(BOOL)wasClean
[selffailedConnection:NSLS(Disconnected)];
//这里是连接Websocket失败的方法,这里面一般都会写重连的方法
-(void)webSocket:(SRWebSocket*)webSocketdidFailWithError:(NSError*)error