Rudy websocket 之客户端详解
1. 介绍
除了能用浏览器的javascript连接上,还可以用任何编程语言,因为websocket协议是基于TCP协议请求的,只要能发送TCP socket请求,就可以发送websocket请求,这篇文章来讲述如何用ruby来发送websocket请求,并讲讲其原理。
2. websocket-ruby
websocket-ruby是一个纯ruby实现websocket请求的gem,它支持很多版本的websocket。比如官方列出的:
学习它,可以让我们对websocket协议的客户端和服务器的实现更为了解。
首先安装它。
$ gem install "websocket"
来看一个最简单的例子,客户端请求websocket请求。
@handshake = WebSocket::Handshake::Server.new # Parse client request @handshake << <<EOF GET /demo HTTP/1.1\r Upgrade: websocket\r Connection: Upgrade\r Host: example.com\r Origin: http://example.com\r Sec-WebSocket-Version: 13\r Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r \r EOF # All data received? @handshake.finished? # No parsing errors? @handshake.valid? # Create response puts @handshake.to_s
因为我们说过websocket协议是基于tcp协议之上,所以我们可以发送类似的socket请求。
@handshake
变量就是我们socket请求的内容。我们主要来看这部分。
其中,第二行代码@handshake << <<EOF
发送的内容,跟之前上一篇文章在浏览器的请求头信息是差不多的,其中来看看Sec-WebSocket-Version
和Sec-WebSocket-Key
。
Sec-WebSocket-Version
表示的是websocket使用的版本,客户端和服务器端都会根据客户端发送的版本号,进行相应的处理,不同的版本对应不同的处理方式,这些都是websocket-ruby实现好的。
比如源码中是这样实现的:
# https://github.com/imanel/websocket-ruby/blob/master/lib/websocket/handshake/client.rb#L103 def include_version @handler = case @version when 75 then Handler::Client75.new(self) when 76, 0 then Handler::Client76.new(self) when 1..3 then Handler::Client01.new(self) when 4..10 then Handler::Client04.new(self) when 11..17 then Handler::Client11.new(self) else fail WebSocket::Error::Handshake::UnknownVersion end end
Sec-WebSocket-Key是用base64算法加密过的随机串,每次请求都不一样,上面是自己指定的,但是它可以由客户端计算出来,比如
# https://github.com/imanel/websocket-ruby/blob/master/lib/websocket/handshake/handler/client04.rb#L33 def key @key ||= Base64.encode64((1..16).map { rand(255).chr } * '').strip end
现在回头来看看上面的演示代码到底输出了什么样的结果。
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
返回的状态码是101,并且返回了Sec-WebSocket-Accept的内容。
Sec-WebSocket-Accept的计算方式是这样的,把客户端发送过来的“Sec-WebSocket-Key”加上一个魔幻字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”。使用SHA-1加密,之后进行BASE-64编码,将结果做为“Sec-WebSocket-Accept”头的值,返回给客户端。
它的算法是这样的:
# https://github.com/imanel/websocket-ruby/blob/master/lib/websocket/handshake/handler/server04.rb#L31 def signature return unless key string_to_sign = "#{key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11" Base64.encode64(Digest::SHA1.digest(string_to_sign)).chomp end
3. websocket-client-simple
websocket-client-simple是对websocket-ruby这个gem的进一步封装,它的源码只有一个文件。还记得上一篇文章,用javascript写websocket请求的例子吗,ruby也可以有类似的语法,就是用这个gem。
require 'websocket-client-simple' ws = WebSocket::Client::Simple.connect 'ws://localhost:8080/echo' ws.on :message do |msg| puts "received data: " + msg.data end ws.on :open do ws.send 'hello!!!' end ws.on :close do |e| p e exit 1 end ws.on :error do |e| p e end loop do ws.send STDIN.gets.strip end
这个例子演示了,输入什么,websocket就会返回相同的输入
也可以看看GoEasy文库的其他资料。