python 实现websocket
Posted 持&恒
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python 实现websocket相关的知识,希望对你有一定的参考价值。
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 " "Upgrade:WebSocket " "Connection: Upgrade " "Sec-WebSocket-Accept: {1} " "WebSocket-Location: ws://{2}/chat " "WebSocket-Protocol:chat " 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(‘ ‘, 1) for line in header.split(‘ ‘)[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的主要内容,如果未能解决你的问题,请参考以下文章
从python代码连接到Flask websocket [重复]