基于python实现聊天室程序


Posted in Python onJuly 27, 2018

本文实例为大家分享了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)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python使用bs4获取58同城城市分类的方法
Jul 08 Python
python 通过字符串调用对象属性或方法的实例讲解
Apr 21 Python
10分钟教你用Python实现微信自动回复功能
Nov 28 Python
python批量从es取数据的方法(文档数超过10000)
Dec 27 Python
基于Python函数和变量名解析
Jul 19 Python
pytorch GAN伪造手写体mnist数据集方式
Jan 10 Python
浅析pip安装第三方库及pycharm中导入第三方库的问题
Mar 10 Python
Django 实现将图片转为Base64,然后使用json传输
Mar 27 Python
CentOS 7如何实现定时执行python脚本
Jun 24 Python
python中的测试框架
Nov 13 Python
Python基于argparse与ConfigParser库进行入参解析与ini parser
Feb 02 Python
python数字图像处理:图像简单滤波
Jun 28 Python
Python中return self的用法详解
Jul 27 #Python
TensorFlow打印tensor值的实现方法
Jul 27 #Python
解决tensorflow测试模型时NotFoundError错误的问题
Jul 27 #Python
Tensorflow 同时载入多个模型的实例讲解
Jul 27 #Python
Tensorflow加载预训练模型和保存模型的实例
Jul 27 #Python
Python解决走迷宫问题算法示例
Jul 27 #Python
python保存文件方法小结
Jul 27 #Python
You might like
php绝对路径与相对路径之间关系的的分析
2010/03/03 PHP
PHP5函数小全(分享)
2013/06/06 PHP
php使用simplexml_load_file加载XML文件并显示XML的方法
2015/03/19 PHP
PHP读取文件内容的五种方式
2015/12/28 PHP
javascript 写类方式之五
2009/07/05 Javascript
Javascript下判断是否为闰年的Datetime包
2010/10/26 Javascript
javascript删除数组元素并且数组长度减小的简单实例
2014/02/14 Javascript
jQuery密码强度检测插件passwordStrength用法实例分析
2015/10/30 Javascript
基于JavaScript实现树形下拉框
2016/08/10 Javascript
vue2.0使用swiper组件实现轮播效果
2017/11/27 Javascript
Vue结合Video.js播放m3u8视频流的方法示例
2018/05/04 Javascript
js获取图片的base64编码并压缩
2020/12/05 Javascript
[06:16]《DAC最前线》之地区预选赛全面回顾
2015/01/19 DOTA
[51:10]VP vs VGJ.S 2018国际邀请赛小组赛BO2 第二场 8.19
2018/08/21 DOTA
python实现linux下使用xcopy的方法
2015/06/28 Python
详解Python中 sys.argv[]的用法简明解释
2017/12/20 Python
Python使用贪婪算法解决问题
2019/10/22 Python
python命令 -u参数用法解析
2019/10/24 Python
使用Python的Turtle库绘制森林的实例
2019/12/18 Python
基于virtualenv创建python虚拟环境过程图解
2020/03/30 Python
python多线程semaphore实现线程数控制的示例
2020/08/10 Python
CSS3教程:边框属性border的极致应用
2009/04/02 HTML / CSS
phonegap常用事件总结(必看篇)
2017/03/31 HTML / CSS
老公给老婆的道歉信
2014/01/10 职场文书
幼儿园美术教学反思
2014/01/31 职场文书
物理力学求职信
2014/02/18 职场文书
环境工程专业自荐信
2014/03/03 职场文书
小学作文评语大全
2014/04/21 职场文书
机电一体化毕业生自荐信
2014/06/19 职场文书
传播学专业毕业生自荐书
2014/07/01 职场文书
市场营销专业毕业生求职信
2014/07/21 职场文书
个人查摆问题整改措施
2014/10/04 职场文书
四风问题党员个人整改措施
2014/10/27 职场文书
2015年党建工作汇报材料
2015/06/25 职场文书
详解缓存穿透击穿雪崩解决方案
2021/05/28 Redis
java固定大小队列的几种实现方式详解
2021/07/15 Java/Android