这个星球上的每一款浏览器,都会在每一个请求里发送 Accept-Encoding: gzip, deflate。而在此之前,sgcWebSockets HTTP 服务器一直忽略它,把响应体原封不动地发出去。你的 HTML、你的 CSS、你的 JavaScript 和你的 JSON 都以完整体积走过网络,尽管客户端在每一个请求里都在告诉你,它非常乐意解开一个更小的包。
sgcWebSockets 2026.7 为 HTTP 服务器加入了响应压缩。当客户端声明支持 gzip 或 deflate 时,服务器现在可以在发送前压缩响应。它既适用于从 DocumentRoot 提供的静态文件,也适用于你在事件处理器中自行构建的响应。它默认是关闭的,所以在你打开它之前,升级不会改变任何东西。
如何启用
压缩通过服务器上的一个选项对象 HTTPCompression 进行配置。在 TsgcWebSocketHTTPServer 上它是一个 published 属性,因此你可以在设计期用对象查看器设置它,也可以在代码中设置:
uses
sgcWebSocket;
begin
sgcWebSocketHTTPServer1.HTTPCompression.Enabled := True;
sgcWebSocketHTTPServer1.Active := True;
end;
这就是全部的设置。设置 Enabled := True 之后,服务器会检查每个请求的 Accept-Encoding 头,如果客户端支持已配置的算法之一,它就压缩响应体并相应地设置 Content-Encoding。它还会加上 Vary: Accept-Encoding,这样你服务器前面的缓存和代理就会按编码方式区分内容,而不会把 gzip 压缩过的响应体交给一个读不懂它的客户端。
如果客户端没有声明支持压缩,什么都不会发生,响应会和以前完全一样地发出去。压缩绝不会强加给一个没有请求它的客户端。
Algorithms 属性是一个集合,两个成员默认都是启用的:
sgcWebSocketHTTPServer1.HTTPCompression.Algorithms := [caGZip, caDeflate];
// or gzip only, if you prefer to keep it to a single well-understood encoding
sgcWebSocketHTTPServer1.HTTPCompression.Algorithms := [caGZip];
当客户端两者都支持时,服务器优先选择 gzip。
哪些内容会被压缩,哪些不会
并不是每个响应都值得压缩。ContentTypes 列表决定了哪些值得,而且它自带一份合理的默认值:text/*、application/json、application/javascript、application/xml、application/xhtml+xml 和 image/svg+xml。以 /* 结尾的条目匹配一整类,因此 text/* 覆盖了 HTML、CSS、纯文本以及其余类型。
文本、HTML、CSS、JavaScript 和 JSON 的压缩效果非常好,因为它们充满了重复的标记、标签名和空白字符。而已经压缩过的格式则不然。一张 JPEG、一张 PNG、一张 WebP 图片、一个 ZIP 归档、一个 .gz 文件或一段 MP4 视频,其中的冗余早已被榨干,再让它过一遍 zlib 只会白白消耗 CPU,产出一个体积大致相同、有时甚至略大的响应体。这些内容类型不在列表里,所以会被跳过。
你可以用自己的列表替换它:
uses
sgcWebSocket, sgcWebSocket_Classes;
begin
with sgcWebSocketHTTPServer1.HTTPCompression do
begin
ContentTypes.Clear;
ContentTypes.Add('text/*'); // html, css, plain text
ContentTypes.Add('application/json'); // your API replies
ContentTypes.Add('application/javascript');
ContentTypes.Add('image/svg+xml');
ContentTypes.Add('application/x-ndjson'); // whatever else you serve
Enabled := True;
end;
sgcWebSocketHTTPServer1.Active := True;
end;
这个检查会忽略任何字符集后缀,因此一个带 Content-Type: text/html; charset=utf-8 的响应仍然匹配 text/*。服务器里还有一条双保险规则:如果压缩后的响应体并不比原始的更小,压缩结果就会被丢弃,改为发送未压缩的响应体。你绝不会因为启用压缩而发出比原来更多的字节。
压缩级别与最小体积
Level 就是常见的 zlib 级别,1 到 9,默认值是 6。这个默认值是速度与压缩比之间的标准折中,也是世界上几乎每一台 Web 服务器采用的取值。把它提到 9 会让每一个响应都明显多耗 CPU,而对 JSON 来说通常换不来多少额外收益:相比未压缩的响应体,最大的收益在级别 1 时就已经拿到了,而级别 6 到 9 争的只是最后那几个百分点。如果你的服务器受限于 CPU,那么把它降到 3 或 4 是一个远比调到 9 更值得一试的实验。
MinSize 默认是 1024 字节,比这更小的响应体会按原样发送。一个 gzip 流自带头部和尾部,在大约一千字节以下时,这些封装开销加上 CPU 成本,根本抵不上你省下的那点字节。微小的 JSON 确认消息和简短的重定向都属于这一类,而它们本就该如此。
with sgcWebSocketHTTPServer1.HTTPCompression do
begin
Level := 6; // 1..9, default 6
MinSize := 1024; // bytes, default 1024, smaller bodies are sent as-is
Enabled := True;
end;
它能带给你什么
收益体现在网络上,而不是你的代码里。一个 HTML 页面或一个 JSON API 响应高度重复,因此一份几十 KB 的典型负载在离开服务器之前通常能压缩到原来的几分之一。网络上的字节更少意味着把它们送达所需的往返更少,而这正是它真正见效的地方:在移动网络和高延迟链路上,交付一个响应所需的时间主要取决于往返次数,而不是带宽。一个能装进更少数据包的响应体,就是会更早到达。
代价是服务器端的 CPU,每个响应一次,而在默认级别下它很小。对于一个文本密集的网站或一个 JSON API 来说,这笔交易几乎总是划算的。
你自行构建的响应
在你的事件处理器里,你不需要做任何特别的事。像以往一样构建响应,设置 ContentText 或 ContentStream 以及 ContentType,只要客户端请求了压缩、并且内容类型在列表中,服务器就会在发出去的路上把它压缩:
procedure TForm1.sgcWebSocketHTTPServer1CommandGet(aConnection: TsgcWSConnection;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
if ARequestInfo.Document = '/api/orders' then
begin
AResponseInfo.ContentType := 'application/json';
AResponseInfo.ContentText := GetOrdersAsJSON; // your payload, plain text
// nothing else to do. If the client sent Accept-Encoding: gzip and the
// body is at least MinSize bytes, it goes out gzipped.
end;
end;
压缩这一步在你的处理器返回之后才运行,因此客户端看到的 Content-Length 是压缩后的长度,而 Content-Encoding 也已经替你设好了。如果你自己设置了 ContentEncoding,因为你提供的响应体本来就是压缩过的,那么服务器会完全不去动你的响应。
有两类响应是刻意永不压缩的:Server-Sent Events 流(text/event-stream),因为它绝不能被缓冲;以及像 204 和 304 这样没有响应体的响应。
从 DocumentRoot 提供的文件
静态文件享受同样的待遇,而且完全不需要写代码。把服务器指向一个文件夹,启用压缩,其中的 HTML、CSS、JavaScript、SVG 和 JSON 文件就会在发出去的路上被压缩。同一个文件夹里的图片和归档文件不会,因为它们的内容类型不在列表中。
sgcWebSocketHTTPServer1.HTTPUploadFiles.DocumentRoot := 'C:\www';
sgcWebSocketHTTPServer1.HTTPCompression.Enabled := True;
sgcWebSocketHTTPServer1.Active := True;
// GET /index.html -> compressed, text/html is in ContentTypes
// GET /app.js -> compressed
// GET /logo.png -> not compressed, image/png is not in the list
// GET /favicon.ico -> not compressed
HTTP.sys 服务器
Windows 上的 HTTP.sys 服务器 TsgcWebSocketServer_HTTPAPI 有同样的 HTTPCompression 选项对象,属性相同,默认值也相同。有一处差别值得说清楚:在 HTTP.sys 服务器上,该属性是 public 而不是 published,因此它不会出现在对象查看器中。你必须在代码中设置它,并且要在激活服务器之前:
uses
sgcWebSocket_Server_HTTPAPI;
begin
sgcWebSocketServer_HTTPAPI1.HTTPCompression.Enabled := True;
sgcWebSocketServer_HTTPAPI1.HTTPCompression.Level := 6;
sgcWebSocketServer_HTTPAPI1.HTTPCompression.MinSize := 1024;
sgcWebSocketServer_HTTPAPI1.Active := True;
end;
目前仅限 HTTP/1.1
在这个版本中,只有 HTTP/1.1 的响应会被压缩。HTTP/2 和 HTTP/3 的响应不会被处理。如果你通过 HTTP/2 提供你的站点,这个特性目前还不会给你带来任何变化。把这一点讲清楚,总好过让你启用一个属性之后,纳闷为什么什么都没变小。
可用性
HTTP 响应压缩在 sgcWebSockets 2026.7 中提供,支持 Delphi 7 到 13 以及 C++Builder,平台涵盖 Win32、Win64、Linux64 和 macOS。它默认关闭:在你设置 HTTPCompression.Enabled := True 之前,升级不会改变你现有服务器的任何行为。
拥有有效订阅的客户可以从客户专区下载新版本。试用用户可以在 esegece.com/products/websockets/download 获取更新后的安装程序。
有疑问、反馈,或者需要帮助为你自己的服务器调整内容类型?联系我们,回复你的将是编写这些代码的人。
