基于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程序设计入门(1)基本语法简介
Jun 13 Python
python爬虫常用的模块分析
Aug 29 Python
对于Python的Django框架使用的一些实用建议
Apr 03 Python
python列表的增删改查实例代码
Jan 30 Python
Python获取指定文件夹下的文件名的方法
Feb 06 Python
符合语言习惯的 Python 优雅编程技巧【推荐】
Sep 25 Python
python实现简单多人聊天室
Dec 11 Python
Python实现 PS 图像调整中的亮度调整
Jun 28 Python
利用pandas合并多个excel的方法示例
Oct 10 Python
Python while循环使用else语句代码实例
Feb 07 Python
Django接收照片储存文件的实例代码
Mar 07 Python
python实现批量提取指定文件夹下同类型文件
Apr 05 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使用CURL实现对带有验证码的网站进行模拟登录的方法
2014/07/23 PHP
ThinkPHP中URL路径访问与模块控制器之间的关系
2014/08/23 PHP
php保存任意网络图片到服务器的方法
2015/04/14 PHP
thinkphp整合微信支付代码分享
2016/11/24 PHP
php输出反斜杠的实例方法
2019/09/19 PHP
Javascript模板技术
2007/04/27 Javascript
JS高级拖动技术 setCapture,releaseCapture
2011/07/31 Javascript
判断window.onload是否多次使用的方法
2014/09/21 Javascript
JavaScript中document.forms[0]与getElementByName区别
2015/01/21 Javascript
jquery实现右键菜单插件
2015/03/29 Javascript
javascript中递归函数用法注意点
2015/07/30 Javascript
Jquery实现select multiple左右添加和删除功能的简单实例
2016/05/26 Javascript
BootStrap智能表单实战系列(十一)级联下拉的支持
2016/06/13 Javascript
小程序点赞收藏功能的实现代码示例
2018/09/07 Javascript
elementUI中Table表格问题的解决方法
2018/12/04 Javascript
JavaScript中filter的用法实例分析
2019/02/27 Javascript
vue计算属性computed的使用方法示例
2019/03/13 Javascript
快速搭建Node.js(Express)用户注册、登录以及授权的方法
2019/05/09 Javascript
Python 遍历子文件和所有子文件夹的代码实例
2016/12/21 Python
python机器学习之随机森林(七)
2018/03/26 Python
详解python中groupby函数通俗易懂
2020/05/14 Python
基于Python词云分析政府工作报告关键词
2020/06/02 Python
CSS3动画之流彩文字效果+图片模糊效果+边框伸展效果实现代码合集
2017/08/18 HTML / CSS
实例教程 HTML5 Canvas 超炫酷烟花绽放动画实现代码
2014/11/05 HTML / CSS
Hotter Shoes英国官网:英伦风格,舒适的鞋子
2017/12/28 全球购物
京东奢侈品:全球奢侈品牌
2018/03/17 全球购物
美国儿童服装、家具和玩具精品店:Maisonette
2019/11/24 全球购物
动物学专业毕业生求职信
2013/10/11 职场文书
社会实践自我鉴定
2013/11/07 职场文书
编辑求职信样本
2013/12/16 职场文书
3的组成教学反思
2014/04/30 职场文书
预备党员自我批评思想汇报
2014/10/10 职场文书
2015自愿离婚协议书范本
2015/01/28 职场文书
工作能力自我评价2015
2015/03/05 职场文书
工作计划范文之财务管理
2019/08/09 职场文书
Nginx源码编译安装过程记录
2021/11/17 Servers