websocket draft-15 服务端握手Demo的perl实现
websocket这个坑爹的项目一直都在草案中,而且有时的更新变化还挺大的。目前最新的版本要先握手,信任后再发送相应数据,数据还掩码了。这次先实现了握手功能,其原理如下:
1.客端向服务器端发送类似这样的HTTP头:
Upgrade: websocket
Connection: Upgrade
Host: localhost:8000
Sec-WebSocket-Origin: null
Sec-WebSocket-Key: FcJ21uh+iiDs7haoaG1cKQ==
Sec-WebSocket-Version: 8
其中
Upgrade:wesocket 描述了该信息是websocket协议,
Host:localhost:8000 这个就是请求的主机地址和端口号,
Sec-WebSocket-Version:8 是版本号,这里是8,用的chrome14作的测试
Sec-WebSocket-Key: FcJ21uh+iiDs7haoaG1cKQ== 这条是很重要的,FcJ21uh+iiDs7haoaG1cKQ==是客户端提供的握手所用的key,需要服务器端根据该key作相应计算后返回一个对应的key来完成握手,具体的计算方法马上就会说到。
2. 服务器端
握手步骤:
(1)服务器端获取到客户端发送过来的 Sec-WebSocket-Key 值(比如这里的 FcJ21uh+iiDs7haoaG1cKQ== );
(2)将获取到的key值与一个magic string “258EAFA5-E914-47DA-95CA-C5AB0DC85B11” 连接成一个新的key串 "FcJ21uh+iiDs7haoaG1cKQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11",这个magic string为标准里给出的值,可以看作为一个唯一ID序列;
(3)将新生成的串进行SHA1编码,生成一个由多组两位16进制数构成的加密串,比如这里的 "FcJ21uh+iiDs7haoaG1cKQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 将生成 "11a8a543955aaf7f9a266c55b2a9cda3151cdec5";
(4)把加密串按2位16进制数字分隔,进行base64编码生成最终的key:base64编码(11 a8 a5 43 95 5a af 7f 9a 26 6c 55 b2 a9 cd a3 15 1c de c5);
(5)将最终生成的key以Sec-WebSocket-Accept:key值 整合到返回头中返回给客户端(该例子的key计算结果为 EailQ5Var3+aJmxVsqnNoxUc3sU=):
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: EailQ5Var3+aJmxVsqnNoxUc3sU=
WebSocket-Origin: localhost
WebSocket-Location: ws://localhost:8000
服务端应答后就完成了这一次的握手,完整代码如下:
use strict;
use IO::Handle;
use Socket;
use Digest::SHA1 qw(sha1 sha1_hex sha1_base64);
use MIME::Base64;
my $buff;
my $port=8000;
my $host='localhost';
my $packhost=inet_aton($host);
my $address=sockaddr_in($port,$packhost);
my $msg="";
my $magicString="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
my $serverKey="";
sub getClientKey{
my $ret="";
if($_[0]=~m"Sec-WebSocket-Key: ([^\s]*)"s){
$ret=$1;
}else{
#print "no matched";
}
$ret=~s/\s//g;
return $ret;
}
sub formatSHA1Hex{
my $ret="";
my $len=length($_[0]);
my $i;
for($i=0;$i<$len;$i+=2){
$ret.=chr(oct("0x".substr($_[0],$i,2)));
}
return $ret;
}
socket(SERVER,AF_INET,SOCK_STREAM,getprotobyname('tcp'));
bind(SERVER,$address);
listen(SERVER,10);
while(1){
accept(CLIENT,SERVER);
sysread(CLIENT,$buff,1000);
$serverKey=encode_base64(formatSHA1Hex(sha1_hex(getClientKey($buff).$magicString)));
print "$buff\n";
$msg="HTTP/1.1 101 Web Socket Protocol Handshake\r\n".
"Upgrade: websocket\r\n".
"Connection: Upgrade\r\n".
"Sec-WebSocket-Accept: ".$serverKey."\r\n".
"WebSocket-Origin: $host\r\n".
"WebSocket-Location: ws://$host:$port\r\n\r\n";
CLIENT->autoflush(1);
syswrite(CLIENT,$msg);
close CLIENT;
}
close SERVER;
exit 1;
为了让代码尽量简单,省去了websockt协议验证之类的细节部分,只列出了核心功能。而且此次只实现了握手部分,而且比较简单,数据相互通信的部分看哪天又闲得蛋疼的时候再写了。。。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器