flask4 + websocket

2019.7.15javascript

Http協議

Http: 超文本傳輸協議 傳輸:Socket TCP 3次握手 斷開 四次揮手html

無狀態請求鏈接前端

TCP 3次握手 發起一次請求 - 一次響應 - 鏈接就斷開了java

http://www.baidu.com 長時間無請求時,不佔用任何資源jquery

劣勢:web

無狀態 - 
好比 : 
客戶端1向服務器發起請求 - 服務器確認客戶端1的身份 - 處理 - 響應 - 斷開
客戶端1向服務器發起請求 - 服務器確認客戶端1的身份 - 處理 - 響應 - 斷開

服務器主動向客戶端推送消息json

WeChat - 微信     
‘你’發送一條微信 - 給服務器 - ‘你好!’ - 服務器收到消息  - 服務器尋找 ‘我’ - 對着個人鏈接 (客戶端) - 發送 你好!

WeChatPay 
AliPay
1.輪詢 對客戶端 以及服務器要求最高

客戶端輪詢 - 由客戶端向服務器發起請求 每秒10次左右(QPS 每秒發起請求的次數限制)flask

場景模擬後端

傳達室 收發快遞模型 

1.大爺(接收快遞) - 小二 半年以後 扔掉
2.大爺(接收快遞) - 小二 -小二從宿舍跑到傳達室 
    - 問大爺 有我快遞嗎?
    -大爺回答:你丫誰呀?  我是大三小二
    - 我知道了,你找我什麼事兒? - 小二 : 有我快遞嗎?  - 大爺:我給你找找,沒找着,你回去把,一會過來
    - 小二 :大爺再見 再見 再見 再見
    - 小二到宿舍想起來快遞沒拿
    - 又去傳達室取快遞(重複上面的過程)
    
- 不少個小二 同事問大爺快遞 
知道好久之後
- 小二 我是大三的小二
- 我知道了,你彙總啊我什麼事兒? 
- 小二 : 有我快遞嗎?
- 大爺: 我這兒真有你的快遞
- 小二 拿走快遞,飛奔二區
- 小二:大爺再見 再見 再見 再見

​ 小資源訪問時 優點很明顯 - 由於客戶端較少瀏覽器

2.長輪詢

服務器長輪詢 - 服務器不響應客戶端而是將鏈接暫時保持住 5 秒以後 沒有消息,響應客戶端

必定時間以後 5s以後 斷開鏈接, 客戶端從新發起請求

情景模擬:

大爺(接收快遞) - 小二
傳達室裝修了 - 創建了一個茶室 - 服務升級了
小二到傳達室 - 問大爺有我快遞嗎?  
- 大爺: 你丫誰呀?
- 小二:我是大三小二
- 大爺:我知道了,你找我什麼事兒? 
- 小二: 有我快遞嗎? 
- 大爺:進屋作作,屋裏有差,我給你找找快遞
- 小二喝茶中。。。。。。
- 大爺:你多等會
- 小二喝茶中。。。。。。
- 大爺:你多等會
- 小二喝茶中。。。。。。
- 小二: 大爺,我去趟廁所
- 斷開
- 小二上完廁所,不對呀,我又不是去喝茶的
- 小二來到傳達室 loop


小二到傳達室 - 問大爺有我快遞嗎?  
- 大爺: 你丫誰呀?
- 小二:我是大三小二
- 大爺:我知道了,你找我什麼事兒? 
- 小二: 有我快遞嗎? 
- 大爺:進屋作作,屋裏有差,我給你找找快遞
- 小二喝茶中。。。。。。
- 大爺說 找到了,你的快遞
- 小二拿着快遞美美的回去了 斷開了

優點:

小二不用那麼累了 客戶端不用大量QPS

劣勢:

傳達室的茶室空間 嚴重浪費服務器資源

大爺學會了影分身 開啓多線程服務客戶端

3.長鏈接 客戶端和 服務器資源 消耗沒有那麼嚴重了

