Workerman如何在WebSocket响应中添加Header

Workerman如何在WebSocket响应中添加Header,第1张

最近在用 Workerman 学习 WebSocket 的时候,遇到了一个问题:

结果浏览器报了这个错:

查阅了 RFC文档 得知问题在于新建WebSocket时的第二个参数,表示客户端接受的子协议

子协议是什么?比如浏览器向服务端发起两个WebSocket连接,一条连接使用明文传输数据,另一条使用加密传输数据。

服务端可以开启两个端口分别处理明文和加密连接。也可以只开一个端口,通过客户端提供的子协议来判断该连接传输数据的方式。

那么新建WebSocket时的子协议参数,最终是以什么形式发送到服务端的?就藏在请求Header的 Sec-Websocket-Protocol 中:

服务端接收到 Sec-Websocket-Protocol 后,必须在响应 Header 中也包含 Sec-Websocket-Protocol ,取值和客户端传来的一样。否则浏览器就会报错: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was receive

再提一点,子协议参数可以是个数组,比如:

表示该WebSocket连接既接收字符格式的数据,也可以接收JSON格式的数据,这时服务端必须在两者中选择其一返回给浏览器。

有点扯远了,今天的主角是 Workerman。Workerman 要获得子协议参数,需要在 onConnect 回调中设置 onWebSocketConnect 回调:

然后通过 $_SERVER['HTTP_SEC_WEBSOCKET_PROTOCOL'] 获得浏览器发来的子协议参数,是个字符串。如果浏览器接受多个子协议,那么协议之间会以 逗号 分隔。方便起见,例子中总是返回第一种子协议。

服务端响应时,将 Sec-Websocket-Protocol 字段放入 $connection->headers 数组中,这样就完成了子协议的协商。

WebSocket protocol 是HTML5一种新的协议(protocol)。它是实现了浏览器与服务器全双工通信(full-duplex)。

现在,很多网站为了实现即时通讯(real-time),所用的技术都是轮询(polling)。轮询是在特定的的时间间隔(time interval)(如每1秒),由浏览器对服务器发出HTTP request,然后由服务器返回最新的数据给客服端的浏览器。这种传统的HTTP request d的模式带来很明显的缺点 – 浏览器需要不断的向服务器发出请求(request),然而HTTP request 的header是非常长的,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽。

而最比较新的技术去做轮询的效果是Comet – 用了AJAX。但这种技术虽然可达到全双工通信,但依然需要发出请求(reuqest)。

在 WebSocket API,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。在此WebSocket 协议中,为我们实现即使服务带来了两大好处:

1. Header

互相沟通的Header是很小的-大概只有 2 Bytes

2. Server Push

服务器可以主动传送数据给客户端

下面实现一个简单PUSH例子如下:

服务端代码:

public class InitServlet extends HttpServlet {

/**

*

*/

private static final long serialVersionUID = 1L

private static List<MessageInbound>socketList

public void init(ServletConfig config) throws ServletException {

InitServlet.socketList = new ArrayList<MessageInbound>()

super.init(config)

System.out.println("Server start============")

}

public static List<MessageInbound>getSocketList() {

return InitServlet.socketList

}

}

public class TestWebSocketServlet extends WebSocketServlet{

private static final Logger log = Logger.getLogger(TestWebSocketServlet.class)

/**

*

*/

private static final long serialVersionUID = 1L

//存储链接的容器

private static List<WebSocketMessageInbound>connsList = new ArrayList<WebSocketMessageInbound>()

@Override

protected StreamInbound createWebSocketInbound(String subProtocol,HttpServletRequest request) {

// TODO Auto-generated method stub

return new WebSocketMessageInbound()

}

public class WebSocketMessageInbound extends MessageInbound{

@Override

protected void onClose(int status) {

//InitServlet.getSocketList().remove(this)

super.onClose(status)

log.debug("onClose")

InitServlet.getSocketList().remove(this)

}

@Override

protected void onOpen(WsOutbound outbound) {

log.debug("onOpen")

super.onOpen(outbound)

InitServlet.getSocketList().add(this)

}

@Override

protected void onBinaryMessage(ByteBuffer message) throws IOException {

// TODO Auto-generated method stub

log.debug("onBinaryMessage")

}

@Override

protected void onTextMessage(CharBuffer message) throws IOException {

// TODO Auto-generated method stub

log.debug("onTextMessage="+message)

// this.getWsOutbound().writeTextMessage(CharBuffer.wrap("===="))

// this.getWsOutbound().writeTextMessage(message)

//发送给所有链接的

for (MessageInbound messageInbound : InitServlet.getSocketList()) {

CharBuffer buffer = CharBuffer.wrap(message)

WsOutbound outbound = messageInbound.getWsOutbound()

outbound.writeTextMessage(buffer)

outbound.flush()

}

}

}

}

web.xml配置

<!-- WebSocket -->

<servlet>

<servlet-name>initServlet</servlet-name>

<servlet-class>com.demo.websocket.InitServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet>

<servlet-name>websocket</servlet-name>

<servlet-class>com.demo.websocket.TestWebSocketServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>websocket</servlet-name>

<url-pattern>/websocket</url-pattern>

</servlet-mapping>

前台代码:

<html>

<head>

<title>WebSoket Demo</title>

<script type="text/javascript">

//验证浏览器是否支持WebSocket协议

if (!window.WebSocket) {

alert("WebSocket not supported by this browser!")

}

var ws

function display() {

//var valueLabel = document.getElementById("valueLabel")

//valueLabel.innerHTML = ""

ws=new WebSocket("ws://localhost:8082/springmvc/websocket")

//监听消息

ws.onmessage = function(event) {

//valueLabel.innerHTML+ = event.data

log(event.data)

}

// 打开WebSocket

ws.onclose = function(event) {

//WebSocket Status:: Socket Closed

}

// 打开WebSocket

ws.onopen = function(event) {

//WebSocket Status:: Socket Open

//// 发送一个初始化消息

ws.send("Hello, Server!")

}

ws.onerror =function(event){

//WebSocket Status:: Error was reported

}

}

var log = function(s) {

if (document.readyState !== "complete") {

log.buffer.push(s)

} else {

document.getElementById("contentId").innerHTML += (s + "\n")

}

}

function sendMsg(){

var msg=document.getElementById("messageId")

//alert(msg.value)

ws.send(msg.value)

}

</script>

</head>

<body onload="display()">

<div id="valueLabel"></div>

<textarea rows="20" cols="30" id="contentId"></textarea>

<br/>

<input name="message" id="messageId"/>

<button id="sendButton" onClick="javascript:sendMsg()" >Send</button>

</body>

</html>


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/bake/11270887.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-14
下一篇2023-05-14

发表评论

登录后才能评论

评论列表(0条)

    保存