1. 程式人生 > >python 實現websocket

python 實現websocket

nop lse ces soc __init__ entos 防火 我們 lin

python中websocket需要我們自己實現握手代碼,流程是這樣:服務端啟動websocket服務,並監聽。當客戶端連接過來時,(需要我們自己實現)服務端就接收客戶端的請求數據,拿到請求頭,根據請求頭信息封裝響應頭,並將響應頭發給前端,這樣就完成了一次握手,接下來服務端和客戶端才可以通信。

上代碼,我的代碼只涉及到服務端發消息給客戶端的情況

先說一下代碼涉及到的知識

1、單例模式

2、多線程

3、redis

4、websokcet

5、在docker容器中運行

#!/usr/bin python
# -*- coding:UTF-8 -*-
import redis
import time, threading, sched, json, socket, base64, hashlib,logging,traceback

allkv_json 
= "" # 發送給前端的數據 conn_list = [] # 處於在線的socket鏈接 # 單例 def singleton(cls): instances = {} def getinstatce(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return getinstatce @singleton class RedisUtils:
def __init__(self, port, db): self.redis_object = redis.Redis(127.0.0.1, port, db) self.allKV = [] def get_all_kv(self): keys = self.redis_object.keys() self.allKV = [key for key in keys if self.redis_object.ttl(key) is not None] # for key in keys: #
self.allKV[key] = ‘online‘ if (time.time() - int(self.redis_object.get(key))) < 10 else ‘off_line‘ class SchedTask: allkv = [] @classmethod def getHostOnlineStatus(cls): global allkv_json ru = RedisUtils(6379, 1) ru.get_all_kv() cls.allkv = ru.allKV allkv_json = json.dumps(cls.allkv) print allkv_json @classmethod def getHostOnlineStatusTask(cls): threading.Thread(target=cls.getHostOnlineStatus).start() @classmethod def run(cls, timedelay): while True: s = sched.scheduler(time.time, time.sleep) s.enter(timedelay, 1, cls.getHostOnlineStatusTask, ()) s.run() class WebsocketUtils(threading.Thread): MAGIC_STRING = 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 HANDSHAKE_STRING = "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade:WebSocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Accept: {1}\r\n" "WebSocket-Location: ws://{2}/chat\r\n" "WebSocket-Protocol:chat\r\n\r\n" def __init__(self, host, port): if not isinstance(host, str): raise KeyError("The host must be a string like \‘127.0.0.1\‘") else: self.host = host if not isinstance(port, int): raise KeyError(The port must be a integer) else: self.port = port try: self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind((host, port)) print ========================================================================================= print host,port self.sock.listen(100) except: print traceback.format_exc() print start socket error super(WebsocketUtils, self).__init__() # 前端握手 def handshake(self, conn): headers = {} shake = conn.recv(1024) print shake if not len(shake): print(len error) return False header, data = shake.split(\r\n\r\n, 1) for line in header.split(\r\n)[1:]: key, value = line.split(: , 1) headers[key] = value if Sec-WebSocket-Key not in headers: print(this is not websocket, client close.) print headers conn.close() return False sec_key = headers[Sec-WebSocket-Key] res_key = base64.b64encode(hashlib.sha1(sec_key + WebsocketUtils.MAGIC_STRING).digest()) str_handshke = WebsocketUtils.HANDSHAKE_STRING.replace({1}, res_key).replace({2}, self.host + ":" + str(self.port)) print str_handshke conn.send(str_handshke) i=0 def send_msg(self): WebsocketUtils.i+=1 global conn_list print send msg +str(WebsocketUtils.i) for conn in conn_list: try: conn.send(%c%c%s % (0x81, len(allkv_json), allkv_json)) except: print send msg error conn.close() conn_list.remove(conn) # 定時給前端發信息 def sched_send_msg(self): while True: s=sched.scheduler(time.time,time.sleep) s.enter(2,1,self.send_msg,()) s.run() def run(self): # 另開一個線程給各個conn發消息 global conn_list t=threading.Thread(target=self.sched_send_msg) t.start() while True: print wait link try: print self.sock self.conn, addr = self.sock.accept() print link ok self.handshake(self.conn) print handshake ok conn_list.append(self.conn) except: print traceback.format_exc() print error time.sleep(3) if __name__ == __main__: # websocket線程 websocket_utils = WebsocketUtils(0.0.0.0, 9000) #websocket_utils.setDaemon(True) # 把當前進程設置為守護進程,主線程執行完畢,子線程均停止 websocket_utils.start() # 定時獲取主機在線信息 SchedTask.run(5)

上js代碼

<html> <head>
<script type="text/javascript">
var socket = new WebSocket(‘ws://192.168.81:9000‘);
console.log(‘socket : ‘ + socket);

socket.onopen = function(e)
{
    console.log(‘onopen : ‘ + e);
        var element = document.getElementById("holder"); 
        element.innerHTML +=  ‘onopen : ‘
        element.innerHTML +=  e;
        element.innerHTML +=  "<br>";
}

socket.onclose = function(e)
{
    console.log(‘onclose : ‘ + e + ‘; length : ‘ + arguments.length);
        var element = document.getElementById("holder"); 
        element.innerHTML +=  ‘close : ‘
        element.innerHTML +=  e;
        element.innerHTML +=  "<br>";
}

socket.onmessage = function(e)
{
    console.log(‘onmessage : ‘ + e + ‘; length : ‘ + arguments.length);
    console.log(‘data : ‘ + e.data);
    socket.send(‘chengang‘);
        var element = document.getElementById("holder"); 
        element.innerHTML +=  ‘onmessage : ‘
        element.innerHTML +=  e
        element.innerHTML +=  "<br>";
        element.innerHTML +=  ‘data : ‘
        element.innerHTML +=  e.data;
        element.innerHTML +=  "<br>";
}
</script>
</head> <body>
<div id="holder" style="width:600px; height:300px"></div>

 </body> </html>

因為是在docket容器中運行的服務端,所以需要在端口映射9000:9000 客戶端js代碼的ip必須是服務端宿主機的ip

可能用的命令,可能會涉及到防火墻的關閉和查看端口是否開放可以這樣做

查看centos端口是否開放 在windos中 telnet 192.168.82.2 80 可以知道192.168.82.2這個主機的80端口是否開放,需要打開windows中telnet 在所有程序中打開,自行百度

查看centos端口占用 netstat -nap |grep ***

python 實現websocket