鏈接保持 - Http 發起請求在請求中寫一個協議 - WebSocket - 收到請求,自動保持此鏈接 - 永久不斷開, 除非主動斷開 - 能夠經過此鏈接主動找到客戶端

場景模擬

大爺(接收快遞) - 小二
傳達室進入信息化時代,裝電話了 2萬臺電話
小二到傳達室 - 問大爺有我快遞嗎?  
- 大爺: 你丫誰呀?
- 小二:我是大三小二
- 大爺:我知道了,你找我什麼事兒? 
- 小二: 有我快遞嗎? 
- 大爺:你把電話號碼留下,回去等我電話,接了以後別掛
- 小二回到宿舍證號聽見大爺來電話了
- 小二接電話 , 對面,我是你大爺,電話別掛,隨時聽着,有快遞就過來取
- 保持通話

優點:數據實時性

劣勢:服務器和客戶端須要一個線程來等待消息

玩遊戲:一個服務器有資源限制,因此分區,不少個服務器

服務器 完成 IO多路複用

2 . GeventWebsocket + Flask

Web (Http) + Socket (鏈接保持)

Flask中運行WebSocket - GeventWebsocket

wsgi?

wsgi ---> envir ---> view

wsgi ---> http ---> envir ---> view

wsgi ---> websockethandler ---> envir ---> view

websockethandler 監聽了 請求頭中的?

ws environ 和 headers

