如果您想了解Websocket-Websocket原理的相关知识,那么本文是一篇不可错过的文章,我们将对握手、解密、加密、基于Python实现简单示例进行全面详尽的解释,并且为您提供关于Flask+g
如果您想了解Websocket - Websocket原理的相关知识,那么本文是一篇不可错过的文章,我们将对握手、解密、加密、基于Python实现简单示例进行全面详尽的解释,并且为您提供关于Flask+gevent-websocket模块实现websocket、HTML5 websockets与PHP websockets vs node.js websockets?、java WebSocket的实现以及Spring WebSocket示例代码、javascript – 与Ruby和EM :: WebSocket :: Server的WebSocket握手的有价值的信息。
本文目录一览:- Websocket - Websocket原理(握手、解密、加密)、基于Python实现简单示例(websocket握手过程详解)
- Flask+gevent-websocket模块实现websocket
- HTML5 websockets与PHP websockets vs node.js websockets?
- java WebSocket的实现以及Spring WebSocket示例代码
- javascript – 与Ruby和EM :: WebSocket :: Server的WebSocket握手
Websocket - Websocket原理(握手、解密、加密)、基于Python实现简单示例(websocket握手过程详解)
一、Websocket原理(握手、解密、加密)
WebSocket协议是基于TCP的一种新的协议。WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符。它实现了浏览器与服务器全双工(full-duplex)通信。其本质是保持TCP连接,在浏览器和服务端通过Socket进行通信。
本文将使用Python编写Socket服务端,一步一步分析请求过程!!!
1、启动服务端
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((''127.0.0.1'', 9527))
sock.listen(5)
# 获取客户端socket对象,并等待用户连接
conn, address = sock.accept()
......
......
......
启动Socket服务器后,等待用户【连接】,然后进行收发数据。
2、客户端连接
<script type="text/javascript">
var socket = new WebSocket("ws://127.0.0.1:9527/xxoo");
......
</script>
当客户端向服务端发送连接请求时,不仅连接还会发送【握手】信息,并等待服务端响应,至此连接才创建成功!
3、建立连接【握手】
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((''127.0.0.1'', 9527))
sock.listen(5)
# 获取客户端socket对象,并等待用户连接
conn, address = sock.accept()
# 获取客户端的【握手】信息,data即握手信息
data = conn.recv(1024)
......
......
conn.send(''响应【握手】信息'')
请求和响应的【握手】信息需要遵循规则:
- 从请求【握手】信息中提取 Sec-WebSocket-Key;
- 利用magic_string 和 Sec-WebSocket-Key 进行hmac1加密,再进行base64加密;
- 将加密结果响应给客户端;
注:magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11
请求【握手】信息为:
b''GET /xxoo HTTP/1.1\r\n
Host: 127.0.0.1:9527\r\n
Connection: Upgrade\r\n
Pragma: no-cache\r\n
Cache-Control: no-cache\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36\r\n
Upgrade: websocket\r\n
Origin: http://localhost:63342\r\n
Sec-WebSocket-Version: 13\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9\r\n
Cookie: session=ba01367c-59b9-41d4-81ba-30b70db282c6\r\n
Sec-WebSocket-Key: jLSLU57WxRJACRQxlN47Tw==\r\n
Sec-WebSocket-Extensions: permessage-deflate;
......''
提取Sec-WebSocket-Key值并加密:
import socket
import base64
import hashlib
def get_headers(data):
"""
将请求头格式化成字典
:param data: 请求头
:return: 请求头信息字典
"""
header_dict = {}
header_str = data.decode("utf8")
for i in header_str.split("\r\n"):
if str(i).startswith("Sec-WebSocket-Key"):
header_dict["Sec-WebSocket-Key"] = i.split(":")[1].strip()
return header_dict
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((''127.0.0.1'', 9527))
sock.listen(5)
# 获取客户端socket对象,并等待用户连接
conn, address = sock.accept()
# 获取客户端的【握手】信息
data = conn.recv(1024)
headers = get_headers(data) # 提取请求头信息
# 对请求头中的Sec-WebSocket-Key进行加密
response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade:websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: %s\r\n" \
"WebSocket-Location: ws://127.0.0.1:9527\r\n\r\n"
magic_string = ''258EAFA5-E914-47DA-95CA-C5AB0DC85B11''
value = headers[''Sec-WebSocket-Key''] + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode(''utf-8'')).digest())
response_str = response_tpl % (ac.decode(''utf-8''))
# 响应【握手】信息
conn.send(response_str.encode("utf-8"))
......
......
至此,客户端与服务端完成握手过程。
4、客户端与服务端收发数据
客户端和服务端传输数据时,需要对数据进行【封包】和【解包】。客户端的JavaScript类库已经封装【封包】和【解包】过程,但Socket服务端需要手动实现。
第一步:获取客户端发送的数据【解包】
# b''\x81\x86#\xa47\x93\xc7\x19\x97v\x86\x19'' 你好
hashstr = b''\x81\x83\xceH\xb6\x85\xffz\x85''
# b''\x81 \x86# \xa47\x93\xc7\x19 \x97v\x86\x19''
# 将第二个字节 \x86# 也就是第9-16位 与 127 进行位运算
payload_len = hashstr[1] & 127
if payload_len == 127:
extend_payload_len = hashstr[2:10]
mask = hashstr[10:14]
decoded = hashstr[14:]
# 当位运算结果等于127时,则第3-10个字节为数据长度
# 第11-14字节为mask ,即解密所需字符串
# 则数据为第15字节至结尾
if payload_len == 126:
extend_payload_len = hashstr[2:4]
mask = hashstr[4:8]
decoded = hashstr[8:]
# 当位运算结果等于126时,则第3-4个字节为数据长度
# 第5-8字节为mask ,即解密所需字符串
# 则数据为第9字节至结尾
if payload_len <= 125:
extend_payload_len = None
mask = hashstr[2:6]
decoded = hashstr[6:]
# 当位运算结果小于等于125时,则这个数字就是数据的长度
# 第3-6字节为mask ,即解密所需字符串
# 则数据为第7字节至结尾
str_byte = bytearray()
for i in range(len(decoded)): # 循环数据长度
byte = decoded[i] ^ mask[i % 4] # ^ 是或运算
str_byte.append(byte)
print(str_byte.decode("utf8"))
解包详细过程:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
第二步:向客户端发送数据【封包】
def send_msg(conn, msg_bytes):
"""
WebSocket服务端向客户端发送消息
:param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept()
:param msg_bytes: 向客户端发送的字节
:return:
"""
import struct
token = b"\x81"
length = len(msg_bytes)
if length < 126:
token += struct.pack("B", length)
elif length == 126:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length)
msg = token + msg_bytes
conn.send(msg)
return True
二、基于Python实现简单示例
1、基于Python socket实现的WebSocket服务端
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import base64
import hashlib
def get_headers(data):
"""
将请求头格式化成字典
:param data: 请求头
:return: 请求头信息字典
"""
header_dict = {}
header_str = data.decode("utf8")
for i in header_str.split("\r\n"):
if str(i).startswith("Sec-WebSocket-Key"):
header_dict["Sec-WebSocket-Key"] = i.split(":")[1].strip()
return header_dict
def send_msg(conn, msg_bytes):
"""
WebSocket服务端向客户端发送消息
:param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept()
:param msg_bytes: 向客户端发送的字节
:return:
"""
import struct
token = b"\x81"
length = len(msg_bytes)
if length < 126:
token += struct.pack("B", length)
elif length == 126:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length)
msg = token + msg_bytes
conn.send(msg)
return True
def run():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((''127.0.0.1'', 9527))
sock.listen(5)
conn, address = sock.accept()
data = conn.recv(1024)
headers = get_headers(data)
# 对请求头中的Sec-WebSocket-Key进行加密
response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade:websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: %s\r\n" \
"WebSocket-Location: ws://127.0.0.1:9527\r\n\r\n"
magic_string = ''258EAFA5-E914-47DA-95CA-C5AB0DC85B11''
value = headers[''Sec-WebSocket-Key''] + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode(''utf-8'')).digest())
response_str = response_tpl % (ac.decode(''utf-8''))
# 响应【握手】信息
conn.send(response_str.encode("utf-8"))
while True:
try:
info = conn.recv(8096)
except Exception as e:
info = None
if not info:
break
payload_len = info[1] & 127
if payload_len == 126:
extend_payload_len = info[2:4]
mask = info[4:8]
decoded = info[8:]
elif payload_len == 127:
extend_payload_len = info[2:10]
mask = info[10:14]
decoded = info[14:]
else:
extend_payload_len = None
mask = info[2:6]
decoded = info[6:]
bytes_list = bytearray()
for i in range(len(decoded)):
chunk = decoded[i] ^ mask[i % 4]
bytes_list.append(chunk)
body = str(bytes_list, encoding=''utf-8'')
send_msg(conn, body.encode(''utf-8''))
sock.close()
if __name__ == ''__main__'':
run()
2、利用JavaScript类库实现客户端
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div>
<input type="text" id="txt"/>
<input type="button" id="btn" value="提交" onclick="sendMsg();"/>
<input type="button" id="close" value="关闭连接" onclick="closeConn();"/>
</div>
<div id="content"></div>
<script type="text/javascript">
var socket = new WebSocket("ws://127.0.0.1:9527/chatsocket");
socket.onopen = function () {
/* 与服务器端连接成功后,自动执行 */
var newTag = document.createElement(''div'');
newTag.innerHTML = "【连接成功】";
document.getElementById(''content'').appendChild(newTag);
};
socket.onmessage = function (event) {
/* 服务器端向客户端发送数据时,自动执行 */
var response = event.data;
var newTag = document.createElement(''div'');
newTag.innerHTML = response;
document.getElementById(''content'').appendChild(newTag);
};
socket.onclose = function (event) {
/* 服务器端主动断开连接时,自动执行 */
var newTag = document.createElement(''div'');
newTag.innerHTML = "【关闭连接】";
document.getElementById(''content'').appendChild(newTag);
};
function sendMsg() {
var txt = document.getElementById(''txt'');
socket.send(txt.value);
txt.value = "";
}
function closeConn() {
socket.close();
var newTag = document.createElement(''div'');
newTag.innerHTML = "【关闭连接】";
document.getElementById(''content'').appendChild(newTag);
}
</script>
</body>
</html>
更多websocket详解参见:http://www.runoob.com/html/html5-websocket.html
websocket原理参考博客:https://www.cnblogs.com/fuqiang88/p/5956363.html
Flask+gevent-websocket模块实现websocket
后端代码:
from flask import Flask,request
from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer
from geventwebsocket.websocket import WebSocket #这条做语法提示用
app = Flask(__name__)
@app.route(''/conn'')
def index():
#获取请求原始数据
user_socket = request.environ
#获取websocket对象
websocket_obj = user_socket[''wsgi.websocket''] #type:WebSocket
while True: #循环监听
# 监听链接,接收数据
msg = websocket_obj.receive()
print(msg)
websocket_obj.send(msg+''youtoo'')
if __name__ == ''__main__'':
# app.run()
#在APP外封装websocket
http_serv = WSGIServer(("0.0.0.0",5000),app,handler_class=WebSocketHandler)
# 启动服务
http_serv.serve_forever()
模板代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>websocket客户端</title>
</head>
<body>
<script type="application/javascript">
var ws = new WebSocket(''ws://127.0.0.1:5000/conn''); //建立websocket链接
//接收数据
ws.onmessage = function (messageEvent) {
console.log(messageEvent.data)
}
</script>
</body>
</html>
HTML5 websockets与PHP websockets vs node.js websockets?
我想知道的是三者之间有什么区别,我的意思是我不想学习所有这三者,如果有的话比其他人好.
解决方法
java WebSocket的实现以及Spring WebSocket示例代码
本篇文章主要介绍了java WebSocket的实现以及Spring WebSocket,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
开始学习WebSocket,准备用它来实现一个在页面实时输出log4j的日志以及控制台的日志。
首先知道一些基础信息:
1.java7 开始支持WebSocket,并且只是做了定义,并未实现
2.tomcat7及以上,jetty 9.1及以上实现了WebSocket,其他容器没有研究
3.spring 4.0及以上增加了WebSocket的支持
4.spring 支持STOMP协议的WebSocket通信
5.WebSocket 作为java的一个扩展,它属于javax包目录下,通常需要手工引入该jar,以tomcat为例,可以在 tomcat/lib 目录下找到 websocket-api.jar
开始实现
先写一个普通的WebSocket客户端,直接引入tomcat目录下的jar,主要的jar有:websocket-api.jar、tomcat7-websocket.jar
public static void f1() { try { WebSocketContainer container = ContainerProvider.getWebSocketContainer(); // 获取WebSocket连接器,其中具体实现可以参照websocket-api.jar的源码,Class.forName("org.apache.tomcat.websocket.WsWebSocketContainer"); String uri = "ws://localhost:8081/log/log"; Session session = container.connectToServer(Client.class, new URI(uri)); // 连接会话 session.getBasicRemote().sendText("123132132131"); // 发送文本消息 session.getBasicRemote().sendText("4564546"); } catch (Exception e) { e.printstacktrace(); } }
其中的URL格式必须是ws开头,后面接注册的WebSocket地址
Client.java 是用于收发消息
@ClientEndpoint public class Client { @Onopen public void onopen(Session session) { System.out.println("Connected to endpoint: " + session.getBasicRemote()); } @OnMessage public void onMessage(String message) { System.out.println(message); } @OnError public void onError(Throwable t) { t.printstacktrace(); } }
到这一步,客户端的收发消息已经完成,现在开始编写服务端代码,用Spring 4.0,其中pom.xml太长就不贴出来了,会用到jackson,spring-websocket,spring-message
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; import com.gionee.log.client.LogWebSocketHandler; /** * 注册普通WebScoket * @author PengBin * @date 2016年6月21日 下午5:29:00 */ @Configuration @EnableWebMvc @EnableWebSocket public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer { @Autowired @Lazy private SimpMessagingTemplate template; /** {@inheritDoc} */ @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(logWebSocketHandler(), "/log"); // 此处与客户端的 URL 相对应 } @Bean public WebSocketHandler logWebSocketHandler() { return new LogWebSocketHandler(template); } }
import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; /** * * @author PengBin * @date 2016年6月24日 下午6:04:39 */ public class LogWebSocketHandler extends TextWebSocketHandler { private SimpMessagingTemplate template; public LogWebSocketHandler(SimpMessagingTemplate template) { this.template = template; System.out.println("初始化 handler"); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { String text = message.getPayload(); // 获取提交过来的消息 System.out.println("handMessage:" + text); // template.convertAndSend("/topic/getLog", text); // 这里用于广播 session.sendMessage(message); } }
这样,一个普通的WebSocket就完成了,自己还可以集成安全控制等等
Spring还支持一种注解的方式,可以实现订阅和广播,采用STOMP格式协议,类似MQ,其实应该就是用的MQ的消息格式,下面是实现
同样客户端:
public static void main(String[] args) { try { WebSocketContainer container = ContainerProvider.getWebSocketContainer(); String uri = "ws://localhost:8081/log/hello/hello/websocket"; Session session = container.connectToServer(Client.class, new URI(uri)); char lf = 10; // 这个是换行 char nl = 0; // 这个是消息结尾的标记,一定要 StringBuilder sb = new StringBuilder(); sb.append("SEND").append(lf); // 请求的命令策略 sb.append("destination:/app/hello").append(lf); // 请求的资源 sb.append("content-length:14").append(lf).append(lf); // 消息体的长度 sb.append("{"name":"123"}").append(nl); // 消息体 session.getBasicRemote().sendText(sb.toString()); // 发送消息 Thread.sleep(50000); // 等待一小会 session.close(); // 关闭连接 } catch (Exception e) { e.printstacktrace(); } }
这里一定要注意,换行符和结束符号,这个是STOMP协议规定的符号,错了就不能解析到
服务端配置
/** * 启用STOMP协议WebSocket配置 * @author PengBin * @date 2016年6月24日 下午5:59:42 */ @Configuration @EnableWebMvc @EnableWebSocketMessagebroker public class WebSocketbrokerConfig extends AbstractWebSocketMessagebrokerConfigurer { /** {@inheritDoc} */ @Override public void registerStompEndpoints(StompEndpointRegistry registry) { System.out.println("注册"); registry.addEndpoint("/hello").withSockJS(); // 注册端点,和普通服务端的/log一样的 // withSockJS()表示支持socktJS访问,在浏览器中使用 } /** {@inheritDoc} */ @Override public void configureMessagebroker(MessagebrokerRegistry config) { System.out.println("启动"); config.enableSimplebroker("/topic"); // config.setApplicationDestinationPrefixes("/app"); // 格式前缀 } } Controller @Controller public class LogController { private SimpMessagingTemplate template; @Autowired public LogController(SimpMessagingTemplate template) { System.out.println("init"); this.template = template; } @MessageMapping("/hello") @SendTo("/topic/greetings") // 订阅 public Greeting greeting(HelloMessage message) throws Exception { System.out.println(message.getName()); Thread.sleep(3000); // simulated delay return new Greeting("Hello, " + message.getName() + "!"); } }
到这里就已经全部完成。
template.convertAndSend("/topic/greetings", "通知"); // 这个的意思就是向订阅了/topic/greetings进行广播
对于用socktJS连接的时候会有一个访问 /info 地址的请求
如果在浏览器连接收发送消息,则用sockt.js和stomp.js
function connect() { var socket = new SockJS('/log/hello/hello'); stompClient = Stomp.over(socket); stompClient.connect({}, function(frame) { setConnected(true); console.log('Connected: ' + frame); stompClient.subscribe('/topic/greetings', function(greeting) { showGreeting(JSON.parse(greeting.body).content); }); }); } function disconnect() { if (stompClient != null) { stompClient.disconnect(); } setConnected(false); console.log("disconnected"); } function sendName() { var name = document.getElementById('name').value; stompClient.send("/app/hello", {}, JSON.stringify({ 'name' : name })); }
在浏览器中可以看到请求返回101状态码,意思就是切换协议
javascript – 与Ruby和EM :: WebSocket :: Server的WebSocket握手
我正在尝试使用JavaScript在我的Rails应用程序中创建一个简单的WebSocket连接.我得到以下内容:
WebSocket connection to ‘ws://localhost:4000/’ Failed: Error during WebSocket handshake: ‘Sec-WebSocket-Accept’ header is missing
我究竟做错了什么?这是我的代码:
JavaScript的:
var socket = new WebSocket('ws://localhost:4000');
socket.onopen = function() {
var handshake =
"GET / HTTP/1.1\n" +
"Host: localhost\n" +
"Upgrade: websocket\n" +
"Connection: Upgrade\n" +
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\n" +
"Sec-WebSocket-Protocol: quote\n" +
"Sec-WebSocket-Version: 13\n" +
"Origin: http://localhost\n";
socket.send(handshake);
};
socket.onmessage = function(data) {
console.log(data);
};
ruby:
require 'rubygems'
require 'em-websocket-server'
module QuoteService
class WebSocket < EventMachine::WebSocket::Server
def on_connect
handshake_response = "HTTP/1.1 101 Switching Protocols\n"
handshake_response << "Upgrade: websocket\n"
handshake_response << "Connection: Upgrade\n"
handshake_response << "Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=\n"
handshake_response << "Sec-WebSocket-Protocol: quote\n"
send_message(handshake_response)
end
def on_receive(data)
puts 'RECEIVED: ' + data
end
end
end
EventMachine.run do
print 'Starting WebSocket server...'
EventMachine.start_server '0.0.0.0', 4000, QuoteService::WebSocket
puts 'running'
end
握手标头是每Wikipedia.
解决方法:
1)我认为一旦连接打开,请求和响应已经发生,所以在那时发送标题为时已晚.此外,标题必须以空行结束,您省略了该行.
2)根据演示,您甚至不必在客户端或服务器中设置标头 – ruby模块自动处理服务器端的标头,html5自动处理客户端的标头.我认为这应该有效:
require "em-websocket-server"
class EchoServer < EM::WebSocket::Server
def on_connect
EM::WebSocket::Log.debug "Connected"
puts "I felt a connection."
end
def on_receive msg
puts "RECEIVED: #{msg}"
send_message msg
end
end
EM.run do
myhost = "0.0.0.0"
myport = 8000
puts "Starting WebSocket server. Listening on port #{myport}..."
EM.start_server myhost, myport, EchoServer
end
html文件:
<!DOCTYPE html> <html> <head><title>Test</title>
<script type="text/javascript">
var myWebSocket = new WebSocket("ws://localhost:8000");
myWebSocket.onopen = function(evt) {
console.log("Connection open. Sending message...");
myWebSocket.send("Hello WebSockets!"); };
myWebSocket.onmessage = function(evt) {
console.log(evt.data);
myWebSocket.close(); };
myWebSocket.onclose = function(evt) {
console.log("Connection closed."); };
myWebSocket.onerror = function(err) {
alert(err.name + " => " + err.message); } </script>
</head> <body> <div>Hello</div> </body> </html>
它确实在Safari 5.1.9(这是一个较旧的浏览器)中工作:我在服务器和客户端上都看到了预期的输出.但是,代码在Firefox 21中不起作用:我收到错误消息…
Firefox can't establish a connection to the server at ws://localhost:8000/.
var myWebSocket = new WebSocket("ws://localhost:8000");
我注意到在Firebug和Safari Developer Tools中,服务器都没有发送Sec-WebSocket-Accept标头:
Response Headers
Connection Upgrade
Upgrade WebSocket
WebSocket-Location ws://localhost:8000/
WebSocket-Origin null
Request Headers
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Cache-Control no-cache
Connection keep-alive, Upgrade
DNT 1
Host localhost:8000
Origin null
Pragma no-cache
Sec-WebSocket-Key r9xT+ywe533EHF09wxelkg==
Sec-WebSocket-Version 13
Upgrade websocket
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:21.0) Gecko/20100101 Firefox/21.0
我试过的任何东西都不会使代码在Firefox 21.0中运行.要检查Firefox 21.0是否支持websockets,我去了:
http://www.websocket.org/echo.html
它说我的浏览器确实支持websockets.
3)您是否有必要使用em-websocket-server模块? github上该模块的最后一次修改是在三年前.每当你在ruby代码中看到需要rubygems时,它应该提醒你代码是旧的.我尝试了更新的em-websocket模块,并且我能够使用Firefox 21.0和Safari 5.1.9上的websockets成功地来回传输数据:
require 'em-websocket'
myhost = "0.0.0.0"
myport = 8000
EM.run {
puts "Listening on port #{myport}..."
EM::WebSocket.run(:host => myhost, :port => myport, :debug => false) do |ws|
ws.onopen do |handshake|
path = handshake.path
query_str = handshake.query
origin = handshake.origin
puts "WebSocket opened:"
puts "\t path \t\t -> #{path}"
puts "\t query_str \t -> #{query_str}"
puts "\t origin \t -> #{origin}"
end
ws.onmessage { |msg|
ws.send "Pong: #{msg}"
}
ws.onclose {
puts "WebSocket closed"
}
ws.onerror { |e|
puts "Error: #{e.message}"
}
end
}
相同的客户端代码.现在响应头包括Sec-WebSocket-Accept:
Response Headers
Connection Upgrade
Sec-WebSocket-Accept LyIm6d+kAAqkcTR744tVK9HMepY=
Upgrade websocket
Request Headers
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Cache-Control no-cache
Connection keep-alive, Upgrade
DNT 1
Host localhost:8000
Origin null
Pragma no-cache
Sec-WebSocket-Key pbK8lFHQAF+arl9tFvHn/Q==
Sec-WebSocket-Version 13
Upgrade websocket
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:21.0) Gecko/20100101 Firefox/21.0
在您的代码中,我认为您没有设置任何标头.相反,您只是来回发送包含看起来像标题的字符的消息.显然,您的浏览器在允许连接之前需要响应中的Sec-WebSocket-Accept标头,并且当em-websocket-server模块未能在响应中设置该标头时,您的浏览器会拒绝连接.
em-websockets-server的相关源代码如下所示:
module EM
module WebSocket
module Protocol
module Version76
# generate protocol 76 compatible response headers
def response
response = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
response << "Upgrade: WebSocket\r\n"
response << "Connection: Upgrade\r\n"
response << "Sec-WebSocket-Origin: #{origin}\r\n"
response << "Sec-WebSocket-Location: #{scheme}://#{host}#{path}\r\n"
if protocol
response << "Sec-WebSocket-Protocol: #{protocol}\r\n"
end
response << "\r\n"
response << Digest::MD5.digest(keyset)
response
end
如您所见,它没有设置Sec-WebSocket-Accept标头.该代码位于名为Version76的模块中,并且搜索google for websockets版本76会产生一个过时的协议(其中包含请求和响应的示例):
http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
这是当前的websockets协议(其中还包含请求和响应的示例):
http://tools.ietf.org/html/rfc6455
结论:em-websockets-server已经过时了.
今天关于Websocket - Websocket原理和握手、解密、加密、基于Python实现简单示例的介绍到此结束,谢谢您的阅读,有关Flask+gevent-websocket模块实现websocket、HTML5 websockets与PHP websockets vs node.js websockets?、java WebSocket的实现以及Spring WebSocket示例代码、javascript – 与Ruby和EM :: WebSocket :: Server的WebSocket握手等更多相关知识的信息可以在本站进行查询。
本文标签: