您的位置:首页 > 编程语言 > Python开发

WebSocket+多线程python socket网页版实时在线聊天实现

2017-10-08 10:25 525 查看
上一篇博文简单介绍了websocket通信协议,本文将应用websocket以及Python多线程网络编程实现一个简单的网页版实时在线聊天小系统,先来张图轻松一下



本应用开发环境

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,有兴趣的同学可以一起来玩
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: