websocket的简单使用

一 轮询

 什么是轮询:设置每一段时间去访问一次服务器,然后服务器返回最新的数据。这样服务器的压力会非常的大,并且还会有延迟。适用于小型程序。

 实现:再客户端的页面设置一个定时发送请求的任务,每个这段时间就要发送一个请求,来获取最新的数据

 实例:

  app.py

from flask import Flask,render_template

app = Flask(__name__)

@app.route(‘/index‘)
def index():
    return render_template(‘index.html‘)

@app.route(‘/message‘)
def message():
    return "信息"

if __name__ == ‘__main__‘:
    app.run()

  index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>投票系统</h1>

    <script src="{{ url_for(‘static‘,filename=‘jquery-3.3.1.min.js‘)}}"></script>
    <script>
        function getMsg() {
            $.ajax({
                url:‘/message‘,
                success:function (data) {
                    console.log(data)
                }
            })
        }
        setInterval(getMsg,2000)

    </script>
</body>
</html>

二 长轮询

 什么是长轮询:就是客户端发送一次请求,服务端接收到了请求,然后将请求hold住,直到其中某个请求发送数据过来,然后再挨个将最新的数据分享到每一个请求,客户端接收到请求以后,继续发送新的请求。在服务端设置了超时时间,如果超出这段时间,就会断开连接,服务端端口连接以后,客户端又会继续发送新的请求。

 实现原理:队列,就是服务端只有一接收到最新的数据,就会将书更新,然后再逐一发送给每一个请求。客户端创建一个对列,将数据通过队列发给了服务端,然后断开队列,然后服务器端在于每一个请求之间创建对列,只要客户端接收到了数据就会断开然后继续发送请求。

 实例:

  app.py:

from flask import Flask,render_template,request,session,redirect,jsonify
import uuid
from queue import Queue,Empty

app = Flask(__name__)
app.secret_key = ‘xfsdfqw‘

USERS = {
    ‘1‘:{‘name‘:‘王旭‘,‘count‘:0},
    ‘2‘:{‘name‘:‘放景洪‘,‘count‘:0},
    ‘3‘:{‘name‘:‘六五‘,‘count‘:0},
}

QUEUE_DICT = {

}
"""
{
    强哥:queue()
    龙哥:queue()
}
"""

@app.before_request
def before_request():
    if request.path == ‘/login‘:
        return None
    user_info = session.get(‘user_info‘)
    if user_info:
        return None
    return redirect(‘/login‘)

@app.route(‘/login‘,methods=[‘GET‘,‘POST‘])
def login():
    if request.method == "GET":
        return render_template(‘login.html‘)
    else:
        uid = str(uuid.uuid4())
        session[‘user_info‘] = {‘id‘:uid,‘name‘:request.form.get(‘user‘)}
        QUEUE_DICT[uid] = Queue()
        return redirect(‘/index‘)

@app.route(‘/index‘)
def index():
    return render_template(‘index.html‘,users=USERS)

@app.route(‘/message‘)
def message():

    result = {‘status‘:True,‘msg‘:None}
    queue = QUEUE_DICT[session.get(‘user_info‘).get(‘id‘)]
    try:
        v = queue.get(timeout=10)
    except Empty as e:
        v = None
    result[‘msg‘] = v
    return jsonify(result)

@app.route(‘/vote‘)
def vote():
    userid = request.args.get(‘id‘)
    old = USERS[userid][‘count‘]
    new = old + 1
    USERS[userid][‘count‘] = new

    for k,v in QUEUE_DICT.items():
        v.put({‘userid‘:userid,‘count‘:new})

    return ‘投票成功‘

if __name__ == ‘__main__‘:
    app.run(threaded=True)

  login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post">
    <input type="text" name="user">
    <input type="submit" value="提交">
</form>
</body>
</html>

  index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>投票系统</h1>
    <ul>
        {% for k,v in users.items() %}
            <li id="user_{{k}}"  ondblclick="vote(‘{{k}}‘)">{{v.name}} <span>{{v.count}}</span> </li>
        {% endfor %}

    </ul>

    <script src="{{ url_for(‘static‘,filename=‘jquery-3.3.1.min.js‘)}}"></script>
    <script>
        $(function () {
            getMsg();
        });

        function getMsg() {
            $.ajax({
                url:‘/message‘,
                success:function (data) {
                    if(data.msg){
                        var nid = "#user_"+data.msg.userid;
                        $(nid).find(‘span‘).text(data.msg.count);
                    }
                    getMsg();
                }
            })
        }

        function vote(id) {
            $.ajax({
                url:‘/vote‘,
                data:{‘id‘:id},
                success:function (data) {
                    console.log(‘投票成功‘);
                }
            })
        }
    </script>
</body>
</html>

三 websocket

 为什么要有websocket

  初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?

  答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。

  举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。

  

  这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。

  轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。

 websocket的介绍:

  服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

  建立的socket创建TCP之上:与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

  数据格式比较轻量,性能开销小,通信高效

  可以发送文本,也可以发送二进制数据。

  没有同源限制,客户端可以与任意服务器通信

  协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

   HTTP协议:http://www.xxx.com    请求+响应+断开  一次请求一次响应

   WebSocket:ws://example.com:80/some/path

    1. 浏览器:发送请求“随机字符串”
    2. 服务端:加密,再返还
    3. 浏览器:检测加密后的结果是否是正确的
    4. 正确:建立连接【再也不断开】

    

  详细信息:http://www.ruanyifeng.com/blog/2017/05/websocket.html

 websocket的本质:

  浏览器:发送链接

  服务器:接受链接

  浏览器:发送握手信息Sec-WebSocket-Key: dCp5MdkY90EIJ83Qdddpjw==\r\n

  服务器:base64(sha1(dCp5MdkY90EIJ83Qdddpjw== + magic string))

  浏览器:接收值,并在内部进行校验

    校验成功:on_open

    校验失败:验证失败

  相互之间进行收发消息:

   浏览器发送消息到服务器:

1. 获取第二个字节的,前7位
2.
    7位 <= 125;+0;mask_key=4;数据
    7位 == 126;+2;mask_key=4;数据
    7位 == 127;+8;mask_key=4;数据
3. mask_key=4;数据

   服务器向浏览器推送消息:1. 数据头+数据

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 ...                |
+---------------------------------------------------------------+

 请求过程解析:

  启动服务器:启动Socket服务器过后,等待着用户的链接,然后进行数据的收发

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‘, 8002))
sock.listen(5)
# 等待用户连接
conn, address = sock.accept()
...
...
...

  客户端连接:当客户端向服务器发送连接的请求的时候,不仅发送连接请求,还会发送握手的信息,并等待服务器的响应,至此链接才会创建成功

<script type="text/javascript">
    var socket = new WebSocket("ws://127.0.0.1:8002/xxoo");
    ...
</script>

  建立握手: 

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‘, 8002))
sock.listen(5)
# 获取客户端socket对象
conn, address = sock.accept()
# 获取客户端的【握手】信息
data = conn.recv(1024)
...
...
...
conn.send(‘响应【握手】信息‘)

   请求和响应的【握手】信息需要遵循规则:

    从请求【握手】信息中提取 Sec-WebSocket-Key
    利用magic_string 和 Sec-WebSocket-Key 进行hmac1加密,再进行base64加密
    将加密结果响应给客户端

   请求握手的信息为:

GET /chatsocket HTTP/1.1
Host: 127.0.0.1:8002
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://localhost:63342
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: mnwFxiOlctXFN/DeMt1Amg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
...
...

   提取Sec-WebSocket-Key的值并且加密:

import socket
import base64
import hashlib

def get_headers(data):
    """
    将请求头格式化成字典
    :param data:
    :return:
    """
    header_dict = {}
    data = str(data, encoding=‘utf-8‘)

    for i in data.split(‘\r\n‘):
        print(i)
    header, body = data.split(‘\r\n\r\n‘, 1)
    header_list = header.split(‘\r\n‘)
    for i in range(0, len(header_list)):
        if i == 0:
            if len(header_list[i].split(‘ ‘)) == 3:
                header_dict[‘method‘], header_dict[‘url‘], header_dict[‘protocol‘] = header_list[i].split(‘ ‘)
        else:
            k, v = header_list[i].split(‘:‘, 1)
            header_dict[k] = v.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‘, 8002))
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://%s%s\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‘), headers[‘Host‘], headers[‘url‘])
# 响应【握手】信息
conn.send(bytes(response_str, encoding=‘utf-8‘))
...
...
...

  客户端和服务端进行收发数据:收发数据时需要封包和解包,客户端的javascript类的库已经封装好了封包和解包的过程,但是socket服务器需要手动实现

   获取客户端发送的数据进行解包:

info = conn.recv(8096)

    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‘)
    print(body)

    解包的过程:

      

    The MASK bit simply tells whether the message is encoded. Messages from the client must be masked, so your server should expect this to be 1. (In fact, section 5.1 of the spec says that your server must disconnect from a client if that client sends an unmasked message.) When sending a frame back to the client, do not mask it and do not set the mask bit. We‘ll explain masking later. Note: You have to mask messages even when using a secure socket.RSV1-3 can be ignored, they are for extensions.

    The opcode field defines how to interpret the payload data: 0x0 for continuation, 0x1 for text (which is always encoded in UTF-8), 0x2 for binary, and other so-called "control codes" that will be discussed later. In this version of WebSockets, 0x3 to 0x7 and 0xB to 0xF have no meaning.

    The FIN bit tells whether this is the last message in a series. If it‘s 0, then the server will keep listening for more parts of the message; otherwise, the server should consider the message delivered. More on this later.

    Decoding Payload Length

    To read the payload data, you must know when to stop reading. That‘s why the payload length is important to know. Unfortunately, this is somewhat complicated. To read it, follow these steps:

        Read bits 9-15 (inclusive) and interpret that as an unsigned integer. If it‘s 125 or less, then that‘s the length; you‘re done. If it‘s 126, go to step 2. If it‘s 127, go to step 3.
        Read the next 16 bits and interpret those as an unsigned integer. You‘re done.
        Read the next 64 bits and interpret those as an unsigned integer (The most significant bit MUST be 0). You‘re done.

    Reading and Unmasking the Data

    If the MASK bit was set (and it should be, for client-to-server messages), read the next 4 octets (32 bits); this is the masking key. Once the payload length and masking key is decoded, you can go ahead and read that number of bytes from the socket. Let‘s call the data ENCODED, and the key MASK. To get DECODED, loop through the octets (bytes a.k.a. characters for text data) of ENCODED and XOR the octet with the (i modulo 4)th octet of MASK. In pseudo-code (that happens to be valid JavaScript):

    var DECODED = "";
    for (var i = 0; i < ENCODED.length; i++) {
        DECODED[i] = ENCODED[i] ^ MASK[i % 4];
    }

    Now you can figure out what DECODED means depending on your application.

   向客户端发送数据进行封包:

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 <= 0xFFFF:
        token += struct.pack("!BH", 126, length)
    else:
        token += struct.pack("!BQ", 127, length)

    msg = token + msg_bytes
    conn.send(msg)
    return True

 实例1:websocket本质

  s1.py:

import socket
import base64
import hashlib
import redis

# conn = redis.Redis()
# conn.blpop()

def get_headers(data):
    """
    将请求头格式化成字典
    :param data:
    :return:
    """
    header_dict = {}
    data = str(data, encoding=‘utf-8‘)
    header, body = data.split(‘\r\n\r\n‘, 1)
    header_list = header.split(‘\r\n‘)
    for i in range(0, len(header_list)):
        if i == 0:
            if len(header_list[i].split(‘ ‘)) == 3:
                header_dict[‘method‘], header_dict[‘url‘], header_dict[‘protocol‘] = header_list[i].split(‘ ‘)
        else:
            k, v = header_list[i].split(‘:‘, 1)
            header_dict[k] = v.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 <= 0xFFFF:
        token += struct.pack("!BH", 126, length)
    else:
        token += struct.pack("!BQ", 127, length)

    msg = token + msg_bytes
    conn.send(msg)
    return True

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((‘127.0.0.1‘, 8002))
sock.listen(5)
# 等待用户连接
conn, address = sock.accept()
print(‘有用户来连接了‘,conn,address)

data = conn.recv(8096)

headers = get_headers(data) # 提取请求头信息
print(‘用户发送过来的握手信息‘,headers[‘Sec-WebSocket-Key‘])

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_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://%s%s\r\n\r\n"

response_str = response_tpl % (ac.decode(‘utf-8‘), headers[‘Host‘], headers[‘url‘])

conn.send(bytes(response_str, encoding=‘utf-8‘))

