基于python實(shí)現(xiàn)聊天室程序
本文實(shí)例為大家分享了python實(shí)現(xiàn)簡(jiǎn)單聊天室的具體代碼,供大家參考,具體內(nèi)容如下
剛剛接觸python編程,又從接觸java開(kāi)始一直對(duì)socket模塊感興趣,所以就做了一個(gè)聊天室的小程序。
該程序由客戶端與服務(wù)器構(gòu)成,使用UDP服務(wù),服務(wù)器端綁定本地IP和端口,客戶端由系統(tǒng)隨機(jī)選擇端口。
實(shí)現(xiàn)了群發(fā)、私發(fā)、點(diǎn)對(duì)點(diǎn)文件互傳功能。
客戶端自建了一個(gè)類繼承了Cmd模塊,使用自定義的命令command進(jìn)行操作,調(diào)用相應(yīng)的do_command方法。
使用json模塊進(jìn)行消息的封裝序列化,在接收方進(jìn)行解析。
客戶端代碼如下:
import socket
import threading
import json
import os
from cmd import Cmd
class Client(Cmd):
"""
客戶端
"""
prompt = '>>>'
intro = '[Welcome] 簡(jiǎn)易聊天室客戶端(Cli版)\n' + '[Welcome] 輸入help來(lái)獲取幫助\n'
buffersize = 1024
def __init__(self, host):
"""
構(gòu)造
"""
super().__init__()
self.__socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# self.__id = None
self.__nickname = None
self.__host = host
self.thread_recv = None
self.threadisalive = False
# 是否在接收文件
self.recvfile = False
# 是否在發(fā)送文件
self.sendfile = False
self.filesize = None
self.sendfilesize = None
# 接收文件包計(jì)數(shù)
self.filecount = None
# 接收文件名
self.filename = None
# 發(fā)送文件名
self.sendfilename = None
# 發(fā)送者
self.filefrom = None
# 接收者
self.fileto = None
# 接收文件流
self.file_recv = None
# 發(fā)送文件流
self.file_send = None
# 接收文件地址
self.filefrom_addr = None
# 發(fā)送文件地址
self.fileto_addr = None
def __receive_message_thread(self):
"""
接受消息線程
"""
while self.threadisalive:
# noinspection PyBroadException
try:
buffer, addr = self.__socket.recvfrom(1024)
'''
文件流由發(fā)送端直接發(fā)送,不經(jīng)過(guò)服務(wù)器,故當(dāng)發(fā)送端發(fā)來(lái)的消息時(shí),將收到的數(shù)據(jù)存入文件
'''
if (addr != self.__host) & (addr == self.filefrom_addr) & self.recvfile:
self.file_recv.write(buffer)
self.filecount += 1
if self.filecount * 1024 >= self.filesize:
self.file_recv.close()
print(self.filename, 'is received.')
self.recvfile = False
continue
js = json.loads(buffer.decode())
# 若接收的數(shù)據(jù)為消息信息,則顯示
if js['type'] == 'message':
print(js['message'])
# 若接收的數(shù)據(jù)為文件發(fā)送請(qǐng)求,則存儲(chǔ)文件信息,并顯示
elif js['type'] == 'filequest':
if self.recvfile:
self.__socket.sendto(json.dumps({
'type': 'fileres',
'fileres': 'no',
'nickname': self.__nickname,
'who': js['nickname'],
'errormessage': 'is transfroming files.',
}).encode(), self.__host)
continue
filename = js['filename']
who = js['nickname']
filesize = js['filesize']
self.recvfile = True
self.filesize = filesize
self.filename = filename
self.filecount = 0
self.filefrom = who
self.filefrom_addr = (js['send_ip'], js['send_port'])
print('[system]:', who, ' send a file(',
filename, ') to you. receive? ')
# 接受的數(shù)據(jù)為請(qǐng)求回復(fù),若同意接收則存儲(chǔ)服務(wù)器發(fā)來(lái)的接收方的地址,并開(kāi)啟發(fā)送線程
elif js['type'] == 'fileres':
if js['fileres'] == 'yes':
print(js['recv_ip'], js['recv_port'])
self.fileto_addr = (js['recv_ip'], js['recv_port'])
thread = threading.Thread(
target=self.__send_file_thread)
thread.start()
else:
print(js['nickname'], js['errormessage'])
self.sendfile = False
except Exception as e:
print(e)
print('[Client] 無(wú)法從服務(wù)器獲取數(shù)據(jù)')
def __send_broadcast_message_thread(self, message):
"""
發(fā)送廣播消息線程
:param message: 消息內(nèi)容
"""
self.__socket.sendto(json.dumps({
'type': 'broadcast',
'nickname': self.__nickname,
'message': message,
}).encode(), self.__host)
def __send_file_thread(self):
"""
發(fā)送文件線程
:param message: 消息內(nèi)容
"""
filecount = 0
print('[system]', 'sending the file...')
while filecount * 1024 <= self.sendfilesize:
self.__socket.sendto(
self.file_send.read(1024), self.fileto_addr)
filecount += 1
self.file_send.close()
self.sendfile = False
print('[system]', 'the file is sended.')
def __send_whisper_message_thread(self, who, message):
"""
發(fā)送私發(fā)消息線程
:param message: 消息內(nèi)容
"""
self.__socket.sendto(json.dumps({
'type': 'sendto',
'who': who,
'nickname': self.__nickname,
'message': message
}).encode(), self.__host)
def send_exit(self):
self.__socket.sendto(json.dumps({
'type': 'offline',
'nickname': self.__nickname,
}).encode(), self.__host)
def start(self):
"""
啟動(dòng)客戶端
"""
self.cmdloop()
def do_login(self, args):
"""
登錄聊天室
:param args: 參數(shù)
"""
nickname = args.split(' ')[0]
# 將昵稱發(fā)送給服務(wù)器,獲取用戶id
self.__socket.sendto(json.dumps({
'type': 'login',
'nickname': nickname,
}).encode(), self.__host)
# 嘗試接受數(shù)據(jù)
buffer = self.__socket.recvfrom(1300)[0].decode()
obj = json.loads(buffer)
if obj['login'] == 'success':
self.__nickname = nickname
print('[Client] 成功登錄到聊天室')
self.threadisalive = True
# 開(kāi)啟子線程用于接受數(shù)據(jù)
self.thread_recv = threading.Thread(
target=self.__receive_message_thread)
self.thread_recv.setDaemon(True)
self.thread_recv.start()
else:
print('[Client] 無(wú)法登錄到聊天室', obj['errormessage'])
def do_send(self, args):
"""
發(fā)送消息
:param args: 參數(shù)
"""
if self.__nickname is None:
print('請(qǐng)先登錄!login nickname')
return
message = args
# 開(kāi)啟子線程用于發(fā)送數(shù)據(jù)
thread = threading.Thread(
target=self.__send_broadcast_message_thread, args=(message, ))
thread.setDaemon(True)
thread.start()
def do_sendto(self, args):
"""
發(fā)送私發(fā)消息
:param args: 參數(shù)
"""
if self.__nickname is None:
print('請(qǐng)先登錄!login nickname')
return
who = args.split(' ')[0]
message = args.split(' ')[1]
# # 顯示自己發(fā)送的消息
# print('[' + str(self.__nickname) + '(' + str(self.__id) + ')' + ']', message)
# 開(kāi)啟子線程用于發(fā)送數(shù)據(jù)
thread = threading.Thread(
target=self.__send_whisper_message_thread, args=(who, message))
thread.setDaemon(True)
thread.start()
def do_catusers(self, arg):
if self.__nickname is None:
print('請(qǐng)先登錄!login nickname')
return
catmessage = json.dumps({'type': 'catusers'})
self.__socket.sendto(catmessage.encode(), self.__host)
def do_catip(self, args):
if self.__nickname is None:
print('請(qǐng)先登錄!login nickname')
return
who = args
catipmessage = json.dumps({'type': 'catip', 'who': who})
self.__socket.sendto(catipmessage.encode(), self.__host)
def do_help(self, arg):
"""
幫助
:param arg: 參數(shù)
"""
command = arg.split(' ')[0]
if command == '':
print('[Help] login nickname - 登錄到聊天室,nickname是你選擇的昵稱')
print('[Help] send message - 發(fā)送消息,message是你輸入的消息')
print('[Help] sendto who message - 私發(fā)消息,who是用戶名,message是你輸入的消息')
print('[Help] catusers - 查看所有用戶')
print('[Help] catip who - 查看用戶IP,who為用戶名')
print('[Help] sendfile who filedir - 向某用戶發(fā)送文件,who為用戶名,filedir為文件路徑')
print('[Help] getfile filename who yes/no - 接收文件,filename 為文件名,who為發(fā)送者,yes/no為是否接收')
elif command == 'login':
print('[Help] login nickname - 登錄到聊天室,nickname是你選擇的昵稱')
elif command == 'send':
print('[Help] send message - 發(fā)送消息,message是你輸入的消息')
elif command == 'sendto':
print('[Help] sendto who message - 發(fā)送私發(fā)消息,message是你輸入的消息')
else:
print('[Help] 沒(méi)有查詢到你想要了解的指令')
def do_exit(self, arg): # 以do_*開(kāi)頭為命令
print("Exit")
self.send_exit()
try:
self.threadisalive = False
self.thread_recv.join()
except Exception as e:
print(e)
# self.__socket.close()
def do_sendfile(self, args):
who = args.split(' ')[0]
filepath = args.split(' ')[1]
filename = filepath.split('\\')[-1]
# 判斷是否在發(fā)送文件
if self.sendfile:
print('you are sending files, please try later.')
return
if not os.path.exists(filepath):
print('the file is not exist.')
return
filesize = os.path.getsize(filepath)
# print(who, filename, filesize)
self.sendfile = True
self.fileto = who
self.sendfilename = filename
self.sendfilesize = filesize
self.file_send = open(filepath, 'rb')
self.__socket.sendto(json.dumps({
'type': 'filequest',
'nickname': self.__nickname,
'filename': self.sendfilename,
'filesize': self.sendfilesize,
'who': self.fileto,
'send_ip': '',
'send_port': '',
}).encode(), self.__host)
print('request send...')
# fileres = self.__socket.recvfrom(1024)[0].decode()
# js = json.loads(fileres)
def do_getfile(self, args):
filename = args.split(' ')[0]
who = args.split(' ')[1]
ch = args.split(' ')[2]
# print(self.filename is not None, filename, self.filename, who, self.filefrom)
if (self.filename is not None) & (filename == self.filename) & (who == self.filefrom):
if ch == 'yes':
self.file_recv = open(self.filename, 'wb')
self.__socket.sendto(json.dumps({
'type': 'fileres',
'fileres': 'yes',
'nickname': self.__nickname,
'who': who,
'recv_ip': '',
'recv_port': '',
}).encode(), self.__host)
print('you agree to reveive the file(', filename, ') from', who)
else:
self.__socket.sendto(json.dumps({
'type': 'fileres',
'fileres': 'no',
'nickname': self.__nickname,
'errormessage': 'deny the file.',
'who': who,
'recv_ip': '',
'recv_port': '',
}).encode(), self.__host)
print('you deny to reveive the file(', filename, ') from', who)
self.recvfile = False
else:
print('the name or sender of the file is wrong.')
c = Client(('127.0.0.1', 12346))
c.start()
服務(wù)器端主要進(jìn)行消息的分類轉(zhuǎn)發(fā)處理,用戶列表、地址列表的維護(hù)。
服務(wù)器端代碼如下:
import socket
import json
obj = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
obj.bind(('127.0.0.1', 12346))
conn_list = []
user_list = []
while True:
try:
receive_data, client_address = obj.recvfrom(1024)
js = json.loads(receive_data.decode())
# 登錄消息
if js['type'] == 'login':
nickname = str(js['nickname'])
if nickname in user_list:
obj.sendto(json.dumps({'login': 'fail',
'errormessage': 'the nickname is exists'}).encode(),
client_address)
else:
# 向其他用戶發(fā)送通知
for i in range(len(conn_list)):
obj.sendto(json.dumps(
{
'type': 'message',
'message': '[system]' + nickname + '已登錄.'
}).encode(), conn_list[i])
user_list.append(nickname)
conn_list.append(client_address)
print(nickname, client_address, '登錄成功!')
obj.sendto(json.dumps({'login': 'success',
'nickname': nickname}).encode(), client_address)
# 群發(fā)消息
elif js['type'] == 'broadcast':
message = js['message']
nickname = js['nickname']
for i in range(len(conn_list)):
obj.sendto(json.dumps(
{
'type': 'message',
'message': nickname + ':' + message
}).encode(), conn_list[i])
# 私發(fā)消息
elif js['type'] == 'sendto':
who = js['who']
nickname = js['nickname']
message = js['message']
# 檢查用戶是否存在
if who not in user_list:
obj.sendto(json.dumps(
{
'type': 'message',
'message': who + ' not exist or not online.please try later.'
}).encode(),
client_address)
else:
obj.sendto(json.dumps(
{
'type': 'message',
'message': nickname + ' whisper to you: ' + message
}).encode(),
conn_list[user_list.index(who)])
# 查看用戶列表
elif js['type'] == 'catusers':
users = json.dumps(user_list)
obj.sendto(json.dumps(
{
'type': 'message',
'message': users,
}).encode(),
client_address)
# 查看用戶IP
elif js['type'] == 'catip':
who = js['who']
if who not in user_list:
obj.sendto(json.dumps(
{
'type': 'message',
'message': who + ' not exist or not online.please try later.'
}).encode(),
client_address)
else:
addr = json.dumps(conn_list[user_list.index(who)])
obj.sendto(json.dumps(
{
'type': 'message',
'message': addr,
}).encode(),
client_address)
# 離線消息
elif js['type'] == 'offline':
user_list.remove(js['nickname'])
conn_list.remove(client_address)
obj.sendto(
(js['nickname'] + 'offline.').encode(),
client_address)
# 向其他用戶發(fā)送通知
for i in range(len(conn_list)):
obj.sendto(json.dumps(
{
'type': 'message',
'message': '[system]' + nickname + '下線了.'
}).encode(), conn_list[i])
# 發(fā)送文件請(qǐng)求
elif js['type'] == 'filequest':
who = js['who']
if who not in user_list:
obj.sendto(json.dumps(
{
'type': 'message',
'message': who + ' not exist or not online.please try later.'
}).encode(),
client_address)
else:
js['send_ip'] = client_address[0]
js['send_port'] = client_address[1]
obj.sendto(json.dumps(js).encode(),
conn_list[user_list.index(who)])
print(js['nickname'], 'request to send file to', who)
# 發(fā)送文件請(qǐng)求回復(fù)
elif js['type'] == 'fileres':
who = js['who']
if js['fileres'] == 'yes':
js['recv_ip'] = client_address[0]
js['recv_port'] = client_address[1]
print(js['nickname'], 'agree to receive file from', js['who'])
else:
print(js['nickname'], 'deny to receive file from', js['who'])
obj.sendto(json.dumps(js).encode(),
conn_list[user_list.index(who)])
except Exception as e:
print(e)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- python實(shí)現(xiàn)多人聊天室
- python socket多線程通訊實(shí)例分析(聊天室)
- Python實(shí)現(xiàn)基于C/S架構(gòu)的聊天室功能詳解
- 小小聊天室Python代碼實(shí)現(xiàn)
- Python socket C/S結(jié)構(gòu)的聊天室應(yīng)用實(shí)現(xiàn)
- python編寫(xiě)簡(jiǎn)易聊天室實(shí)現(xiàn)局域網(wǎng)內(nèi)聊天功能
- python實(shí)現(xiàn)簡(jiǎn)單多人聊天室
- Python實(shí)現(xiàn)的使用telnet登陸聊天室實(shí)例
- Python socket實(shí)現(xiàn)簡(jiǎn)單聊天室
- python實(shí)現(xiàn)簡(jiǎn)易聊天室(Linux終端)
相關(guān)文章
機(jī)器學(xué)習(xí)數(shù)據(jù)預(yù)處理之獨(dú)熱One-Hot編碼及其代碼詳解
獨(dú)熱編碼即 One-Hot 編碼,又稱一位有效編碼。其方法是使用 N位 狀態(tài)寄存器來(lái)對(duì) N個(gè)狀態(tài) 進(jìn)行編碼,每個(gè)狀態(tài)都有它獨(dú)立的寄存器位,并且在任意時(shí)候,其中只有一位有效,這篇文章主要介紹了機(jī)器學(xué)習(xí)數(shù)據(jù)預(yù)處理之獨(dú)熱One-Hot編碼及其代碼詳解,需要的朋友可以參考下2022-07-07
基于python進(jìn)行桶排序與基數(shù)排序的總結(jié)
今天小編就為大家分享一篇基于python進(jìn)行桶排序與基數(shù)排序的總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
Python定時(shí)庫(kù)Apscheduler的簡(jiǎn)單使用
Apscheduler是基于Quartz的Python定時(shí)任務(wù)框架,功能上跟Quartz一致,使用上跟Quartz也幾乎一致。下面通過(guò)本文給大家介紹Python定時(shí)庫(kù)Apscheduler的簡(jiǎn)單使用,感興趣的朋友一起看看吧2021-11-11
Python通過(guò)kerberos安全認(rèn)證操作kafka方式
這篇文章主要介紹了Python通過(guò)kerberos安全認(rèn)證操作kafka方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-06-06
Python實(shí)現(xiàn)的序列化和反序列化二叉樹(shù)算法示例
這篇文章主要介紹了Python實(shí)現(xiàn)的序列化和反序列化二叉樹(shù)算法,結(jié)合實(shí)例形式分析了Python二叉樹(shù)的構(gòu)造、遍歷、序列化、反序列化等相關(guān)操作技巧,需要的朋友可以參考下2019-03-03
Flask框架學(xué)習(xí)筆記之模板操作實(shí)例詳解
這篇文章主要介紹了Flask框架學(xué)習(xí)筆記之模板操作,結(jié)合實(shí)例形式詳細(xì)分析了flask框架模板引擎Jinja2的模板調(diào)用、模板繼承相關(guān)原理與操作技巧,需要的朋友可以參考下2019-08-08
使用PyTorch訓(xùn)練一個(gè)圖像分類器實(shí)例
今天小編就為大家分享一篇使用PyTorch訓(xùn)練一個(gè)圖像分類器實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-01-01

