卡农(Canon)是一种音乐谱曲技法,和赋格一样是复调音乐的写作技法之一,也是利用对位法的模仿技法。卡农同时也指以此种技法创作出来的音乐作品,比如巴赫的《五首卡农变奏曲》。
References:
https://zh.wikipedia.org/wiki/卡农
卡农(Canon)是一种音乐谱曲技法,和赋格一样是复调音乐的写作技法之一,也是利用对位法的模仿技法。卡农同时也指以此种技法创作出来的音乐作品,比如巴赫的《五首卡农变奏曲》。
References:
https://zh.wikipedia.org/wiki/卡农
WebSocket是一种网络传输协议,可在单个TCP连接上进行全双工通信,位于OSI模型的应用层。WebSocket协议在2011年由IETF标准化为RFC 6455,后由RFC 7936补充规范。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。
WebSocket是一种与HTTP不同的协议。两者都位于OSI模型的应用层,并且都依赖于传输层的TCP协议。 虽然它们不同,但是RFC 6455中规定:it is designed to work over HTTP ports 80 and 443 as well as to support HTTP proxies and intermediaries(WebSocket通过HTTP端口80和443进行工作,并支持HTTP代理和中介)。 为了实现兼容性,WebSocket握手使用HTTP Upgrade头从HTTP协议更改为WebSocket协议。
与HTTP不同,WebSocket提供全双工通信。
WebSockets URI的格式如下:
ws-URI = “ws:” “//” host [ “:” port ] path [ “?” query ]
wss-URI = “wss:” “//” host [ “:” port ] path [ “?” query ]
ws和wss分别对应明文和加密连接(对比HTTP和HTTPS)。
因为WebSocket被设计为和HTTP/HTTPS一起工作在80/443端口并且支持HTTP代理和网关,所以一个服务端程序同时接收HTTP和WebSocket请求。对,没错,一个Server端程序可以同时接收HTTP请求和WebSocket请求。
Demo如下:
//处理普通的HTTP请求 http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello, world!")) }) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "index.html") }) http.HandleFunc("/ws/chat", func(w http.ResponseWriter, r *http.Request) { //这里写一个读取cookie的实例是告诉大家,在处理WebSocket的handshake请求时,服务端是可以读到该域名下的cookie的,具体细节和HTTP协议规范一致。 cookie, err := r.Cookie("key") if err != nil { fmt.Println(err) } else { fmt.Println("cookie:" + cookie.String()) } //使用github.com/gorilla/websocket conn, err := upgrader.Upgrade(w, r, nil) if err != nil { fmt.Println(err) return } //... //此处conn就是一个websocket连接了,服务端可以通过conn来发送消息给客户端 })
仔细一想,你就会明白。客户端发起ws请求,第一次握手(handshake)的请求和正常的HTTP请求体的格式是遵循的同一个规范——RFC2616。所以WebSocket的第一次握手请求正好被设计为要求和正常的HTTP请求体一样,这样就解答了为什么在一个普通的HTTP Server程序只监听一个端口的情况下可以同时处理HTTP请求和WebSocket请求了。同时也请注意对客户端而言,是建立了两个连接:一个HTTP连接,一个WebSocket连接。
如果你还是不明白,那么我换另外一种说法:
客户端发起了一个请求,可能是HTTP请求也可能是WebSocket,但是他们的请求体格式是一样的。服务端可以事先和客户端约定,根据path路径区分来源,进而决定哪一个是当HTTP请求处理:返回正常HTTP Response,哪一个当WebSocket请求处理:返回Upgrade,继而持有连接实现双向通信。
摘抄部分RFC内容如下:
The handshake MUST be a valid HTTP request as specified by [RFC2616].
The method of the request MUST be GET, and the HTTP version MUST be at least 1.1.
For example, if the WebSocket URI is “ws://example.com/chat”, the first line sent should be “GET /chat HTTP/1.1”.
References:
https://datatracker.ietf.org/doc/rfc6455
https://zh.wikipedia.org/wiki/WebSocket
https://developer.mozilla.org/zh-CN/docs/Web/API/WebSockets_API/Writing_WebSocket_servers