基于python实现聊天室程序

作者:woaidouya123 时间:2022-09-26 07:50:33 

本文实例为大家分享了python实现简单聊天室的具体代码,供大家参考,具体内容如下

刚刚接触python编程,又从接触java开始一直对socket模块感兴趣,所以就做了一个聊天室的小程序。

该程序由客户端与服务器构成,使用UDP服务,服务器端绑定本地IP和端口,客户端由系统随机选择端口。

实现了群发、私发、点对点文件互传功能。

客户端自建了一个类继承了Cmd模块,使用自定义的命令command进行操作,调用相应的do_command方法。

使用json模块进行消息的封装序列化,在接收方进行解析。

客户端代码如下:


import socket
import threading
import json
import os
from cmd import Cmd

class Client(Cmd):
"""
客户端
"""
prompt = '>>>'
intro = '[Welcome] 简易聊天室客户端(Cli版)\n' + '[Welcome] 输入help来获取帮助\n'
buffersize = 1024

def __init__(self, host):
 """
 构造
 """
 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
 # 是否在发送文件
 self.sendfile = False
 self.filesize = None
 self.sendfilesize = None

# 接收文件包计数
 self.filecount = None
 # 接收文件名
 self.filename = None
 # 发送文件名
 self.sendfilename = None

# 发送者
 self.filefrom = None
 # 接收者
 self.fileto = None

# 接收文件流
 self.file_recv = None
 # 发送文件流
 self.file_send = None

# 接收文件地址
 self.filefrom_addr = None
 # 发送文件地址
 self.fileto_addr = None

def __receive_message_thread(self):
 """
 接受消息线程
 """
 while self.threadisalive:
  # noinspection PyBroadException
  try:
   buffer, addr = self.__socket.recvfrom(1024)
   '''
   文件流由发送端直接发送,不经过服务器,故当发送端发来的消息时,将收到的数据存入文件
   '''
   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())

# 若接收的数据为消息信息,则显示
   if js['type'] == 'message':
    print(js['message'])

# 若接收的数据为文件发送请求,则存储文件信息,并显示
   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? ')

# 接受的数据为请求回复,若同意接收则存储服务器发来的接收方的地址,并开启发送线程
   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] 无法从服务器获取数据')

def __send_broadcast_message_thread(self, message):
 """
 发送广播消息线程
 :param message: 消息内容
 """
 self.__socket.sendto(json.dumps({
  'type': 'broadcast',
  'nickname': self.__nickname,
  'message': message,
 }).encode(), self.__host)

def __send_file_thread(self):
 """
 发送文件线程
 :param message: 消息内容
 """
 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):
 """
 发送私发消息线程
 :param message: 消息内容
 """
 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):
 """
 启动客户端
 """
 self.cmdloop()

def do_login(self, args):
 """
 登录聊天室
 :param args: 参数
 """
 nickname = args.split(' ')[0]

# 将昵称发送给服务器,获取用户id
 self.__socket.sendto(json.dumps({
  'type': 'login',
  'nickname': nickname,
 }).encode(), self.__host)
 # 尝试接受数据

buffer = self.__socket.recvfrom(1300)[0].decode()
 obj = json.loads(buffer)
 if obj['login'] == 'success':
  self.__nickname = nickname
  print('[Client] 成功登录到聊天室')
  self.threadisalive = True
  # 开启子线程用于接受数据
  self.thread_recv = threading.Thread(
   target=self.__receive_message_thread)
  self.thread_recv.setDaemon(True)
  self.thread_recv.start()
 else:
  print('[Client] 无法登录到聊天室', obj['errormessage'])

def do_send(self, args):
 """
 发送消息
 :param args: 参数
 """
 if self.__nickname is None:
  print('请先登录!login nickname')
  return
 message = args
 # 开启子线程用于发送数据
 thread = threading.Thread(
  target=self.__send_broadcast_message_thread, args=(message, ))
 thread.setDaemon(True)
 thread.start()

