言成言成啊 | Kit Chen's Blog

Linux服务器安装GUI界面工具及HTTP Upgrade机制

发布于2025-05-31 18:55:23,更新于2025-06-22 13:51:50,标签:http devops  文章会持续修订,转载请注明来源地址:https://meethigher.top/blog

一、通过noVNC访问GUI

本文记录在Linux使用Docker安装GUI工具,并使用Firefox浏览器访问页面。

获取docker镜像ubuntu-desktop-lxde-vnc,该镜像内置如下工具

  • Web桌面服务(noVNC):默认端口80
  • VNC服务:默认端口5900
  • 火狐浏览器

有如上工具,基本上使用GUI的条件都满足了。

1.) 获取镜像

1
docker pull dorowu/ubuntu-desktop-lxde-vnc:focal-arm64

2.) 启动镜像,只开启noVNC服务,并设置密码123456

1
2
3
4
5
6
7
8
docker run -d --rm \
-p 80:80 \
-e TZ=Asia/Shanghai \
-e VNC_PASSWORD=123456 \
-v /etc/localtime:/etc/localtime:ro \
-v /dev/shm:/dev/shm \
--name gui \
dorowu/ubuntu-desktop-lxde-vnc:focal-arm64

-v /dev/shm:/dev/shm:挂载共享内存设备,提高图形性能(Chromium/Firebase 会用到)。

3.) 浏览器访问http://ip:80

二、HTTP Upgrade

2.1 原理

WebSocket 不是独立于 HTTP 的协议,而是通过 HTTP/1.1 的 Upgrade 机制建立连接。建立之后,通信就完全脱离 HTTP,变成 WebSocket 专用的帧格式。

而像上面的 noVNC 就是通过一个端口,实现了 HTTP 服务以及升级成 WebSocket 进行实时通信。

查看 WebSocket 升级的过程。

1.) 客户端通过 HTTP/1.1 向服务端发起一个升级请求。

1
2
3
4
5
6
7
GET /ws HTTP/1.1
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: cVw5C1V0TEmN6ZTY76U1OQ==
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Host: 192.168.1.106:8088

说明

  • Connection:Upgrade
    • 表示这个连接要进行升级
  • Upgrade:websocket
    • 表示这个连接要升级成 WebSocket
  • Sec-WebSocket-Version:13
    • 当前使用的WebSocket版本
  • Sec-WebSocket-Key:随机生成的Base64编码。

2.) 服务端同意升级。

1
2
3
4
5
HTTP/1.1 101 Switching Protocols
upgrade: websocket
connection: upgrade
sec-websocket-accept: rpyOTEZGtzbDpjvK9/TiQPfjp3I=
sec-websocket-extensions: permessage-deflate

说明

  • 101 Switching Protocols
    • 协议升级成功
  • Sec-WebSocket-Accept:rpyOTEZGtzbDpjvK9/TiQPfjp3I=
    • 服务端根据客户端的key生成的校验值。生成方式是将Sec-WebSocket-Key的值+协议版本对应的GUID拼接后的字符串进行SHA-1编码。客户端拿到后,做同样的操作,然后校验。

具体的细节可自行查阅文档。RFC 6455 - The WebSocket Protocol

下面记录使用 Vertx 实现 HTTP/WebSocket 服务的示例,源码参考meethigher/vertx-examples: learn to use vertx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class Example17 {


private static final Logger log = LoggerFactory.getLogger(Example17.class);

public static void main(String[] args) {
Vertx vertx = Vertx.vertx();

HttpServer server = vertx.createHttpServer();

// 处理 HTTP 请求
server.requestHandler(req -> {
if ("/hello".equals(req.path())) {
req.response()
.putHeader("content-type", "text/plain")
.end("HTTP " + System.currentTimeMillis());
} else {
req.response()
.setStatusCode(404)
.end("Not Found");
}
});

// 处理 WebSocket 请求
server.webSocketHandler(ws -> {
if ("/ws".equals(ws.path())) {
ws.handler(buffer -> {
// 收到消息后,原封不动回写
ws.writeTextMessage("WebSocket " + System.currentTimeMillis());
});
} else {
ws.reject();
}
});

// 监听端口
server.listen(8080, res -> {
if (res.succeeded()) {
log.info("http server started on port {}", res.result().actualPort());
} else {
log.error("http server start failed", res.cause());
}
});
}
}

Postman 可以支持发起 WebSocket 请求,右键 File-New-WebSocket 即可。

2.2 http2对websocket升级的调整

HTTP2 明确禁止通过 Upgrade 请求头来进行协议升级。参考协议升级机制 - HTTP | MDN

但是也不能说 HTTP2 完全不支持 WebSocket 升级,只是换了升级方式,大部分浏览器支持的不太好而已。RFC 8441 - Bootstrapping WebSockets with HTTP/2

2.3 为何在大模型LLM使用上sse多于websocket

最近在使用 mcp 时,发现内部通信机制使用了异步 sse,形成双向通信。

  1. 客户端与服务端建立 sse 长连接,服务端返回 sessionId 接口
  2. 客户端通过的 http post 向 sessionId 接口发起请求
  3. 服务端通过 sse 长连接返回响应

于是就思考为何不直接使用 websocket?原因也很简单。

像这种实时通信的,最好使用长连接,直接基于 tcp 自定义协议也行。但是便于开发的角度而言,可选的方案有 sse 和 websocket。

websocket 在 http2 上的升级,目前支持的并不好。而 http2 又解决了 http1.1 的队头阻塞问题,目前使用比较广泛。所以我认为这是弃用 websocket 的一个主要原因。

发布:2025-05-31 18:55:23
修改:2025-06-22 13:51:50
链接:https://meethigher.top/blog/2025/linux-server-gui/
标签:http devops 
付款码 打赏 分享
Shift+Ctrl+1 可控制工具栏