后话
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
对于面试,说几句个人观点。
面试,说到底是一种考试。正如我们一直批判应试教育脱离教育的本质,为了面试学习技术也脱离了技术的初心。但考试对于人才选拔的有效性是毋庸置疑的,几千年来一直如此。除非你有实力向公司证明你足够优秀,否则,还是得乖乖准备面试。这也并不妨碍你在通过面试之后按自己的方式学习。
其实在面试准备阶段,个人的收获是很大的,我也认为这是一种不错的学习方式。首先,面试问题大部分基础而且深入,这些是平时工作的基础。就好像我们之前一直不明白学习语文的意义,但它的意义就在每天的谈话间。
所谓面试造火箭,工作拧螺丝。面试往往有更高的要求,也迫使我们更专心更深入地去学习一些知识,也何尝不是一种好事。
2.1 为什么使用 WebSocket?
我们做客户端开发时,接触最多的应用层网络协议,就是 HTTP 协议,而今天介绍的 WebSocket,下层和 HTTP 一样也是基于 TCP 协议,是一种轻量级网络通信协议,也属于应用层协议。
WebSocket 与 HTTP/2 一样,其实都是为了解决 HTTP/1.1 的一些缺陷而诞生的,而 WebSocket 针对的就是「请求-应答」这种"半双工"的模式的通信缺陷。
「请求-应答」是"半双工"的通信模式,数据的传输必须经过一次请求应答,这个完整的通信过程,通信的同一时刻数据只能在一个方向上传递。它最大的问题在于,HTTP 是一种被动的通信模式,服务端必须等待客户端请求才可以返回数据,无法主动向客户端发送数据。
这也导致在 WebSocket 出现之前,一些对实时性有要求的服务,都是基于轮询(Polling)这种简单的模式来实现。轮询就是由客户端定时发起请求,如果服务端有需要传递的数据,可以借助这个请求去响应数据。
轮询的缺点也非常明显,大量空闲的时间,其实是在反复发送无效的请求,这显然是一种资源的损耗。
虽然在之后的 HTTP/2、HTTP/3 中,针对这种半双工的缺陷新增了 Stream、Server Push 等特性,但是「请求-应答」依然是 HTTP 协议主要的通信方式。
WebSocket 协议是由 HTML5 规范定义的,原本是为了浏览器而设计的,可以避免同源的限制,浏览器可以与任意服务端通信,现代浏览器基本上都已经支持 WebSocket。
虽然 WebSocket 原本是被定义在 HTML5 中,但它也适用于移动端,尽管移动端也可以直接通过 Socket 与服务端通信,但借助 WebSocket,可以利用 80(HTTP) 或 443(HTTPS)端口通信,有效的避免一些防火墙的拦截。
WebSocket 是真正意义上的全双工模式,也就是我们俗称的「长连接」。当完成握手连接后,客户端和服务端均可以主动的发起请求,回复响应,并且两边的传输都是相互独立的。
2.2 WebSocket 的特点
WebSocket 的数据传输,是基于 TCP 协议,但是在传输之前,还有一个握手的过程,双方确认过眼神,才能够正式的传输数据。
WebSocket 的握手过程,符合其 “Web” 的特性,是利用 HTTP 本身的 “协议升级” 来实现。
在建立连接前,客户端还需要知道服务端的地址,WebSocket 并没有另辟蹊径,而是沿用了 HTTP 的 URL 格式,但协议标识符变成了 “ws” 或者 “wss”,分别表示明文和加密的 WebSocket 协议,这一点和 HTTP 与 HTTPS 的关系类似。
以下是一些 WebSocket 的 URL 例子:
ws://cxmydev.com/some/path
ws://cxmydev.com:8080/some/path
wss://cxmydev.com:443?uid=xxx
而在连接建立后,WebSocket 采用二进制帧的形式传输数据,其中常用的包括用于数据传输的数据帧 MESSAGE 以及 3 个控制帧:
-
PING:主动保活的 PING 帧;
-
PONG:收到 PING 帧后回复;
-
CLOSE:主动关闭 WebSocket 连接;
更多 WebSocket 的协议细节,可以参考《WebSocket Protocol 规范》,具体细节,有机会为什么再开单篇文章讲解。
了解这些基本知识,我们基本上就可以把 WebSocket 使用起来,并且不会掉到坑里。
我们再小结一下 WebSocket 的特性:
-
WebSocket 建立在 TCP 协议之上,对服务器端友好;
-
默认端口采用 80 或 443,握手阶段采用 HTTP 协议,不容易被防火墙屏蔽,能够通过各种 HTTP 代理服务器;
-
传输数据相比 HTTP 轻量,少了 HTTP Header,性能开销更小,通信更高效;
-
通过 MESSAGE 帧发送数据,可以发送文本或者二进制数据,如果数据过大,会被分为多个 MESSAGE 帧发送;
-
WebSocket 沿用 HTTP 的 URL,协议标识符是 “ws” 或 “wss”。
那接下来我们就看看如何利用 OkHttp 使用 WebSocket。
3.1 建立 WebSocket 连接
借助 OkHttp 可以很轻易的实现 WebSocket,它的 OkHttpClient 中,提供了 newWebSocket()
方法,可以直接建立一个 WebSocket 连接并完成通信。
fun connectionWebSockt(hostName:String,port:Int){
val httpClient = OkHttpClient.Builder()
.pingInterval(40, TimeUnit.SECONDS) // 设置 PING 帧发送间隔
.build()
val webSocketUrl = “ws:// h o s t N a m e : {hostName}: hostName:{port}”
val request = Request.Builder()
.url(webSocketUrl)
.build()
httpClient.newWebSocket(request, object:WebSocketListener(){
// …
})
}
我想熟悉 OkHttp 的朋友,对上面这段代码不会有疑问,只是 URL 换成了 “ws” 协议标识符。另外,还需要配置 pingInterval()
,这个细节后文会讲解。
调用 newWebSocket()
后,就会开始 WebSocket 连接,但是核心操作都在 WebSocketListener 这个抽象类中。
3.2 使用 WebSocketListener
WebSocketListener 是一个抽象类,其中定义了比较多的方法,借助这些方法回调,就可以完成对 WebSocket 的所有操作。
var mWebSocket : WebSocket? = null
fun connectionWebSockt(hostName:String,port:Int){
// …
httpClient.newWebSocket(request, object:WebSocketListener(){
override fun onOpen(webSocket: WebSocket, response: Response) {
super.onOpen(webSocket, response)
// WebSocket 连接建立
mWebSocket = webSocket
}
override fun onMessage(webSocket: WebSocket, text: String) {
super.onMessage(webSocket, text)
// 收到服务端发送来的 String 类型消息
}
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
super.onClosing(webSocket, code, reason)
// 收到服务端发来的 CLOSE 帧消息,准备关闭连接
}
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
super.onClosed(webSocket, code, reason)
// WebSocket 连接关闭
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
super.onFailure(webSocket, t, response)
// 出错了
}
})
}
在 WebSocketListener
的所有方法回调中,都包含了 WebSocket
类型的对象,它就是当前建立的 WebSocket 连接实体,通过它就可以向服务端发送 WebSocket 消息。
如果需要在其他时机发送消息,可以在回调 onOpen()
这个建立连接完成的时机,保存 webSocket 对象,以备后续使用。
OkHttp 中的 WebSocket
本身是一个接口,它的实现类是 RealWebSocket
,它定义了一些发送消息和关闭连接的方法:
-
send(text):发送 String 类型的消息;
-
send(bytes):发送二进制类型的消息;
-
close(code, reason):主动关闭 WebSocket 连接;
利用这些回调和 WebSocket
的方法,我们就可以完成 WebSocket 通信了。
3.3 Mock WebSocket
有时候为了方便我们测试,OkHttp 还提供了扩展的 MockWebSocket 服务,来模拟服务端。
Mock 需要添加额外的 Gradle 引用,最好和 OkHttp 版本保持一致:
api ‘com.squareup.okhttp3:okhttp:3.9.1’
api ‘com.squareup.okhttp3:mockwebserver:3.9.1’
Mock WebServer 的使用也非常简单,只需要利用 MockWebSocket 类即可。
var mMockWebSocket: MockWebServer? = null
fun mockWebSocket() {
if (mMockWebSocket != null) {
return
}
mMockWebSocket = MockWebServer()
mMockWebSocket?.enqueue(MockResponse().withWebSocketUpgrade(object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
super.onOpen(webSocket, response)
// 有客户端连接时回调
}
override fun onMessage(webSocket: WebSocket, text: String) {
super.onMessage(webSocket, text)
// 收到新消息时回调
}
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
super.onClosing(webSocket, code, reason)
// 客户端主动关闭时回调
}
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
super.onClosed(webSocket, code, reason)
// WebSocket 连接关闭
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
super.onFailure(webSocket, t, response)
// 出错了
}
}))
}
Mock WebSocket 服务端,依然需要用到我们前面讲到的 WebSocketListener
,这个就比较熟悉,不再赘述了。
之后就可以通过 mMockWebSocket
获取到这个 Mock 的服务的 IP 和端口。
var mMockWebSocket: MockWebServer? = null
fun mockWebSocket() {
if (mMockWebSocket != null) {
return
}
文末
逆水行舟不进则退,所以大家要有危机意识。
同样是干到35岁,普通人写业务代码划水,榜样们深度学习拓宽视野晋升管理。
这也是为什么大家都说35岁是程序员的门槛,很多人迈不过去,其实各行各业都是这样都会有个坎,公司永远都缺的高级人才,只用这样才能在大风大浪过后,依然闪耀不被公司淘汰不被社会淘汰。
为了帮助大家更好温习重点知识、更高效的准备面试,特别整理了《前端工程师核心知识笔记》电子稿文件。
内容包括html,css,JavaScript,ES6,计算机网络,浏览器,工程化,模块化,Node.js,框架,数据结构,性能优化,项目等等。
269页《前端大厂面试宝典》
包含了腾讯、字节跳动、小米、阿里、滴滴、美团、58、拼多多、360、新浪、搜狐等一线互联网公司面试被问到的题目,涵盖了初中级前端技术点。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
前端面试题汇总