271 lines
5.1 KiB
Markdown
271 lines
5.1 KiB
Markdown
# WebSocket
|
||
|
||
## 目录
|
||
- [WebSocket协议](#websocket协议)
|
||
- [WebSocket与HTTP对比](#websocket与http对比)
|
||
- [WebSocket实现](#websocket实现)
|
||
- [心跳机制](#心跳机制)
|
||
- [重连机制](#重连机制)
|
||
- [WebSocket最佳实践](#websocket最佳实践)
|
||
- [面试常见问题](#面试常见问题)
|
||
|
||
---
|
||
|
||
## WebSocket协议
|
||
|
||
### WebSocket 简介
|
||
|
||
```java
|
||
// WebSocket:全双工通信协议
|
||
// - 客户端和服务器可以同时发送数据
|
||
// - 基于 TCP
|
||
// - 低延迟
|
||
// - 适合实时通信
|
||
```
|
||
|
||
### WebSocket 握手
|
||
|
||
```java
|
||
// WebSocket 握手过程
|
||
// 1. 客户端发送 HTTP 请求,包含 Upgrade 头
|
||
GET /chat HTTP/1.1
|
||
Upgrade: websocket
|
||
Connection: Upgrade
|
||
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
|
||
|
||
// 2. 服务器响应,同意升级
|
||
HTTP/1.1 101 Switching Protocols
|
||
Upgrade: websocket
|
||
Connection: Upgrade
|
||
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
|
||
```
|
||
|
||
---
|
||
|
||
## WebSocket与HTTP对比
|
||
|
||
### 对比表
|
||
|
||
| 特性 | HTTP | WebSocket |
|
||
|------|------|-----------|
|
||
| 通信方式 | 请求-响应 | 全双工 |
|
||
| 连接 | 短连接 | 长连接 |
|
||
| 开销 | 每次请求都有头部 | 连接建立后开销小 |
|
||
| 实时性 | 低 | 高 |
|
||
| 适用场景 | 一般请求 | 实时通信 |
|
||
|
||
### 使用场景
|
||
|
||
```java
|
||
// WebSocket 适用场景:
|
||
// 1. 实时聊天
|
||
// 2. 实时推送
|
||
// 3. 游戏
|
||
// 4. 股票行情
|
||
|
||
// HTTP 适用场景:
|
||
// 1. 一般 API 请求
|
||
// 2. 文件下载
|
||
// 3. 网页浏览
|
||
```
|
||
|
||
---
|
||
|
||
## WebSocket实现
|
||
|
||
### 使用 OkHttp WebSocket
|
||
|
||
```java
|
||
// 创建 WebSocket
|
||
Request request = new Request.Builder()
|
||
.url("ws://echo.websocket.org")
|
||
.build();
|
||
|
||
WebSocket webSocket = client.newWebSocket(request, new WebSocketListener() {
|
||
@Override
|
||
public void onOpen(WebSocket webSocket, Response response) {
|
||
// 连接打开
|
||
webSocket.send("Hello");
|
||
}
|
||
|
||
@Override
|
||
public void onMessage(WebSocket webSocket, String text) {
|
||
// 接收文本消息
|
||
}
|
||
|
||
@Override
|
||
public void onMessage(WebSocket webSocket, ByteString bytes) {
|
||
// 接收二进制消息
|
||
}
|
||
|
||
@Override
|
||
public void onClosing(WebSocket webSocket, int code, String reason) {
|
||
// 正在关闭
|
||
webSocket.close(1000, null);
|
||
}
|
||
|
||
@Override
|
||
public void onClosed(WebSocket webSocket, int code, String reason) {
|
||
// 已关闭
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
|
||
// 连接失败
|
||
}
|
||
});
|
||
|
||
// 发送消息
|
||
webSocket.send("Hello");
|
||
webSocket.send(ByteString.of("Hello".getBytes()));
|
||
|
||
// 关闭连接
|
||
webSocket.close(1000, "Normal closure");
|
||
```
|
||
|
||
---
|
||
|
||
## 心跳机制
|
||
|
||
### 心跳实现
|
||
|
||
```java
|
||
// 心跳:保持连接活跃
|
||
private Handler heartbeatHandler = new Handler();
|
||
private Runnable heartbeatRunnable = new Runnable() {
|
||
@Override
|
||
public void run() {
|
||
if (webSocket != null) {
|
||
webSocket.send("ping");
|
||
heartbeatHandler.postDelayed(this, 30000); // 30秒发送一次
|
||
}
|
||
}
|
||
};
|
||
|
||
// 启动心跳
|
||
heartbeatHandler.post(heartbeatRunnable);
|
||
|
||
// 停止心跳
|
||
heartbeatHandler.removeCallbacks(heartbeatRunnable);
|
||
```
|
||
|
||
---
|
||
|
||
## 重连机制
|
||
|
||
### 重连实现
|
||
|
||
```java
|
||
// 重连机制
|
||
private void reconnect() {
|
||
if (isConnecting || isConnected) {
|
||
return;
|
||
}
|
||
|
||
isConnecting = true;
|
||
new Handler().postDelayed(() -> {
|
||
connect();
|
||
}, reconnectDelay);
|
||
|
||
reconnectDelay = Math.min(reconnectDelay * 2, MAX_RECONNECT_DELAY);
|
||
}
|
||
|
||
// 连接失败时重连
|
||
@Override
|
||
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
|
||
isConnected = false;
|
||
isConnecting = false;
|
||
reconnect();
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## WebSocket最佳实践
|
||
|
||
### 1. 管理连接状态
|
||
|
||
```java
|
||
// 管理连接状态
|
||
private boolean isConnected = false;
|
||
private boolean isConnecting = false;
|
||
|
||
// 检查连接状态
|
||
if (!isConnected) {
|
||
connect();
|
||
}
|
||
```
|
||
|
||
### 2. 处理消息队列
|
||
|
||
```java
|
||
// 消息队列:连接断开时缓存消息
|
||
private Queue<String> messageQueue = new LinkedList<>();
|
||
|
||
// 发送消息
|
||
public void sendMessage(String message) {
|
||
if (isConnected) {
|
||
webSocket.send(message);
|
||
} else {
|
||
messageQueue.offer(message);
|
||
}
|
||
}
|
||
|
||
// 连接成功后发送缓存消息
|
||
@Override
|
||
public void onOpen(WebSocket webSocket, Response response) {
|
||
isConnected = true;
|
||
while (!messageQueue.isEmpty()) {
|
||
webSocket.send(messageQueue.poll());
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. 及时关闭连接
|
||
|
||
```java
|
||
@Override
|
||
protected void onDestroy() {
|
||
super.onDestroy();
|
||
if (webSocket != null) {
|
||
webSocket.close(1000, "Activity destroyed");
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 面试常见问题
|
||
|
||
### Q1: WebSocket 和 HTTP 的区别?
|
||
|
||
**答案:**
|
||
- **HTTP**:请求-响应模式,短连接
|
||
- **WebSocket**:全双工通信,长连接,实时性更好
|
||
|
||
### Q2: WebSocket 的使用场景?
|
||
|
||
**答案:**
|
||
- 实时聊天
|
||
- 实时推送
|
||
- 游戏
|
||
- 股票行情
|
||
|
||
### Q3: WebSocket 的心跳机制?
|
||
|
||
**答案:**
|
||
- 定期发送心跳消息
|
||
- 保持连接活跃
|
||
- 检测连接是否断开
|
||
|
||
### Q4: WebSocket 的重连机制?
|
||
|
||
**答案:**
|
||
- 连接断开时自动重连
|
||
- 使用指数退避策略
|
||
- 限制最大重连延迟
|
||
|
||
---
|
||
|
||
*最后更新:2024年*
|