websocket 无需经过轮询服务器的方式以得到响应 同步在线用户数 上线下线 抓包 3-way-handshake web-linux-shell 开发

 

https://code.google.com/archive/p/phpwebsocket/source/default/sourcephp

The WebSocket API (WebSockets) - Web APIs | MDN
https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_APIcss

WebSockets - Web API 接口参考 | MDN
https://developer.mozilla.org/zh-CN/docs/Web/API/WebSockets_APIhtml

WebSockets 是一种先进的技术。它能够在用户的浏览器和服务器之间打开交互式通讯会话。使用此API,您能够向服务器发送消息并接收事件驱动的响应,而无需经过轮询服务器的方式以得到响应。node

接口

WebSocket
用于链接WebSocket服务器的主要接口,以后能够在这个链接上发送 和接受数据。
CloseEvent
链接关闭时WebSocket对象发送的事件。
MessageEvent
当从服务器获取到消息的时候WebSocket对象触发的事件。

工具

  • HumbleNet: 一个在浏览器中工做的跨平台网络库。它由一个围绕websocket和WebRTC的C包装器组成,抽象了跨浏览器的差别,方便了为游戏和其它应用程序建立多用户网络功能。
  • µWebSockets:由C++11Node.js 实现的高度可扩展的WebSocket服务器和客户端.。
  • ClusterWS:  轻量级、快速和强大的框架,用于在Node.js.中构建可伸缩的WebSocket应用程序。
  • Socket.IO: 一个基于长轮询/WebSocket的Node.js第三方传输协议。
  • SocketCluster: 一个用于Node.js的pub/sub专一于可伸缩 WebSocket框架。
  • WebSocket-Node: 一个用 Node.js实现WebSocket服务器API。
  • Total.js:一个用Node.js 实现的的Web应用程序框架(例如:WebSocket聊天)。
  • Faye: 一个 Node.jsWebSocket (双向链接)和 EventSource (单向链接)的服务器和客户端。
  • SignalR: SignalR在可用时将隐藏使用WebSockets,在不可用时将优雅地使用其余技术和技术,而应用程序代码保持不变。
  • Caddy: 可以将任意命令(stdin/stdout)代理为websocket的web服务器。
  • ws: 一个流行的WebSocket客户端和服务器 Node.js库。
  • jsonrpc-bidirectional: 易于使用异步RPC库,经过单个WebSocket或RTCDataChannel (WebRTC)链接支持双向调用。TCP / SCTP /等。客户端和服务器能够各自承载本身的JSONRPC和服务器端点。
  • rpc-websockets: JSON-RPC 2.0在websocket上实现Node.js和JavaScript。

 

 

同步ws链接数来同步在线用户数python

https://websockets.readthedocs.io/en/stable/intro.html#synchronization-examplegit

Synchronization example

A WebSocket server can receive events from clients, process them to update the application state, and synchronize the resulting state across clients.github

Here’s an example where any client can increment or decrement a counter. Updates are propagated to all connected clients.web

The concurrency model of asyncio guarantees that updates are serialized.shell

Run this script in a console:npm

 

#!/usr/bin/env python

# WS server example that synchronizes state across clients

import asyncio
import json
import logging
import websockets

logging.basicConfig()

STATE = {"value": 0}

USERS = set()


def state_event():
    return json.dumps({"type": "state", **STATE})


def users_event():
    return json.dumps({"type": "users", "count": len(USERS)})


async def notify_state():
    if USERS:  # asyncio.wait doesn't accept an empty list
        message = state_event()
        await asyncio.wait([user.send(message) for user in USERS])


async def notify_users():
    if USERS:  # asyncio.wait doesn't accept an empty list
        message = users_event()
        await asyncio.wait([user.send(message) for user in USERS])


async def register(websocket):
    USERS.add(websocket)
    await notify_users()


async def unregister(websocket):
    USERS.remove(websocket)
    await notify_users()


async def counter(websocket, path):
    # register(websocket) sends user_event() to websocket
    await register(websocket)
    try:
        await websocket.send(state_event())
        async for message in websocket:
            data = json.loads(message)
            if data["action"] == "minus":
                STATE["value"] -= 1
                await notify_state()
            elif data["action"] == "plus":
                STATE["value"] += 1
                await notify_state()
            else:
                logging.error("unsupported event: {}", data)
    finally:
        await unregister(websocket)


start_server = websockets.serve(counter, "localhost", 6789)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

  

Then open this HTML file in several browsers.

 