while True:
    info = conn.recv(8096)
    # 1. 获取第2个字节 content[1] & 127
    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‘)
    print(body)
    body = body + ‘ sb‘

    send_msg(conn,body.encode(‘utf-8‘))

  s1.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script type="text/javascript">
    /*
    1. 创建socket
    2. 发送【握手(验证)信息】
     */
    var socket = new WebSocket("ws://127.0.0.1:8002/xxoo");
    socket.onopen = function () {
        /* 与服务器端连接成功后,自动执行 */
        console.log(‘服务端加密规则正确,连接成功‘);
    }
    socket.onmessage = function (event) {
        /* 服务器端向客户端发送数据时,自动执行 */
        var response = event.data;
        console.log(‘获取websocekt推送的消息:‘,response)
    };
</script>
</body>
</html>

 实例2:

  app.py:

from flask import Flask,render_template,request,session,redirect,jsonify
import uuid
from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer
import json

app = Flask(__name__)
app.secret_key = ‘xfsdfqw‘

USERS = {
    ‘1‘:{‘name‘:‘王旭‘,‘count‘:0},
    ‘2‘:{‘name‘:‘放景洪‘,‘count‘:0},
    ‘3‘:{‘name‘:‘六五‘,‘count‘:0},
}

@app.before_request
def before_request():
    if request.path == ‘/login‘:
        return None
    user_info = session.get(‘user_info‘)
    if user_info:
        return None
    return redirect(‘/login‘)

@app.route(‘/login‘,methods=[‘GET‘,‘POST‘])
def login():
    if request.method == "GET":
        return render_template(‘login.html‘)
    else:
        uid = str(uuid.uuid4())
        session[‘user_info‘] = {‘id‘:uid,‘name‘:request.form.get(‘user‘)}
        return redirect(‘/index‘)

@app.route(‘/index‘)
def index():
    return render_template(‘index.html‘,users=USERS)

WS_DICT = {

}

@app.route(‘/message‘)
def message():
    if request.environ.get(‘wsgi.websocket‘):
        ws = request.environ[‘wsgi.websocket‘]
        # 1. 刚连接成功
        uid = session.get(‘user_info‘).get(‘id‘)
        WS_DICT[uid] = ws

        from geventwebsocket.websocket import WebSocket
        while True:
            # 2. 等待用户发送消息,并接受
            message = ws.receive()
            # 关闭:message=None
            if not message:
                del WS_DICT[uid]
                break

            old = USERS[message][‘count‘]
            new = old + 1
            USERS[message][‘count‘] = new

            data = {‘user‘:message,‘count‘:new}

            for k,v in WS_DICT.items():
                # 3. 向客户端推送消息
                v.send(json.dumps(data))

    return "Connected!"

if __name__ == ‘__main__‘:
    http_server = WSGIServer((‘127.0.0.1‘, 5000), app, handler_class=WebSocketHandler)
    http_server.serve_forever()    

  login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post">
    <input type="text" name="user">
    <input type="submit" value="提交">
</form>
</body>
</html>

  index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>投票系统</h1>
    <a onclick="closeConn();">关闭连接</a>
    <a onclick="createConn();">创建连接</a>
    <ul>
        {% for k,v in users.items() %}
            <li id="user_{{k}}"  ondblclick="vote(‘{{k}}‘)">{{v.name}} <span>{{v.count}}</span> </li>
        {% endfor %}

    </ul>

    <script src="{{ url_for(‘static‘,filename=‘jquery-3.3.1.min.js‘)}}"></script>
    <script>

        var socket = null;

        function socketInit() {
            socket.onopen = function () {
            /* 与服务器端连接成功后,自动执行 */
        };

            socket.onmessage = function (event) {
                /* 服务器端向客户端发送数据时,自动执行 */
                var response = JSON.parse(event.data); // {‘user‘:1,‘count‘:new}
                var nid = ‘#user_‘ + response.user;
                $(nid).find(‘span‘).text(response.count)
            };

            socket.onclose = function (event) {
                /* 服务器端主动断开连接时,自动执行 */
            };

        }

        /*
        我要投票
        id:帅哥id
         */
        function vote(id) {

            socket.send(id);
        }

        function closeConn() {
            socket.close()
        }
        function createConn() {
            socket = new WebSocket("ws://127.0.0.1:5000/message");
            socketInit();
        }
    </script>
