WebSocket+多线程python socket网页版实时在线聊天实现
2017-10-08 10:25
525 查看
上一篇博文简单介绍了websocket通信协议,本文将应用websocket以及Python多线程网络编程实现一个简单的网页版实时在线聊天小系统,先来张图轻松一下
本应用开发环境
测试运行环境
开发语言涉及
应用核心实现
功能实现
服务端实现
客户端核心实现(具体代码请移步GitHub)
登录成功页面(模板参考微信)
退出
搜索含有关键字的用户或者群组
添加群组
群聊
点对点聊天
发送方
接收方不在聊天页面
接收方进入聊天页面并回复消息
基本展示就到这,所有代码已经放到GitHub,有兴趣的同学可以一起来玩
本应用开发环境
windows7 x86_64 JetBrains PyCharm Community Edition 2017.2.2 x64 python3.5 Google Chrome浏览器
测试运行环境
server端运行在windows PyCharm中 client端运行在rhel7.2 Apache服务中
开发语言涉及
Python、JavaScript(以及jQuery库)、HTML、CSS
应用核心实现
Python套接字、Python多线程、websocket通信协议应用、前端三剑客
功能实现
用户自注册、登录、搜索并添加好友、加入群聊、创建群组、点对点聊天、群组聊天、退出等
服务端实现
# coding=utf-8 ''' file:websockt-server.py date:2017/10/2 20:48 author:lockey email:lockey@123.com desc: ''' import socket,json import hashlib import threading from base64 import b64encode #用户信息存储字典 users={} #群组信息存储字典 groups={} #判断是否设置了掩码 def is_bit_set(int_type, offset): mask = 1 << offset return not 0 == (int_type & mask) #设置掩码 def set_bit(int_type, offset): return int_type | (1 << offset) def bytes_to_int(data): # note big-endian is the standard network byte order return int.from_bytes(data, byteorder='big') #发送数据的封装,不完整实现 def pack(data): """pack bytes for sending to client""" frame_head = bytearray(2) # set final fragment frame_head[0] = set_bit(frame_head[0], 7) # set opcode 1 = text frame_head[0] = set_bit(frame_head[0], 0) # payload length assert len(data) < 126, "haven't implemented that yet" frame_head[1] = len(data) # add data frame = frame_head + data.encode('utf-8') return frame #接收数据的转换 def parse_recv_data(msg): en_bytes = b'' cn_bytes = [] if len(msg) < 6: return '' v = msg[1] & 0x7f if v == 0x7e: p = 4 elif v == 0x7f: p = 10 else: p = 2 mask = msg[p:p + 4] data = msg[p + 4:] for k, v in enumerate(data): nv = chr(v ^ mask[k % 4]) nv_bytes = nv.encode() nv_len = len(nv_bytes) if nv_len == 1: en_bytes += nv_bytes else: en_bytes += b'%s' cn_bytes.append(ord(nv_bytes.decode())) if len(cn_bytes) > 2: # 字节数组转汉字 cn_str = '' clen = len(cn_bytes) count = int(clen / 3) for x in range(0, count): i = x * 3 b = bytes([cn_bytes[i], cn_bytes[i + 1], cn_bytes[i + 2]]) cn_str += b.decode() new = en_bytes.replace(b'%s%s%s', b'%s') new = new.decode() res = (new % tuple(list(cn_str))) else: res = en_bytes.decode() return res #发送数据 def send_data(dataObj): data = json.dumps(dataObj)#转换数据为对象判断数据发送类型(群发或者点对点) if data == None or len(data) <= 0: return False if dataObj['type'] == 'personal': userto = dataObj['to'] userfrom = dataObj['from'] try: users[userto]['conn'].send(pack(data)) except: users[userto]['toRead'].append(data) print(users[userto]['toRead']) ret = {'type': 'server', 'status': 0} data = json.dumps(ret) users[userfrom]['conn'].send(pack(data)) if dataObj['type'] == 'group': groupto = dataObj['to'] grps = groups[groupto] for user in grps['groupmems']: try: users[user]['conn'].send(pack(data)) except: print('group Members error') class WebSocket(threading.Thread): # 继承Thread GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" def __init__(self, conn, name, remote, path="/"): threading.Thread.__init__(self) # 初始化父类Thread self.conn = conn self.name = name self.remote = remote self.path = path self.buffer = "" def run(self): headers = {} self.handshaken = False while True: if self.handshaken == False: print('Start Handshaken with {}!'.format(self.remote)) self.buffer += bytes.decode(self.conn.recv(1024)) if self.buffer.find('\r\n\r\n') != -1: header, data = self.buffer.split('\r\n\r\n', 1) for line in header.split("\r\n")[1:]: key, value = line.split(": ", 1) headers[key] = value headers["Location"] = ("ws://%s%s" % (headers["Host"], self.path)) key = headers['Sec-WebSocket-Key'] token = b64encode(hashlib.sha1(str.encode(str(key + self.GUID))).digest()) handshake = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " + bytes.decode( token) + "\r\nWebSocket-Origin: " + str(headers["Origin"]) + "\r\nWebSocket-Location: " + str( headers["Location"]) + "\r\n\r\n" self.conn.send(str.encode(str(handshake)))#发送握手信息 self.handshaken = True print('Handshaken with {} success!'.format(self.remote)) else: all_data = self.conn.recv(128) data=parse_recv_data(all_data) if not len(all_data): return False else: try: #以下为各种请求的处理(登录、注册、创建群组、发送消息等) print(data) #nowTime = time.strftime('%H:%M:%S', time.localtime(time.time())) dataObj = json.loads(data) if dataObj['type'] == 'quit': quituser = dataObj['username'] print('User %s Logout!' % (quituser)) del users[quituser]['conn'] users[quituser]['status'] = 0 self.conn.close() return False if (dataObj['type'] == 'login'): regUser = dataObj['username'] retStatus = 0 try: if users[regUser] and users[regUser]['password'] == dataObj['password']: if users[regUser]['status'] == 1: retStatus = 2 else: users[regUser]['status'] = 1 #toRead = users[regUser]['toRead'] #dataObj = {"type":"login","status":0,"toRead":'~'.join(toRead)} users[regUser]['toRead'] = [] users[regUser]['conn'] = self.conn else: retStatus = 1 except: retStatus = 1 finally: dataObj = {"type": "login", "status": retStatus} data = json.dumps(dataObj) self.conn.send(pack(data)) continue if (dataObj['type'] == 'reg'): regUser = dataObj['username'] if regUser in users: dataObj = {"type": "reg", "status": 1} else: users[regUser] = {'password':dataObj['password'],'conn':self.conn,'friends':[],'groups':[],'toRead':[],'status':0} dataObj = {"type":"reg","status":0} users[regUser]['status'] = 1 data = json.dumps(dataObj) self.conn.send(pack(data)) continue if (dataObj['type'] == 'addFriend'): username = dataObj['username'] friendname = dataObj['target'] if friendname not in users[username]['friends']: users[username]['friends'].append(friendname); users[friendname]['friends'].append(username); dataObj = {"type": "addFriend", "status": 0} else: dataObj = {"type":"reg","status":1} data = json.dumps(dataObj) self.conn.send(pack(data)) continue if (dataObj['type'] == 'search'): keywords = dataObj['keywords'] gps=[];persons=[] for g in groups: if keywords in g: gps.append(g) for p in users: if keywords in p: persons.append(p) dataObj={"type":"search",'groups':gps,'persons':persons} data = json.dumps(dataObj) self.conn.send(pack(data)) continue if (dataObj['type'] == 'enterGroup'): gname = dataObj['target'] uname = dataObj['username'] if uname not in groups[gname]['groupmems']: groups[gname]['groupmems'].append(uname) users[uname]['groups'].append(gname) dataObj = {"type": "enterGroup", "status": 0} else: dataObj = {"type": "enterGroup", "status": 1} data = json.dumps(dataObj) self.conn.send(pack(data)) continue if (dataObj['type'] == 'getGroups'): uname = dataObj['username'] if uname in users: dataObj = {"type": "getGroups", "status": 0,'list':users[uname]['groups']} else: dataObj = {"type":"getGroups","status":1} data = json.dumps(dataObj) self.conn.send(pack(data)) continue if (dataObj['type'] == 'getFriends'): uname = dataObj['username'] if uname in users: dataObj = {"type": "getFriends", "status": 0,'list':users[uname]['friends']} else: dataObj = {"type":"getFriends","status":1} data = json.dumps(dataObj) self.conn.send(pack(data)) continue if (dataObj['type'] == 'addgroup'): owner = dataObj['username'] groupname = dataObj['groupName'] groupmotto = dataObj['groupMotto'] groupmems = dataObj['groupMems'] if groupname in groups: dataObj = {"type": "addgroup", "status": 1} else: members=groupmems.split(',') groups[groupname] = {'owner':owner,'groupname':groupname,'groupmotto':groupmotto,'groupmems':members} dataObj = {"type":"addgroup","status":0} for user in members: users[user]['groups'].append(groupname) data = json.dumps(dataObj) self.conn.send(pack(data)) continue send_data(dataObj) except: print('Something wrong!') class WebSocketServer(object): def __init__(self): self.socket = None def begin(self): print('WebSocketServer Start!') self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(("192.168.0.33", 8899)) self.socket.listen(50) #创建套接字,并监听 while True: connection, address = self.socket.accept() username = "user-" + str(address[1]) newSocket = WebSocket(connection, username, address) newSocket.start() # 开始线程,执行run函数 if __name__ == "__main__": server = WebSocketServer() server.begin()
客户端核心实现(具体代码请移步GitHub)
var socket; function connect() { var host = "ws://192.168.0.33:8899"; socket = new WebSocket(host); try { socket.onopen = function (msg) { $('#tipsDiv').html('<h4 class="ok">服务器连接成功!</h4>').hide(5000) }; socket.onmessage = function (msg) {}; socket.onclose = function (msg) { $('.loginError').html('服务器中断!').show(); }; } catch (ex) { log(ex); } } function send() { var msg = $('#editArea').text(); msg = {'type':chatType,'from':username,'to':$('#chatArea .title_name').text(),'msg':msg,'time':times}; msg = JSON.stringify(msg) socket.send(msg); }
以下为部分功能展示:
注册登录成功页面(模板参考微信)
退出
搜索含有关键字的用户或者群组
提交搜索后系统会返回包含关键字的用户或者群组,并且进行标识,当用户把鼠标移动到搜索结果列表之上对于不是好友或者还未进入的群组会出现加号按钮,这时可以添加好友或者进去群组
添加群组
群聊
群里一个人发消息,其他人如果在群聊页面的话直接可以接受消息,如果不在群聊页面也会有消息提醒
点对点聊天
发送方
接收方不在聊天页面
接收方进入聊天页面并回复消息
基本展示就到这,所有代码已经放到GitHub,有兴趣的同学可以一起来玩
相关文章推荐
- Python+React+Websocket+Redis实现的实时多人聊天
- Python进阶开发之网络编程,socket实现在线聊天机器人
- 网页实时聊天之PHP实现websocket
- 网页实时聊天之PHP实现websocket
- 入门WebSocket和socket.io,实现在线实时聊天室
- javascript和jQuery实现网页实时聊天的ajax长轮询
- 在线实时聊天的实现
- laravel5.1 广播事件broadcasting event 实现web在线实时聊天(1) 安装配置
- web socket+node.js+mysql实现网页在线对战棋牌游戏(超级适合情侣玩)
- socket.io 实现网页聊天
- python socket 简单实现聊天功能
- 网页实时聊天之PHP实现websocket
- 网页实时聊天之PHP实现websocket
- dwr实现javaweb实时聊天通讯网页版常见问题及解决方案
- Python+Socket实现基于TCP协议的客户与服务端中文自动回复聊天功能示例
- 网页版在线聊天java Socket实现
- 最近做了一个类似社交的网站,看到新浪微博右侧有一个网页在线聊天的功能,如何才能实现网页在线聊天功能(新浪微博这种聊天)?
- javascript和jQuery实现网页实时聊天的ajax长轮询
- SignalR实现网页实时聊天功能
- 页面里的在线实时聊天实现