<!DOCTYPE html>
<html>
    <head>
        <title>WebSocket demo</title>
        <style type="text/css">
            body {
                font-family: "Courier New", sans-serif;
                text-align: center;
            }
            .buttons {
                font-size: 4em;
                display: flex;
                justify-content: center;
            }
            .button, .value {
                line-height: 1;
                padding: 2rem;
                margin: 2rem;
                border: medium solid;
                min-height: 1em;
                min-width: 1em;
            }
            .button {
                cursor: pointer;
                user-select: none;
            }
            .minus {
                color: red;
            }
            .plus {
                color: green;
            }
            .value {
                min-width: 2em;
            }
            .state {
                font-size: 2em;
            }
        </style>
    </head>
    <body>
        <div class="buttons">
            <div class="minus button">-</div>
            <div class="value">?</div>
            <div class="plus button">+</div>
        </div>
        <div class="state">
            <span class="users">?</span> online
        </div>
        <script>
            var minus = document.querySelector('.minus'),
                plus = document.querySelector('.plus'),
                value = document.querySelector('.value'),
                users = document.querySelector('.users'),
                websocket = new WebSocket("ws://127.0.0.1:6789/");
            minus.onclick = function (event) {
                websocket.send(JSON.stringify({action: 'minus'}));
            }
            plus.onclick = function (event) {
                websocket.send(JSON.stringify({action: 'plus'}));
            }
            websocket.onmessage = function (event) {
                data = JSON.parse(event.data);
                switch (data.type) {
                    case 'state':
                        value.textContent = data.value;
                        break;
                    case 'users':
                        users.textContent = (
                            data.count.toString() + " user" +
                            (data.count == 1 ? "" : "s"));
                        break;
                    default:
                        console.error(
                            "unsupported event", data);
                }
            };
        </script>
    </body>
</html>

  

WebSocket是一种网络传输协议,可在单个TCP链接上进行全双工通讯,位于OSI模型应用层。WebSocket协议在2011年由IETF标准化为RFC 6455,后由RFC 7936补充规范。Web IDL中的WebSocket API由W3C标准化。

WebSocket使得客户端和服务器之间的数据交换变得更加简单,容许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只须要完成一次握手,二者之间就能够建立持久性的链接,并进行双向数据传输。

 

背景

如今,不少网站为了实现推送技术,所用的技术都是轮询。轮询是在特定的的时间间隔(如每秒),由浏览器对服务器发出HTTP请求,而后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器须要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会消耗不少的带宽资源。

比较新的轮询技术是Comet。这种技术虽然能够实现双向通讯,但仍然须要反复发出请求。并且在Comet中广泛采用的HTTP长链接也会消耗服务器资源。

在这种状况下,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,而且可以更实时地进行通信。

Websocket使用wswss统一资源标志符,相似于HTTPS。其中wss表示使用了TLS的Websocket。如:

ws://example.com/wsapi
wss://secure.example.com/wsapi

Websocket与HTTP和HTTPS使用相同的TCP端口,能够绕过大多数防火墙的限制。默认状况下,Websocket协议使用80端口;运行在TLS之上时,默认使用443端口。

 

优势

  • 较少的控制开销。在链接建立后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的状况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还须要加上额外的4字节的掩码。相对于HTTP请求每次都要携带完整的头部,此项开销显著减小了。
  • 更强的实时性。因为协议是全双工的,因此服务器能够随时主动给客户端下发数据。相对于HTTP请求须要等待客户端发起请求服务端才能响应,延迟明显更少;即便是和Comet等相似的长轮询比较,其也能在短期内更屡次地传递数据。
  • 保持链接状态。与HTTP不一样的是,Websocket须要先建立链接,这就使得其成为一种有状态的协议,以后通讯时能够省略部分状态信息。而HTTP请求可能须要在每一个请求都携带状态信息(如身份认证等)。
  • 更好的二进制支持。Websocket定义了二进制帧,相对HTTP,能够更轻松地处理二进制内容。
  • 能够支持扩展。Websocket定义了扩展,用户能够扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。
  • 更好的压缩效果。相对于HTTP压缩,Websocket在适当的扩展支持下,能够沿用以前内容的上下文,在传递相似的数据时,能够显著地提升压缩率。[14]

握手协议

WebSocket 是独立的、建立在 TCP 上的协议。

Websocket 经过 HTTP/1.1 协议的101状态码进行握手。

为了建立Websocket链接,须要经过浏览器发出请求,以后服务器进行回应,这个过程一般称为“握手”(handshaking)。

 

 

一个典型的Websocket握手请求以下:

客户端请求

GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13

服务器回应

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Location: ws://example.com/