</body>
</html>

  highchart的使用:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>投票系统</h1>
    <ul>
        {% for k,v in users.items() %}
            <li id="user_{{k}}"  ondblclick="vote(‘{{k}}‘)">{{v.name}} <span>{{v.count}}</span> </li>
        {% endfor %}

    </ul>

    <div id="container" style="width: 400px;height:300px;"></div>

    <script src="{{ url_for(‘static‘,filename=‘jquery-3.3.1.min.js‘)}}"></script>
    <script src="{{ url_for(‘static‘,filename=‘js/highcharts.js‘)}}"></script>
    <script src="{{ url_for(‘static‘,filename=‘js/highcharts-3d.js‘)}}"></script>
    <script src="{{ url_for(‘static‘,filename=‘highcharts-zh_CN.js‘)}}"></script>
    <script>
        var chart = null;
        var options = {
            chart: {
                type: ‘column‘,
                options3d: {
                    enabled: true,
                    alpha: 15,
                    beta: 15,
                    viewDistance: 25,
                    depth: 40
                },
                marginTop: 80,
                marginRight: 40
            },
            title: {
                text: ‘全栈7期最帅的男人‘
            },
            xAxis: {
                visible:false
            },
            yAxis: {
                allowDecimals: false,
                min: 0,
                title: {
                    text: ‘得票‘
                }
            },
            tooltip: {
                headerFormat: ‘<b>{point.key}</b><br>‘,
                pointFormat: ‘<span style="color:{series.color}">\u25CF</span> {series.name}: {point.y} ‘
            },
            plotOptions: {
                column: {
                    //stacking: ‘normal‘,
                    //depth: 40
                }
            },
            series: [{
                    name: ‘王旭‘,
                    data: [5]
                }, {
                    name: ‘放景洪‘,
                    data: [3]
                }, {
                    name: ‘刘武‘,
                    data: [2]
                }
            ]
        };

        $(function () {
            chart = Highcharts.chart(‘container‘, options);
            getMsg();
        });

        function getMsg() {
            $.ajax({
                url:‘/message‘,
                success:function (data) {
                    if(data.msg){
                        var nid = "#user_"+data.msg.userid;
                        $(nid).find(‘span‘).text(data.msg.count);
                        // chart.series[0].data[0].update(18);
                    }
                    getMsg();
                }
            })
        }

        function vote(id) {
            $.ajax({
                url:‘/vote‘,
                data:{‘id‘:id},
                success:function (data) {
                    console.log(‘投票成功‘);
                }
            })
        }
    </script>
</body>
</html>

 实例3:flaskchat

  服务端:安装支持websocket的组件   使用的是gevent-websocket  django使用的是:channel。而Tornado原生支持

  客户端:支持js创建socket

  x1.py:  

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer
from flask import Flask, request, render_template, abort

app = Flask(__name__)

@app.route(‘/‘)
def index():
    return render_template(‘chat.html‘)

@app.route(‘/message‘)
def message():
    if request.environ.get(‘wsgi.websocket‘):
        ws = request.environ[‘wsgi.websocket‘]

        from geventwebsocket.websocket import WebSocket
        while True:
            message = ws.receive()
            if not message:
                break
            print(message)
            ws.send(message)

    return "Connected!"

if __name__ == ‘__main__‘:
    http_server = WSGIServer((‘‘, 5000), app, handler_class=WebSocketHandler)
    http_server.serve_forever()

  chat.py:

from __future__ import print_function

import json

from gevent import monkey
monkey.patch_all()

from flask import Flask, render_template
from werkzeug.debug import DebuggedApplication

from geventwebsocket import WebSocketServer, WebSocketApplication, Resource

flask_app = Flask(__name__)
flask_app.debug = True

class ChatApplication(WebSocketApplication):
    def on_open(self):
        print("Some client connected!")

    def on_message(self, message):
        if message is None:
            return

        message = json.loads(message)

        if message[‘msg_type‘] == ‘message‘:
            self.broadcast(message)
        elif message[‘msg_type‘] == ‘update_clients‘:
            self.send_client_list(message)

    def send_client_list(self, message):
        current_client = self.ws.handler.active_client
        current_client.nickname = message[‘nickname‘]

        self.ws.send(json.dumps({
            ‘msg_type‘: ‘update_clients‘,
            ‘clients‘: [
                getattr(client, ‘nickname‘, ‘anonymous‘)
                for client in self.ws.handler.server.clients.values()
            ]
        }))

    def broadcast(self, message):
        for client in self.ws.handler.server.clients.values():
            client.ws.send(json.dumps({
                ‘msg_type‘: ‘message‘,
                ‘nickname‘: message[‘nickname‘],
                ‘message‘: message[‘message‘]
            }))

    def on_close(self, reason):
        print("Connection closed!")

