1. 介绍

上一篇文章websocket之简单的服务器端(二)介绍了两个简单的websocket服务器,并且介绍了如何用javascript连接上websocket服务器。除了能用浏览器的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-VersionSec-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就会返回相同的输入。

本篇完结。

下一篇: websocket之实现简易聊天室(四)

results matching ""

    No results matching ""