字段说明

  • Connection必须设置Upgrade,表示客户端但愿链接升级。
  • Upgrade字段必须设置Websocket,表示但愿升级到Websocket协议。
  • Sec-WebSocket-Key是随机的字符串,服务器端会用这些数据来构造出一个SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一个特殊字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,而后计算SHA-1摘要,以后进行BASE-64编码,将结果作为“Sec-WebSocket-Accept”头的值,返回给客户端。如此操做,能够尽可能避免普通HTTP请求被误认为Websocket协议。
  • Sec-WebSocket-Version 表示支持的Websocket版本。RFC6455要求使用的版本是13,以前草案的版本均应当弃用。
  • Origin字段是可选的,一般用来表示在浏览器中发起此Websocket链接所在的页面,相似于Referer。可是,与Referer不一样的是,Origin只包含了协议和主机名称。
  • 其余一些定义在HTTP协议中的字段,如Cookie等,也能够在Websocket中使用。

 

抓包验证

 

101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议

 

 zh.wikipedia.org/wiki/传输控制协议

 

 

 

 

 

https://help.aliyun.com/document_detail/66031.html

一.概述

移动端APP大多数功能都能经过客户端向服务器端发送请求,服务器应答来完成。好比:用户注册,获取商品列表等能力。

但有一些场景须要服务器向客户端推送应用内通知,如:用户之间的即时通讯等功能。这种时候就须要创建一个通讯通道,让服务器可以给指定的客户端发送下行通知请求。也就是客户端和服务器端之间具有双向通讯的能力。

具有双向通行能力的架构对于移动APP属于刚性需求。

使用须知

API网关已经在全部Region开放双向通讯能力,双向通讯能力构建于WebSocket协议之上,目前Android,Objective-C,JAVA三种SDK均支持双向通讯。

实现方式

API网关目前已经在全部Region提供双向通讯的能力,用户只须要在API网关上设置三个API,而后下载自动生成的SDK到客户端,简单嵌入到客户端就能完美实现客户端和服务器端之间的双向通讯的功能。

下面是利用API网关实现双向通讯的能力的业务流程简图:

流程描述

(1) 客户端在启动的时候和API网关创建了WebSocket链接,而且将本身的设备ID告知API网关;

(2) 客户端在WebSocket通道上发起注册信令;

(3) API网关将注册信令转换成HTTP协议发送给用户后端服务,而且在注册信令上加上设备ID参数;

(4) 用户后端服务验证注册信令,若是验证经过,记住用户设备ID,返回200应答;

(5) 用户后端服务经过HTTP/HTTPS/WebSocket三种协议中的任意一种向API网关发送下行通知信令,请求中携带接收请求的设备ID;

(6) API网关解析下行通知信令,找到指定设备ID的链接,将下行通知信令经过WebSocket链接发送给指定客户端;

(7) 客户端在不想收到用户后端服务通知的时候,经过WebSocket链接发送注销信令给API网关,请求中不携带设备ID;

(8) API网关将注销信令转换成HTTP协议发送给用户后端服务,而且在注册信令上加上设备ID参数;

(9) 用户后端服务删除设备ID,返回200应答。

二.双向通讯三种管理信令

要使用API网关的双向通讯能力,首先要了解API网关双向通讯相关的三种信令,须要注意的是,这三个信令其实就是API网关上的三个API,须要用户去API网关建立后才能使用。

1.注册信令

注册信令是客户端发送给用户后端服务的信令,起到两个做用:

(1)将客户端的设备ID发送给用户后端服务,用户后端服务须要记住这个设备ID。用户不须要定义设备ID字段,设备ID字段由API网关的SDK自动生成;

(2)用户能够将此信令定义为携带用户名和密码的API,用户后端服务在收到注册信令的验证客户端的合法性。用户后端服务在返回注册信令应答的时候,返回非200时,API网关会视此状况为注册失败。

客户端要想收到用户后端服务发送过来的通知,须要先发送注册信令给API网关,收到用户后端服务的200应答后正式注册成功。

2.下行通知信令

用户后端服务,在收到客户端发送的注册信令后,记住注册信令中的设备ID字段,而后就能够向API网关发送接收方为这个设备的下行通知信令了。只要这个设备在线,API网关就能够将此下行通知发送到端。

3.注销信令

客户端在不想收到用户后端服务的通知时发送注销信令发送给API网关,收到用户后端服务的200应答后注销成功,再也不接受用户后端服务推送的下行消息。

 

 

#!/usr/bin/env python

# WS server example that synchronizes state across clients
# https://pypi.org/project/websockets/

import asyncio
import json
import logging
import websockets

logging.basicConfig()

