WebSocket协议初探
1.介绍
历史上在创建一个需要双向通信的web应用需要滥用服务器推送技术通过上流通知。
这样产生三个问题:
- 服务器被迫使用不同连接数量的TCP连接每个客户端。
- 这个协议很高的消耗,在每个客户端和服务端交流的数据中有HTTP header。
- 客户端被迫维持来自外部连接的响应的映射。
websocket协议以及API,通过独立TCP直接连接,用来取代HTTP轮询web页面到服务器。
类似的技术被大量web应用使用在游戏、股市行情、多用户即时通讯等,websocket基于http基础通信上保证效率和可靠之间的平衡.
2.协议
两个部分:握手和数据传输。
客户端握手:
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13
服务端握手:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Protocol: chat
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; import java.util.Base64; import java.security.MessageDigest; public class WebServer { public static void main(String[] args) throws IOException, NoSuchAlgorithmException { ServerSocket serverSocket = new ServerSocket(8888); Socket socket = serverSocket.accept(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true); String line; Map<String, String> map = new HashMap<>(); while (bufferedReader.ready()) { line = bufferedReader.readLine(); System.out.println(line); String[] head = line.split(":"); if (head.length == 2) { map.put(head[0], head[1]); } } System.out.println("--------------------"); //UUID uuid = UUID.fromString("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); String code = map.get("Sec-WebSocket-Key").substring(1) + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; MessageDigest md = MessageDigest.getInstance("SHA-1"); md.update(code.getBytes()); byte[] key = md.digest(); printWriter.write("HTTP/1.1 101 Switching Protocols\r\n"); printWriter.write("Upgrade: websocket\r\n"); printWriter.write("Connection: Upgrade\r\n"); printWriter.write("Sec-WebSocket-Accept: " + Base64.getEncoder().encodeToString(key) + " \r\n"); printWriter.write("\r\n"); printWriter.flush(); } }
描述:在服务器与客户端握手成功后,在传输中通道内的消息由一个或多个帧组成。websocket帧不需要特别的网络帧表示,一般为碎片的帧通过中间服务合并和分离。每一个帧都有关联的属性,同一个消息内有一样的属性类型。有几种类型的帧,文本帧,二进制帧,控制帧等,其它保留的表示帧方式。