WebSocket+多執行緒python socket網頁版實時線上聊天實現
上一篇博文簡單介紹了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:[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('<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,有興趣的同學可以一起來玩
相關推薦
WebSocket+多執行緒python socket網頁版實時線上聊天實現
上一篇博文簡單介紹了websocket通訊協議,本文將應用websocket以及Python多執行緒網路程式設計實現一個簡單的網頁版實時線上聊天小系統,先來張圖輕鬆一下 本應用開發環境 windows7 x86_64 JetBrains PyCharm
python多執行緒爬取網頁
#-*- encoding:utf8 -*- ''' Created on 2018年12月25日 @author: Administrator ''' from multiprocessing.dummy import Pool as pl import csv import requests fr
python多執行緒抓取網頁內容並寫入MYSQL
自己的第一個多執行緒練習,中間踩了不少坑,程式寫的很渣,但是勉強能實現功能需求了 ,實際上抓取網頁是多執行緒在MYSQL寫入的時候是加了執行緒鎖的 ,實際上感覺就不是在多執行緒寫入了,不過作為第一個練習程式就這樣吧 ,後續部落格還會繼續更新優化版本。## htm
Jsoup簡單例子2.0——多執行緒爬取網頁內的郵箱
上一篇文章講了利用Jsoup爬取貼吧帖子裡的郵箱,雖然爬取成功了,但我對效率有所追求。10頁的帖子爬取了兩百多個郵箱,最快用時8秒,一般需要9秒。在思考了一下怎麼提升效率後,決定採用多執行緒的方式爬取網頁內的郵箱。廢話不多說,直接上程式碼。 引入Jsoup的jar包此處省略,沒有的可以檢視上篇文
python中多執行緒的共享資料,通過queue來實現,內有生產者消費者經典模型的示例程式碼
queue:佇列,即先進先出,它有以下幾個方法: 1.判斷佇列的大小:size() 2.向佇列中新增:put() 3.向佇列中取出:get() 4.如果佇列規定了長度,用來判斷是否滿了:full() import threading,time import queu
Java網路程式設計--多執行緒的Socket
首先,學好計算機網路知識真的很重要。雖然,學不好不會影響理解下面這個關於巨集觀講解,但是,學好了可以自己打漁吃,學不好就只能知道眼前有魚吃卻打不到漁。 在Java中網路程式有2種協議:TCP和UDP。 TCP 是可靠的連線。這個可靠的意思就是得有
python爬蟲入門八:多程序/多執行緒 python佇列Queue Python多執行緒(2)——執行緒同步機制 python學習筆記——多程序中共享記憶體Value & Array python 之 多程序 Python多程序 Python 使用multiprocessing 特別耗記
什麼是多執行緒/多程序 引用蟲師的解釋: 計算機程式只不過是磁碟中可執行的,二進位制(或其它型別)的資料。它們只有在被讀取到記憶體中,被作業系統呼叫的時候才開始它們的生命期。 程序(有時被稱為重量級程序)是程式的一次執行。每個程序都有自己的地址空間,記憶體,資料棧以及其它記錄其執行軌跡的輔助資料
《Unity 3D遊戲客戶端基礎框架》多執行緒非同步 Socket 框架構建
引言: 之前寫過一個 demo 案例大致講解了 Socket 通訊的過程,並和自建的伺服器完成連線和簡單的資料通訊,詳細的內容可以檢視 Unity3D —— Socket通訊(C#)。但是在實際專案應用的過程中,這個 demo 的實現方式顯得異常簡陋,而且對應
C# 實現的多執行緒非同步Socket資料包接收器框架
幾天前在博問中看到一個C# Socket問題,就想到筆者2004年做的一個省級交通流量接收伺服器專案,當時的基本求如下: 接收自動觀測裝置通過無線網絡卡、Internet和Socket上報的交通量資料包 全年365*24執行的自動觀測裝置5分鐘上報一次觀測資料,每筆記錄約
BOOST::ASIO多執行緒下socket關閉導致程序崩潰問題定位及解決
boost::threadpool::detail::worker_thread<boost::threadpool::detail::pool_core<boost::function0<void>, boost::threadpool::fifo_scheduler, boost:
網路爬蟲:使用多執行緒爬取網頁連結
前言: 經過前面兩篇文章,你想大家應該已經知道網路爬蟲是怎麼一回事了。這篇文章會在之前做過的事情上做一些改進,以及說明之前的做法的不足之處。 思路分析: 1.邏輯結構圖 上圖中展示的就是我們網路爬蟲中的整個邏輯思路(呼叫Python解析URL,這裡只作了簡略
多執行緒解決socket併發問題
概念 這篇部落格我們利用多執行緒解決伺服器併發問題 程序是資源分配最小的單位,執行緒是CPU排程的最小單位 多程序與多執行緒比較(下圖取自網路,原作者不詳) 對比維度 多程序 多執行緒 總結 資料共享、同步 資料共享複雜,
ctrl+c關閉多執行緒python程式
專案中經常需要用到多執行緒,如果一個python程式用了多執行緒,當子執行緒沒有結束時,用ctrl+c是關閉不了主執行緒的,這時候就只能用kill命令殺掉,這樣會很麻煩。 所以探討了下怎麼ctrl+C關閉多執行緒python程式,也在網上查了很多別人的做法,自己做了很多實驗,嘗試了很多種方法,總結得出一個能
多執行緒訪問socket, 是否需要加鎖?
參照文章: 編寫socket 多執行緒併發程式時, 遇到以下兩個問題: 同一個socket, 是否可以在兩個執行緒中同時send 和 recv? 同一個socket, 是否可以在多個執行緒中同時send? 答案: 都可以, 並無需加鎖控制. socket是全雙工的,
【Python筆記】Python多執行緒程序如何正確響應Ctrl-C以實現優雅退出
相信用C/C++寫過服務的同學對通過響應Ctrl-C(訊號量SIG_TERM)實現多執行緒C程序的優雅退出都不會陌生,典型的實現偽碼如下: #include <signal.h> int main(int argc, char * argv[])
CP2K4.1-release & 最新5.x-development 多程序多執行緒(MPI+OPENMP) (psmp版 & popt版)超詳細編譯安裝教程極致優化加速版(全編譯器可編譯版)
CP2K 4.1-release & 最新5.x-development 多程序多執行緒(MPI+OPENMP) (psmp版 & popt版)超詳細編譯安裝教程極致優化加速版(全
可擴充套件多執行緒非同步Socket伺服器框架EMTASS 2.0
0 前言 >>[前言]、[第1節]、[第2節]、[第3節]、[第4節]、[第5節]、[第6節] 在程式設計與實際應用中,Socket資料包接收伺服器夠得上一個經典問題了:需要計算機與網路程式設計知識(主要是Socket),與業務處理邏輯密切(如:包組成
goLang 多執行緒抓取網頁資料
突然有個想法想用goLang快速的抓取網頁資料,於是想到了 多執行緒進行頁面抓取 package main import ( "fmt" "log" "net/http" "os" "st
Liunx C 程式設計之多執行緒與Socket
多執行緒 pthread.h是linux特有的標頭檔案,POSIX執行緒(POSIX threads),簡稱Pthreads,是執行緒的POSIX標準。該標準定義了建立和操縱執行緒的一整套API。在類Unix作業系統(Unix、Linux、Mac OS X等)中,都使用Pthreads作為作業系統的執行緒。
32-多執行緒--概述+Thread類+多執行緒的建立方式(繼承Thread類+實現Runnable介面)+Runnable介面+執行緒的名稱+執行緒的狀態
一、概述 1、程序:對應的是一個應用程式在記憶體中的所屬空間。程序是不直接執行的,它只是在分配該應用程式的記憶體空間 注:如果一個程式在記憶體中開闢了空間,就代表它在執行。不執行要釋放空間 2、執行緒:程序中的一個負責程式執行的控制單元,也叫執行路徑。一個程序中可以有多個執行路徑,稱之為