def do_sendto(self, args):
 """
 发送私发消息
 :param args: 参数
 """
 if self.__nickname is None:
  print('请先登录!login nickname')
  return
 who = args.split(' ')[0]
 message = args.split(' ')[1]
 # # 显示自己发送的消息
 # print('[' + str(self.__nickname) + '(' + str(self.__id) + ')' + ']', message)
 # 开启子线程用于发送数据
 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('请先登录!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('请先登录!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: 参数
 """
 command = arg.split(' ')[0]
 if command == '':
  print('[Help] login nickname - 登录到聊天室,nickname是你选择的昵称')
  print('[Help] send message - 发送消息,message是你输入的消息')
  print('[Help] sendto who message - 私发消息,who是用户名,message是你输入的消息')
  print('[Help] catusers - 查看所有用户')
  print('[Help] catip who - 查看用户IP,who为用户名')
  print('[Help] sendfile who filedir - 向某用户发送文件,who为用户名,filedir为文件路径')
  print('[Help] getfile filename who yes/no - 接收文件,filename 为文件名,who为发送者,yes/no为是否接收')
 elif command == 'login':
  print('[Help] login nickname - 登录到聊天室,nickname是你选择的昵称')
 elif command == 'send':
  print('[Help] send message - 发送消息,message是你输入的消息')
 elif command == 'sendto':
  print('[Help] sendto who message - 发送私发消息,message是你输入的消息')
 else:
  print('[Help] 没有查询到你想要了解的指令')

def do_exit(self, arg): # 以do_*开头为命令
 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]
 # 判断是否在发送文件
 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()

服务器端主要进行消息的分类转发处理,用户列表、地址列表的维护。

服务器端代码如下:


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:
   # 向其他用户发送通知
   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)

# 群发消息
 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])

# 私发消息
 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)
  # 向其他用户发送通知
  for i in range(len(conn_list)):
   obj.sendto(json.dumps(
    {
     'type': 'message',
     'message': '[system]' + nickname + '下线了.'
    }).encode(), conn_list[i])

# 发送文件请求
 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)

# 发送文件请求回复
 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)

来源:https://blog.csdn.net/woaidouya123/article/details/79747274

标签:python,聊天室
0
投稿

猜你喜欢

  • MySQL安全性指南 (1)(转)

    2010-07-27 12:49:00
  • Python机器学习logistic回归代码解析

    2021-02-20 23:59:16
  • Pytorch基本变量类型FloatTensor与Variable用法

    2022-10-10 21:14:45
  • python指定路径斜杠与反斜杠遇到的问题

    2023-03-18 20:47:27
  • python可以美化表格数据输出结果的两个工具

    2022-06-05 02:33:32
  • 微信小程序webview组件交互,内联h5页面并网页实现微信支付实现解析

    2023-07-02 05:30:09
  • 详解Python的条件语句

    2021-03-04 08:27:56
  • 一文了解MySQL二级索引的查询过程

    2024-01-25 23:24:54
  • MySQL分库分表的几种方式

    2024-01-22 05:25:36
  • JS鼠标事件大全 推荐收藏

    2024-05-28 15:41:00
  • mysql中如何判断是否支持分区

    2024-01-19 18:00:57
  • 你是真正的用户体验设计者吗? Ⅱ

    2008-03-21 12:30:00
  • JavaScript实现模仿桌面窗口的方法

    2024-04-19 10:16:56
  • 详解Python中的字符串格式化

    2023-09-10 22:38:14
  • Python稀疏矩阵scipy.sparse包使用详解

    2022-07-31 04:42:42
  • MySQL 5数据库连接超时问题

    2009-12-29 10:23:00
  • IA学习笔记02:组织体系

    2009-06-12 12:22:00
  • 在页面中输出当前客户端时间javascript实例代码

    2024-02-25 23:07:56
  • MySQL选错索引的原因以及解决方案

    2024-01-19 21:34:34
  • go日志库logrus的安装及快速使用

    2024-04-26 17:31:40
  • asp之家 网络编程 m.aspxhome.com