ws請求 environ 原始信息
{'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_SOFTWARE': 'gevent/1.4 Python/3.6', 'SCRIPT_NAME': '', 'wsgi.version': (1, 0), 'wsgi.multithread': False, 'wsgi.multiprocess': False, 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, 'SERVER_NAME': 'Win7-2019WPWAAA', 'SERVER_PORT': '9537', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/my_socket', 'QUERY_STRING': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REMOTE_ADDR': '127.0.0.1', 'REMOTE_PORT': '50844', 'HTTP_HOST': '127.0.0.1:9537', 'HTTP_CONNECTION': 'Upgrade', 'HTTP_PRAGMA': 'no-cache', 'HTTP_CACHE_CONTROL': 'no-cache', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36', 'HTTP_UPGRADE': 'websocket', 'HTTP_ORIGIN': 'http://localhost:49419', 'HTTP_SEC_WEBSOCKET_VERSION': '13', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9', 'HTTP_COOKIE': 'csrftoken=5L4cgBIxsjanDPXGuTBlOk5ihhrn8LYnSppNIG4LyEclJHsC6MOC7AR9K1jbDJUO; sessionid=ofv3e4j3kmwo26wrimax68wz5t56rmr3', 'HTTP_SEC_WEBSOCKET_KEY': 'KNmnx/BwHQ1R3Q+DxJNRGQ==', 'HTTP_SEC_WEBSOCKET_EXTENSIONS': 'permessage-deflate; client_max_window_bits', 'wsgi.input': <gevent.pywsgi.Input object at 0x0000000003C85A08>, 'wsgi.input_terminated': True, 'wsgi.websocket_version': '13', 'wsgi.websocket': <geventwebsocket.websocket.WebSocket object at 0x0000000003CB60B0>, 'werkzeug.request': <Request 'http://127.0.0.1:9537/my_socket' [GET]>}

'wsgi.websocket': <geventwebsocket.websocket.WebSocket object at 0x0000000003CB60B0>,

http environ
#{'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_SOFTWARE': 'gevent/1.4 Python/3.6', 'SCRIPT_NAME': '', 'wsgi.version': (1, 0), 'wsgi.multithread': False, 'wsgi.multiprocess': False, 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, 'SERVER_NAME': 'Win7-2019WPWAAA', 'SERVER_PORT': '9537', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/my_socket', 'QUERY_STRING': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REMOTE_ADDR': '127.0.0.1', 'REMOTE_PORT': '50450', 'HTTP_HOST': '127.0.0.1:9537', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_CACHE_CONTROL': 'max-age=0', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9', 'HTTP_COOKIE': 'csrftoken=5L4cgBIxsjanDPXGuTBlOk5ihhrn8LYnSppNIG4LyEclJHsC6MOC7AR9K1jbDJUO; sessionid=ofv3e4j3kmwo26wrimax68wz5t56rmr3', 'wsgi.input': <gevent.pywsgi.Input object at 0x0000000003A65A08>, 'wsgi.input_terminated': True, 'werkzeug.request': <Request 'http://127.0.0.1:9537/my_socket' [GET]>}
http - readers
Host: 127.0.0.1:9537
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: csrftoken=5L4cgBIxsjanDPXGuTBlOk5ihhrn8LYnSppNIG4LyEclJHsC6MOC7AR9K1jbDJUO

ws
WebSocket {url: "ws://127.0.0.1:9537/my_socket", readyState: 3, bufferedAmount: 0, onopen: null, onerror: null, …}

ws - headers
Host: 127.0.0.1:9537
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36
Upgrade: websocket          # == websocket 請求 websockethandler處理的key  握手的信息
Origin: http://localhost:49419
Sec-Websocket-Version: 13   #
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: csrftoken=5L4cgBIxsjanDPXGuTBlOk5ihhrn8LYnSppNIG4LyEclJHsC6MOC7AR9K1jbDJUO; sessionid=ofv3e4j3kmwo26wrimax68wz5t56rmr3
Sec-Websocket-Key: jiytsu3cA3JWnElexaajjA==     # 公鑰
Sec-Websocket-Extensions: permessage-deflate; client_max_window_bits

和一個東西計算出私鑰 , 匹配出來的值能不能創建起聯繫

html 代碼:

<script type="application/javascript">

ws = new WebSocket('ws://127.0.0.1:9537/my_socket');

WebSocket {url: "ws://127.0.0.1:9537/my_socket", readyState: 0, bufferedAmount: 0, onopen: null, onerror: null, …}

ws

WebSocket {url: "ws://127.0.0.1:9537/my_socket", readyState: 1, bufferedAmount: 0, onopen: null, onerror: null, …}

readyState: 0

0 鏈接沒有創建好

1 創建好了 保持鏈接

2 客戶端主動斷開 (由於是客戶端的,看不出來,不是服務器發的)

3 服務器主動斷開鏈接 (1次就斷開)

VM258:1 WebSocket is already in CLOSING or CLOSED state.

<script type="application/javascript">
var ws = new WebSocket('ws/127.0.0.1:9537/my_socket')
http_serv = WSGIServer(('0.0.0.0',9537),app,handler_class=WebSocketHandler) #WSH 如何處理ws請求的

websocket 快速實現功能

要實現大的功能1G : 用底層

想法:

jquery

微信頁面(微信我的資料)

加上js?

多併發

ql

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>羣聊</title>
</head>
<body>
<p><input type="text" id="content"><button onclick="send_msg()">發送</button></p>

<div id="chat_list">

</div>
</body>
<script type="application/javascript">
  var ws = new WebSocket("ws://192.168.16.40:9527/my_socket");
  // 監聽電話
  ws.onmessage = function (eventMessage) {
      console.log(eventMessage.data);
      var p = document.createElement("p");
      p.innerText = eventMessage.data;
      document.getElementById("chat_list").appendChild(p);
  };

  function send_msg() {
      var content = document.getElementById("content").value;
      ws.send(content);
  };

</script>
</html>

py

# 客戶端
# 服務端
# Http  Flask 瀏覽器
# Websocket GeventWebsocket+Flask 客戶端JavaScript(Websocket客戶端)

from flask import Flask,request,render_template
from geventwebsocket.handler import WebSocketHandler  # 提供WS協議處理
from geventwebsocket.server import WSGIServer  # 承載服務
from geventwebsocket.websocket import WebSocket  # 語法提示

app = Flask(__name__)

user_socket_list = []

@app.route("/my_socket")
def my_socket():
    # 獲取當前客戶端與服務器的Socket鏈接
    user_socket = request.environ.get("wsgi.websocket") # type:WebSocket
    if user_socket:
        user_socket_list.append(user_socket)
        print(len(user_socket_list),user_socket_list)
    # print(user_socket,"OK 鏈接已經創建好了,接下來發消息吧")
    while 1:
        msg = user_socket.receive()
        print(msg)

        for usocket in user_socket_list:
            try:
                usocket.send(msg)
            except:
                continue

        # user_socket.send(msg)

        # print(request.headers)


@app.route("/gc")
def gc():
    return render_template("gc.html")



if __name__ == '__main__':
    # app.run("0.0.0.0",9527)
    http_serv = WSGIServer(("0.0.0.0",9527),app,handler_class=WebSocketHandler)
    http_serv.serve_forever()

sl.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>羣聊</title>
</head>
<body>
<p>個人暱稱:<input type="text" id="username">
  <button onclick="loginGc()">登陸</button>
</p>
<p>給<input type="text" id="to_user">發送<input type="text" id="content">
  <button onclick="send_msg()">發送</button>
</p>

<div id="chat_list">

</div>
</body>
<script type="application/javascript">
    var ws = null;


    function loginGc() {
        var username = document.getElementById("username").value;
        ws = new WebSocket("ws://192.168.16.40:9527/my_socket/" + username);
        // 監聽電話
        ws.onmessage = function (eventMessage) {
            console.log(eventMessage.data);
            str_obj = JSON.parse(eventMessage.data);

            var p = document.createElement("p");
            p.innerText = str_obj.from_user +" : "+str_obj.chat;
            document.getElementById("chat_list").appendChild(p);
        };
    };

    function send_msg() {
        var username = document.getElementById("username").value;
        var to_user = document.getElementById("to_user").value;
        var content = document.getElementById("content").value;
        var sendStr = {
            from_user:username,
            to_user:to_user,
            chat:content
        };
        ws.send(JSON.stringify(sendStr));
    };

</script>
</html>

py

# 客戶端
# 服務端
# Http  Flask 瀏覽器
# Websocket GeventWebsocket+Flask 客戶端JavaScript(Websocket客戶端)
import json

from flask import Flask,request,render_template
from geventwebsocket.handler import WebSocketHandler  # 提供WS協議處理
from geventwebsocket.server import WSGIServer  # 承載服務
from geventwebsocket.websocket import WebSocket  # 語法提示

app = Flask(__name__)

# user_socket_dict = {nicheng:lianjie}
user_socket_dict = {}

@app.route("/my_socket/<username>")
def my_socket(username):
    # 獲取當前客戶端與服務器的Socket鏈接
    user_socket = request.environ.get("wsgi.websocket") # type:WebSocket
    if user_socket:
        user_socket_dict[username] = user_socket
        print(len(user_socket_dict),user_socket_dict)
    # print(user_socket,"OK 鏈接已經創建好了,接下來發消息吧")
    while 1:
        msg = user_socket.receive()
        msg_dict = json.loads(msg)
        # msg = user_socket.receive()
        to_user_nick = msg_dict.get("to_user")
        print(to_user_nick)
        to_user_socket = user_socket_dict.get(to_user_nick)
        to_user_socket.send(msg)




@app.route("/sl")
def sl():
    return render_template("sl.html")



if __name__ == '__main__':
    # app.run("0.0.0.0",9527)
    http_serv = WSGIServer(("0.0.0.0",9527),app,handler_class=WebSocketHandler)
    http_serv.serve_forever()

錯誤 1 net::ERR_CONNECTION_REFUSED

ws = new WebSocket('ws://192.168.16.40:9537/gc');
WebSocket is already in CLOSING or CLOSED state.

錯誤 2

ws = new WebSocket('ws://192.168.16.40:9537/my_socket');
WebSocket {url: "ws://192.168.16.40:9537/my_socket", readyState: 0, bufferedAmount: 0, onopen: null, onerror: null, …}
VM65:1 WebSocket connection to 'ws://192.168.16.40:9537/my_socket' failed: Error in connection establishment:

發錯了地址,發到了 40端口,老師的端口了

gc.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<p><input type="text" id="content"><button onclick="send_msg()">發送</button></p>
<div id="chat_list">

</div>
</body>
<script type="application/javascript">
//  // 監聽電話
var ws = new WebSocket('ws://127.0.0.1:9537/my_socket');

ws.onmessage = function (eventMessage) {
     console.log(eventMessage.data);
     var p = document.createElement('p');
      p.innerText = eventMessage.data;
     document.getElementById('chat_list').appendChild(p);
};

function send_msg() {
    var content = document.getElementById('content').value;
    ws.send(content);
}
</script>
</html>

老 gc .html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <meta name="viewport" content="width=device-width, initial-scale=1">


</head>
<body>

<!--放聊天記錄-->


<p>個人暱稱:<input type="text" id="username"><button onclick="loginGc()">登錄</button></p>

<p><input type="text" id="content"> <button onclick="send_msg()">發送</button></p>

<div id="chat_list">

</div>


<script type="application/javascript">
var ws = null;

//  // 監聽電話
// ws.onmessage = function (eventMessage) {
//     console.log(eventMessage.data);
//     var p = document.createElement('p');
//     p.innerText = eventMessage.data;
//     document.getElementById('chat_list').appendChild(p);
// };


function loginGc() {
    var username = document.getElementById('username').value;
    var ws = new WebSocket('ws://127.0.0.1:9537/my_socket/');
    ws.send(content);

    ws.onmessage = function (eventMessage) {
    console.log(eventMessage.data);
    var p = document.createElement('p');
    p.innerText = eventMessage.data;
    document.getElementById('chat_list').appendChild(p);
    };

}

function send_msg() {
    var content = document.getElementById('content').value;
    var username = document.getElementById('username').value;
    var ws = new WebSocket('ws://127.0.0.1:9537/my_socket/');
    var sendStr = {
        from_user : username,
        chat : content
    };
    ws.send(content);
}


</script>
</body>

</html>

ws.onmessage = function (eventMessage) {
eventMessage : 和request似的?

函數做用?

function send_msg() {
      var content = document.getElementById("content").value;
      ws.send(content);
  }

Uncaught ReferenceError: send_msg is not defined

var ws = new WebSocket("ws://192.168.16.90:9537/my_socket");
  // 監聽電話
  ws.onmessage = function (eventMessage) {
      console.log(eventMessage.data);
      var p = document.createElement("p");
      p.innerText = eventMessage.data;
      document.getElementById("chat_list").appendChild(p);
  };

沒有發送函數也能夠實現增長信息,只不過服務器端不顯示

<p><input type="text" id="content"><button onclick="send_msg()">發送</button></p>

並非,好像是沒有數據了? 這樣 數據是發過去又發回來的,爲何不直接使用呢

for usocket in user_socket_list:
try:
usocket.send(msg)       #前端接收的eventmesaage.data 就是這個msg
# print('haole')
except:
continue

也不知道改動的什麼地方,莫名其妙就行了

由於看不出那裏有錯,照抄的老師的,還把前端後端都換成老師的,而後去看視頻,發現老師是沒放在羣聊的文件夾裏,因此就把他提了出來,就好使了,而後再去運行以前的,也好使了。不知道錯誤在哪?還會出錯的,對嗎?

告訴你:

不是 from flask import Flask,request,render_template

而是 import json 腦子怎麼長的?奇特!

前端寫 JSON 大寫 額

ws.send(JSON.stringify(sendStr));

laoqunliao

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <meta name="viewport" content="width=device-width, initial-scale=1">


</head>
<body>

<!--放聊天記錄-->


<p>個人暱稱:<input type="text" id="username"><button onclick="loginGc()">登錄</button></p>

<p><input type="text" id="content"> <button onclick="send_msg()">發送</button></p>

<div id="chat_list">

</div>

</body>


<script type="application/javascript">
var ws = null;

function loginGc() {
    var username = document.getElementById('username').value;

 ws = new WebSocket('ws://192.168.16.90:9537/my_socket/'+username);

    ws.onmessage = function (eventMessage) {
    console.log(eventMessage.data);
    var p = document.createElement('p');
    p.innerText = eventMessage.data;
    document.getElementById('chat_list').appendChild(p);
    };

}

function send_msg() {
    var content = document.getElementById('content').value;
    var username = document.getElementById('username').value;


    var sendStr = {
        from_user : username,
        chat : content
    };
ws.send(JSON.stringify(sendStr));
};


</script>


</html>
str_obj = JSON.parse(eventMessage.data);
p.innerText = str_obj.from_user +" : "+str_obj.chat;

顯示 ----> dasf : adfas

p.innerText = eventMessage.data;

顯示 ----> {"from_user":"adas","chat":"ada"}

這個函數果真沒大有實際做用,由於經過直接打開html , 錯誤由於無法識別 tmplates 嗎?

# @app.route("/gcs")
# def gc():
#     return render_template("gcs.html")

那個make jinja 了 哪一個能夠被識別好像 那就能夠藍圖文件夾用公有資源了把?!

錯誤緣由?好幾回了

ws.send(JSON.stringify(sendStr));

是我沒點登錄 ? 發的是空
Uncaught TypeError: Cannot read property 'send' of null

AttributeError: 'NoneType' object has no attribute 'send' # kong不能打.send

Cannot read property 'send' of null

是由於沒有用戶,不支持離線。亮哥說。解決了,好似,可是我以前怎麼可已發過去?也沒有確認用戶存在因此接收功能吧?

多我的聊天,因此須要另一我的,不能本身和本身聊吧

源碼講解 :當http直接訪問 而不是ws

錯誤返回:

null not revice()

大意:

websockethandler 也是 WSGI的 只不過封裝了

繼承的WSGIHandler 除了可以處理標準的http,還能夠處理webserver

GUID magic_string 用來作通訊協議驗證的

self.environ.update ({'wsgi.websocket':None}) #http仍是web都有environ原始信息

self.websocket = None #都給了None

只有websocket 纔有http_update , http請求沒有

讀源碼的時候各類記筆記: 防止忘了 #

ws登陸 ws://192.168.16.90 登陸 aaa sec 密鑰 。。。

self.websocket = Websocket(self.environ,) #夯住了 新的長鏈接一直保持着

http -> websocket

environ.update = {''} # http請求進來 沒有http-upgrate 只有None , None無法receive

明天講websocket 握手原理 和 加密 (有時間的話)

源碼:
SUPPORTED_VERSIONS = ('13', '8', '7')
GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
self.environ.update({
    'wsgi.websocket': None
})
self.websocket = None
upgrade = self.environ.get('HTTP_UPGRADE', '').lower()

'HTTP_UPGRADE': 'websocket'

if upgrade == 'websocket':
    connection = self.environ.get('HTTP_CONNECTION', '').lower()    #加到了connection裏面

'HTTP_CONNECTION': 'Upgrade' ws都有 這兩個key

if 'upgrade' not in connection:
        # This is not a websocket request, so we must not handle it
        self.logger.warning("Client didn't ask for a connection "
                            "upgrade")
        return
else:
    # This is not a websocket request, so we must not handle it
    return
if self.request_version != 'HTTP/1.1':              # websocket 默認是http/1.1  略過

'HTTP_SEC_WEBSOCKET_VERSION': '13', #

if self.environ.get('HTTP_SEC_WEBSOCKET_VERSION'):
    return self.upgrade_connection()

def upgrade_connection(self):

version = self.environ.get("HTTP_SEC_WEBSOCKET_VERSION")

SUPPORTED_VERSIONS = ('13', '8', '7')

爲Sec-WebSocket-Accept 作準備

key = self.environ.get("HTTP_SEC_WEBSOCKET_KEY", '').strip()
key_len = len(base64.b64decode(key))

http 請求不就是tcp請求,tcp請求本質上不就是socket請求,因此把 def upgrade_connection(self):

​ #Validate and 'upgrade' the HTTP request to a WebSocket request. ---> self.websocket

若是Sec-WebSocket-Accept匹配對了,一直保持着

self.websocket = WebSocket(self.environ, Stream(self), self)    #建立一個新的鏈接 保持長鏈接     # 裏面是init函數
self.environ.update({
    'wsgi.websocket_version': version,
    'wsgi.websocket': self.websocket        #把鏈接放到wsig裏了    
})
class WebSocket(object):
    def __init__(self, environ, stream, handler):   #self == WSGIServer? ?WebSocketHandler
if PY3:
    accept = base64.b64encode(          #accept驗證經過的字符串
        hashlib.sha1((key + self.GUID).encode("latin-1")).digest()
    ).decode("latin-1")         #經過以後才能一直鏈接
else:
    accept = base64.b64encode(hashlib.sha1(key + self.GUID).digest())
headers = [
    ("Upgrade", "websocket"),
    ("Connection", "Upgrade"),
    ("Sec-WebSocket-Accept", accept)    
]

走了這麼多,給http請求的返回了none,給ws請求的返回了 websocket(不知道是什麼)

程序的部分,爲何會錯

http請求,源碼裏,wsgi.websocket 給的none 而後就返回了

user_socket = request.environ.get("wsgi.websocket") # type:WebSocket    
if user_socket:
    user_socket_list.append(user_socket)
    print(len(user_socket_list),user_socket_list)
# print(user_socket,"OK 鏈接已經創建好了,接下來發消息吧")
while 1:
    msg = user_socket.receive()     # send_msg 發過來的信息   # http請求這裏是None ,
    print(msg)

因此,AttributeError: 'NoneType' object has no attribute 'receive'

代碼整理一下

一個發送框,一直髮送,由於while了的,還得跳出來嗎? .py
from flask import Flask,request,render_template
from geventwebsocket.handler import WebSocketHandler    #提供WS協議
from geventwebsocket.server import WSGIServer   #承載服務
from geventwebsocket.websocket import WebSocket # 語法提示

app = Flask(__name__)
user_socket_list = []   #存放每一個socket的列表

@app.route('/ql')
def ql():
    return render_template('ql1.html')

@app.route('/my_socket')
def my_socket():
    # 獲取當前客戶端與服務器的socket鏈接
    user_socket = request.environ.get('wsgi.websocket') #type:WebSocket
    if user_socket:
        user_socket_list.append(user_socket)    # 加入多個用戶
        print(len(user_socket_list),user_socket_list)   #1 [<geventwebsocket.websocket.WebSocket object at 0x0000000003CB90B0>]
    while 1 :
        msg = user_socket.receive()  # 接收前端發過來的信息
        print(msg)
        for usocket in user_socket_list:
            try:
                usocket.send(msg)
            except :
                continue         #斷開鏈接的 不影響整個羣聊

if __name__ == '__main__':
    http_serv = WSGIServer(('0.0.0.0',9527),app,handler_class=WebSocketHandler)
    http_serv.serve_forever()
一個的前端.html
</head>
<body>
<p><input type="text" id="content"> <button onclick="send_msg()">發送</button></p>

<div id="chat_list">

</div>

</body>

<script type="application/javascript">      // 爲何?  application 格式的?   // 做用    添加聊天信息在chat_list那
var ws = new WebSocket('ws://192.168.16.90:9527/my_socket');
// 監聽電話             
    ws.onmessage = function (eventMessage) {              // eventMessage 存放信息
        console.log(eventMessage.data);                  // document 方法? 
        var p = document.createElement('p');             // 建立p標籤
        p.innerText = eventMessage.data;              // 數據寫入p data是前面後端傳過來的
        document.getElementById('chat_list').appendChild(p);   // 加入到前面列表
    };
function send_msg(){             // 做用 , 發給後端,而且激活 onmessage 
    var content = document.getElementById('content').value;
    ws.send(content);
};

</script>
羣暱稱.html
<p>個人暱稱:<input type="text" id="username"><button onclick="loginGc()">登錄</button></p>

<p><input type="text" id="content"> <button onclick="send_msg()">發送</button></p>

<div id="chat_list">

</div>

</body>


<script type="application/javascript">
var ws = null;

function loginGc() {
    var username = document.getElementById('username').value;

 ws = new WebSocket('ws://192.168.16.90:9537/my_socket/'+username);

    ws.onmessage = function (eventMessage) {
    console.log(eventMessage.data);
    str_obj = JSON.parse(eventMessage.data);
    var p = document.createElement('p');
     p.innerText = str_obj.from_user +" : "+str_obj.chat;
    document.getElementById('chat_list').appendChild(p);
    };

}

function send_msg() {
    var content = document.getElementById('content').value;
    var username = document.getElementById('username').value;


    var sendStr = {
        from_user : username,
        chat : content
    };
ws.send(JSON.stringify(sendStr));

};


</script>
羣聊暱稱.py
import json

user_socket_dict = {}

@app.route("/my_socket/<username>")
def my_socket(username):
    # 獲取當前客戶端與服務器的Socket鏈接
    user_socket = request.environ.get("wsgi.websocket") # type:WebSocket
    if user_socket:
        user_socket_dict[username] = user_socket
        print(len(user_socket_dict),user_socket_dict)
    # print(user_socket,"OK 鏈接已經創建好了,接下來發消息吧")
    while 1:
        # msg = json.loads(user_socket.receive())
        msg = user_socket.receive()
        print(msg)
        for usocket in user_socket_dict.values():
            print(usocket)
            try:
                usocket.send(msg)
            except:
                continue
私聊.html
<p>個人暱稱:<input type="text" id="username">
  <button onclick="loginGc()">登陸</button>
</p>
<p>給<input type="text" id="to_user">發送<input type="text" id="content">
  <button onclick="send_msg()">發送</button>
</p>

<div id="chat_list">

</div>
</body>
<script type="application/javascript">
    var ws = null;


    function loginGc() {
        var username = document.getElementById("username").value;
        ws = new WebSocket("ws://192.168.16.90:9537/my_socket/" + username);
        // 監聽電話
        ws.onmessage = function (eventMessage) {
            console.log(eventMessage.data);
            str_obj = JSON.parse(eventMessage.data);

            var p = document.createElement("p");
            p.innerText = str_obj.from_user +" : "+str_obj.chat;
            document.getElementById("chat_list").appendChild(p);
        };
    };

    function send_msg() {
        var username = document.getElementById("username").value;
        var to_user = document.getElementById("to_user").value;
        var content = document.getElementById("content").value;
        var sendStr = {
            from_user:username,
            to_user:to_user,
            chat:content
        };
        ws.send(JSON.stringify(sendStr));
    };

</script>
私聊.html
app = Flask(__name__)

# user_socket_dict = {nicheng:lianjie}
user_socket_dict = {}

@app.route("/my_socket/<username>")
def my_socket(username):
    # 獲取當前客戶端與服務器的Socket鏈接
    user_socket = request.environ.get("wsgi.websocket") # type:WebSocket
    if user_socket:
        user_socket_dict[username] = user_socket        # bbbb = {gevent
        print(len(user_socket_dict),'dict',user_socket_dict)
    # print(user_socket,"OK 鏈接已經創建好了,接下來發消息吧")
    while 1:
        msg = user_socket.receive()
        msg_dict = json.loads(msg)
        # msg = user_socket.receive()
        to_user_nick = msg_dict.get("to_user")  #'bbbb'
        print('toueser-nick',to_user_nick)
        to_user_socket = user_socket_dict.get(to_user_nick)     #'bbbb'
        to_user_socket.send(msg)

@app.route("/sl")
def sl():
    return render_template("sl.html")
相關文章
相關標籤/搜索