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

上一篇博文简单介绍了websocket通信协议,本文将应用websocket以及Python多线程网络编程实现一个简单的网页版实时在线聊天小系统,先来张图轻松一下
WebSocket+多线程python socket网页版实时在线聊天实现_第1张图片

本应用开发环境

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:[email protected]
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('

服务器连接成功!

'
).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); }

以下为部分功能展示:

注册
WebSocket+多线程python socket网页版实时在线聊天实现_第2张图片

登录成功页面(模板参考微信)
WebSocket+多线程python socket网页版实时在线聊天实现_第3张图片

退出
WebSocket+多线程python socket网页版实时在线聊天实现_第4张图片

搜索含有关键字的用户或者群组

提交搜索后系统会返回包含关键字的用户或者群组,并且进行标识,当用户把鼠标移动到搜索结果列表之上对于不是好友或者还未进入的群组会出现加号按钮,这时可以添加好友或者进去群组

WebSocket+多线程python socket网页版实时在线聊天实现_第5张图片

添加群组
WebSocket+多线程python socket网页版实时在线聊天实现_第6张图片

群聊

群里一个人发消息,其他人如果在群聊页面的话直接可以接受消息,如果不在群聊页面也会有消息提醒

WebSocket+多线程python socket网页版实时在线聊天实现_第7张图片
WebSocket+多线程python socket网页版实时在线聊天实现_第8张图片
WebSocket+多线程python socket网页版实时在线聊天实现_第9张图片

点对点聊天

发送方
WebSocket+多线程python socket网页版实时在线聊天实现_第10张图片
接收方不在聊天页面
WebSocket+多线程python socket网页版实时在线聊天实现_第11张图片

接收方进入聊天页面并回复消息
WebSocket+多线程python socket网页版实时在线聊天实现_第12张图片

基本展示就到这,所有代码已经放到GitHub,有兴趣的同学可以一起来玩

你可能感兴趣的:(python,jquery,javascript,html,前端)