@flask_app.route(‘/‘)
def index():
    return render_template(‘index.html‘)

WebSocketServer(
    (‘0.0.0.0‘, 8000),

    Resource([
        (‘^/chat‘, ChatApplication),
        (‘^/.*‘, DebuggedApplication(flask_app))
    ]),

    debug=False
).serve_forever()

  chat.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Python聊天室</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="container" style="border: 1px solid #dddddd;margin: 20px;min-height: 500px;">

</div>

<script src="{{ url_for(‘static‘, filename=‘jquery-2.1.4.min.js‘) }}"></script>
<script type="text/javascript">
    $(function () {
        wsUpdater.start();
    });

    var wsUpdater = {
        socket: null,
        uid: null,
        start: function () {
            var url = "ws://127.0.0.1:5000/message";
            wsUpdater.socket = new WebSocket(url);
            wsUpdater.socket.onmessage = function (event) {
                console.log(event);
                if (wsUpdater.uid) {
                    wsUpdater.showMessage(event.data);
                } else {
                    wsUpdater.uid = event.data;
                }
            }
        },
        showMessage: function (content) {
            $(‘#container‘).append(content);
        }
    };

    function sendMsg() {
        var msg = {
            uid: wsUpdater.uid,
            message: $("#txt").val()
        };
        wsUpdater.socket.send(JSON.stringify(msg));
    }

    function closeConn() {
        wsUpdater.socket.close();
    }
</script>

</body>
</html>

四 Tornado框架

 Tornado是一个支持WebSocket的优秀框架,其内部原理正如1~5步骤描述,当然Tornado内部封装功能更加完整。

 服务端:自己原生支持websocket

 浏览器:支持使用js创建socket

 框架介绍:

            Tornado:短小精悍+第三方库的支持+异步非阻塞
              Flask:短小精悍+第三方库的支持
             Django:重武器
         自定义框架:...
             bottle: 微型(1000)

 Tornado安装:pip install tornado

 Tornado的详细信息:http://www.cnblogs.com/wupeiqi/tag/Tornado/

 实例:以下是基于Tornado实现的聊天室示例:

  app.py:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import uuid
import json
import tornado.ioloop
import tornado.web
import tornado.websocket

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.render(‘index.html‘)

class ChatHandler(tornado.websocket.WebSocketHandler):
    # 用户存储当前聊天室用户
    waiters = set()
    # 用于存储历时消息
    messages = []

    def open(self):
        """
        客户端连接成功时,自动执行
        :return:
        """
        ChatHandler.waiters.add(self)
        uid = str(uuid.uuid4())
        self.write_message(uid)

        for msg in ChatHandler.messages:
            content = self.render_string(‘message.html‘, **msg)
            self.write_message(content)

    def on_message(self, message):
        """
        客户端连发送消息时,自动执行
        :param message:
        :return:
        """
        msg = json.loads(message)
        ChatHandler.messages.append(message)

        for client in ChatHandler.waiters:
            content = client.render_string(‘message.html‘, **msg)
            client.write_message(content)

    def on_close(self):
        """
        客户端关闭连接时,,自动执行
        :return:
        """
        ChatHandler.waiters.remove(self)

def run():
    settings = {
        ‘template_path‘: ‘templates‘,
        ‘static_path‘: ‘static‘,
    }
    application = tornado.web.Application([
        (r"/", IndexHandler),
        (r"/chat", ChatHandler),
    ], **settings)
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
    run()

  index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Python聊天室</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="container" style="border: 1px solid #dddddd;margin: 20px;min-height: 500px;">

    </div>

    <script src="/static/jquery-2.1.4.min.js"></script>
    <script type="text/javascript">
        $(function () {
            wsUpdater.start();
        });

        var wsUpdater = {
            socket: null,
            uid: null,
            start: function() {
                var url = "ws://127.0.0.1:8888/chat";
                wsUpdater.socket = new WebSocket(url);
                wsUpdater.socket.onmessage = function(event) {
                    console.log(event);
                    if(wsUpdater.uid){
                        wsUpdater.showMessage(event.data);
                    }else{
                        wsUpdater.uid = event.data;
                    }
                }
            },
            showMessage: function(content) {
                $(‘#container‘).append(content);
            }
        };

        function sendMsg() {
            var msg = {
                uid: wsUpdater.uid,
                message: $("#txt").val()
            };
            wsUpdater.socket.send(JSON.stringify(msg));
        }

</script>

</body>
</html>

原文地址:https://www.cnblogs.com/fangjie0410/p/8718992.html

时间: 2024-11-19 08:12:52

websocket的简单使用的相关文章

基于html5 localStorage , web SQL, websocket的简单聊天程序

new function() { var ws = null; var connected = false; var serverUrl; var connectionStatus; var sendMessage; var connectButton; var disconnectButton; var sendButton; var open = function() { var url = serverUrl.val(); ws = new WebSocket(url); ws.onope

websocket(二) websocket的简单实现,识别用户属性的群聊

没什么好说的,websocket实现非常简单,我们直接看代码. 运行环境:jdk8 tomcat8 无须其他jar包. 具体环境支持自己百度 package com.reach.socketController; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CopyOnWriteArraySet; import javax.annot

websocket实现简单聊天程序

程序的流程图: 主要代码: 服务端 app.js 先加载所需要的通信模块: var express = require('express'); var app = express(); var http = require('http').createServer(app); var io = require('socket.io').listen(http); var fs = require('fs'); 创建用户列表和消息列表: var person = []; var history =

WebSocket 最简单的用法

1 package com.uptop.websocket; 2 3 import javax.websocket.*; 4 import javax.websocket.server.ServerEndpoint; 5 6 import java.io.IOException; 7 import java.util.concurrent.CopyOnWriteArraySet; 8 9 10 /** 11 * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义

用swoole和websocket开发简单聊天室

首先,我想说下写代码的一些习惯,第一,任何可配置的参数或变量都要写到一个config文件中.第二,代码中一定要有日志记录和完善的报错并记录报错.言归正传,swoole应该是每个phper必须要了解的,它号称重新定义了php.此聊天室利用了swoole高并发并且异步非阻塞的特点提高了程序的性能. 首先,定义一个 swoole_lock 和 swoole_websocket_server ,并且配置参数,具体参数详情可以去swoole官网查看. public function start(){ $t

Unity中Websocket的简单使用

首先我们需要一个websocket服务器,之前的博文中有做Tomcat架设简单Websocket服务器用的时候打开就行了,先不管它 Unity中新建场景建UI(UGUI)有一个连接按钮Button一个信息输入框InputField一个发送按钮Button一个断开按钮Button一个消息显示框Text 场景中建一个GameObject,在上面加个脚本,就叫WSMgr好了用到了BestHTTP这个插件 using System.Collections; using System.Collection

u3d:使用websocket最简单的测试通信

使用插件:besthttp 1.10 u3d版本:5.4.0 代码:找的网上的一篇代码,自己偷懒没写 只简单测试了下 using System.Collections; using System.Collections.Generic; using UnityEngine; using BestHTTP; using BestHTTP.WebSocket; using System; using BestHTTP.Examples; using UnityEngine.UI; using Sys

WebSocket实现简单的在线聊天

SuperWebSocket在WebService中的应用 最开始使用是寄托在IIS中,发布之后测试时半个小时就会断开,所以改为WindowsService 1. 新建Windows服务项目[TestWindowsService],重命名Service1为MyWebSocketService 2. 打开MyWebSocketService设计视图,右键,添加安装程序,自动添加ProjectInstaller.cs. 打开设计视图,选中ServiceInstaller1,右键修改属性: Servi

学习WebSocket(一):Spring WebSocket的简单使用

1.什么是websocket? WebSocket协议定义了一种web应用的新功能,它实现了服务器端和客户端的全双工通信.全双工通信即通信的双方可以同时发送和接收信息 的信息交互方式.它是继Java applets, XMLHttpRequest, Adobe Flash, ActiveXObject等使web应用更具交互性的新技术. 在实现连线过程中,浏览器和服务器通过TCP三次握手建立连接. 如果和服务器连接成功后,浏览器通过HTTP发送握手请求,如果服务器同意握手连接,客户端和服务端之后就