STATE = {"value": 0}
CMD = {'cmdStr': '', 'cmdRet': ''}
TIME = {'now': ''}
USERS = set()


def state_event():
    return json.dumps({"type": "state", **STATE, **CMD, **TIME})


def users_event():
    return json.dumps({"type": "users", "count": len(USERS)})


def cmd_event():
    return json.dumps({'type': 'cmd', "cmdStr": CMD['cmdStr'], 'cmdRet': CMD['cmdRet']})


async def notify_state():
    if USERS:  # asyncio.wait doesn't accept an empty list
        message = state_event()
        await asyncio.wait([user.send(message) for user in USERS])


async def notify_users():
    if USERS:  # asyncio.wait doesn't accept an empty list
        message = users_event()
        await asyncio.wait([user.send(message) for user in USERS])


async def run_cmd():
    cmd = CMD['cmdStr']
    proc = await asyncio.create_subprocess_shell(cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
    stdout, stderr = await proc.communicate()
    output = ''
    if stdout:
        output += f'[stdout]\n{stdout.decode()}'
    if stderr:
        output += f'[stderr]\n{stderr.decode()}'
    CMD['cmdRet'] = output
    message = cmd_event()
    print(message)
    await asyncio.wait([user.send(message) for user in USERS])


async def register(websocket):
    USERS.add(websocket)
    await notify_users()


async def unregister(websocket):
    USERS.remove(websocket)
    await notify_users()


async def counter(websocket, path):
    # register(websocket) sends user_event() to websocket
    await register(websocket)
    try:
        await websocket.send(state_event())
        async for message in websocket:
            data = json.loads(message)
            if data["action"] == "minus":
                STATE["value"] -= 1
                await notify_state()
            elif data["action"] == "plus":
                STATE["value"] += 1
                await notify_state()
            elif data["action"] == "cmd":
                CMD['cmdStr'] = data['cmdStr']
                await run_cmd()
            else:
                logging.error("unsupported event: {}", data)
    finally:
        await unregister(websocket)


host, port = '0.0.0.0', 6789

start_server = websockets.serve(counter, host, port)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

  

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>WebSocket demo-Shell</title>
    <style type="text/css">
        body {
            font-family: "Courier New", sans-serif;
            text-align: center;
        }

        .buttons {
            font-size: 4em;
            display: flex;
            justify-content: center;
        }

        .button, .value {
            line-height: 1;
            padding: 2rem;
            margin: 2rem;
            border: medium solid;
            min-height: 1em;
            min-width: 1em;
        }

        .button {
            cursor: pointer;
            user-select: none;
        }

        .minus {
            color: red;
        }

        .plus {
            color: green;
        }

        .value {
            min-width: 2em;
        }

        .state {
            font-size: 2em;
        }
    </style>
</head>
<body>
<div class="buttons">
    <div class="minus button">-</div>
    <div class="value">?</div>
    <div class="plus button">+</div>
</div>
<div class="state">
    <span class="users">?</span> online
</div>
<input id="cmdStr" style="margin-top: 4em;width: 50em;height: 10em;" type="text" id="input" name="name" required
       placeholder="请输入shell命令">
<button id="cmdBtn" style="color: red">cmdBtn--TODO监听键盘</button>
<pre id="cmdRet">
3
</pre>
<script>
var minus = document.querySelector('.minus'),
    plus = document.querySelector('.plus'),
    value = document.querySelector('.value'),
    users = document.querySelector('.users'),
    cmdStr = document.getElementById('cmdStr'),
    cmdBtn = document.getElementById('cmdBtn'),
    cmdRet = document.getElementById('cmdRet'),
    // websocket = new WebSocket("ws://192.168.11.228:6789/");
    websocket = new WebSocket("ws://192.168.11.215:6789/");
minus.onclick = function (event) {
    websocket.send(JSON.stringify({action: 'minus'}));
}

plus.onclick = function (event) {
    websocket.send(JSON.stringify({action: 'plus'}));
}

cmdBtn.onclick = function (event) {
    websocket.send(JSON.stringify({action: 'cmd', cmdStr: cmdStr.value}));
}
websocket.onmessage = function (event) {
    data = JSON.parse(event.data);
    switch (data.type) {
        case 'state':
            value.textContent = data.value;
            break;
        case 'users':
            users.textContent = (
                data.count.toString() + " user" +
                (data.count == 1 ? "" : "s"));
            break;
        case 'cmd':
            cmdRet.textContent = data.cmdRet;
            break;
        default:
            console.error(
                "unsupported event", data);
    }
};
</script>
</body>
</html>
相关文章
相关标签/搜索