python 實現websocket
阿新 • • 發佈:2018-09-28
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