中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python實現基于C/S架構的聊天室功能詳解

發布時間:2020-10-24 03:39:51 來源:腳本之家 閱讀:218 作者:juggd 欄目:開發技術

本文實例講述了Python實現基于C/S架構的聊天室功能。分享給大家供大家參考,具體如下:

一、課程介紹

1.簡介

本次項目課是實現簡單聊天室程序的服務器端和客戶端。

2.知識點

服務器端涉及到asyncoreasynchatsocket這幾個模塊,客戶端用到了telnetlibwxtimethread這幾個模塊。

3.所需環境

本次課中編寫客戶端需要用到wxPython,它是一個GUI工具包,請先使用下面的命令安裝:

$ sudo apt-get install python-wxtools

密碼為shiyanlou

4.項目效果截圖

登錄窗口

Python實現基于C/S架構的聊天室功能詳解

聊天窗口

Python實現基于C/S架構的聊天室功能詳解

5.源代碼下載

git clone https://github.com/shiyanlou/pythonchat.git

說明:如果你不理解上述代碼的下載方式或者下載后在環境中找不到代碼,可以點擊查看這里

二、項目實戰(服務器端)

1.服務器類

首先需要一個聊天服務器,這里繼承asyncore的dispatcher類來實現,代碼如下

class ChatServer(dispatcher):
  """
  聊天服務器
  """
  def __init__(self, port):
    dispatcher.__init__(self)
    self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
    self.set_reuse_addr()
    self.bind(('', port))
    self.listen(5)
    self.users = {}
    self.main_room = ChatRoom(self)
  def handle_accept(self):
    conn, addr = self.accept()
    ChatSession(self, conn)

2.會話類

有了服務器類還需要能維護每個用戶的連接會話,這里繼承asynchat的async_chat類來實現,代碼如下:

class ChatSession(async_chat):
  """
  負責和單用戶通信
  """
  def __init__(self, server, sock):
    async_chat.__init__(self, sock)
    self.server = server
    self.set_terminator('\n')
    self.data = []
    self.name = None
    self.enter(LoginRoom(server))
  def enter(self, room):
    '從當前房間移除自身,然后添加到指定房間'
    try:
      cur = self.room
    except AttributeError:
      pass
    else:
      cur.remove(self)
    self.room = room
    room.add(self)
  def collect_incoming_data(self, data):
    '接受客戶端的數據'
    self.data.append(data)
  def found_terminator(self):
    '當客戶端的一條數據結束時的處理'
    line = ''.join(self.data)
    self.data = []
    try:
      self.room.handle(self, line)
    except EndSession:
      self.handle_close()
  def handle_close(self):
    async_chat.handle_close(self)
    self.enter(LogoutRoom(self.server))

3.命令解釋器

現在就需要一個命令解釋器能夠解釋用戶的命令,例如登錄、查詢在線用戶和發消息等,代碼如下:

class CommandHandler:
  """
  命令處理類
  """
  def unknown(self, session, cmd):
    '響應未知命令'
    session.push('Unknown command: %s\n' % cmd)
  def handle(self, session, line):
    '命令處理'
    if not line.strip():
      return
    parts = line.split(' ', 1)
    cmd = parts[0]
    try:
      line = parts[1].strip()
    except IndexError:
      line = ''
    meth = getattr(self, 'do_' + cmd, None)
    try:
      meth(session, line)
    except TypeError:
      self.unknown(session, cmd)

4.房間

接下來就需要實現聊天室的房間了,這里我們定義了三種房間,分別是用戶剛登錄時的房間、聊天的房間和退出登錄的房間,這三種房間都有一個公共的父類,代碼如下:

class Room(CommandHandler):
  """
  包含多個用戶的環境,負責基本的命令處理和廣播
  """
  def __init__(self, server):
    self.server = server
    self.sessions = []
  def add(self, session):
    '一個用戶進入房間'
    self.sessions.append(session)
  def remove(self, session):
    '一個用戶離開房間'
    self.sessions.remove(session)
  def broadcast(self, line):
    '向所有的用戶發送指定消息'
    for session in self.sessions:
      session.push(line)
  def do_logout(self, session, line):
    '退出房間'
    raise EndSession
class LoginRoom(Room):
  """
  剛登錄的用戶的房間
  """
  def add(self, session):
    '用戶連接成功的回應'
    Room.add(self, session)
    session.push('Connect Success')
  def do_login(self, session, line):
    '登錄命令處理'
    name = line.strip()
    if not name:
      session.push('UserName Empty')
    elif name in self.server.users:
      session.push('UserName Exist')
    else:
      session.name = name
      session.enter(self.server.main_room)
class ChatRoom(Room):
  """
  聊天用的房間
  """
  def add(self, session):
    '廣播新用戶進入'
    session.push('Login Success')
    self.broadcast(session.name + ' has entered the room.\n')
    self.server.users[session.name] = session
    Room.add(self, session)
  def remove(self, session):
    '廣播用戶離開'
    Room.remove(self, session)
    self.broadcast(session.name + ' has left the room.\n')
  def do_say(self, session, line):
    '客戶端發送消息'
    self.broadcast(session.name + ': ' + line + '\n')
  def do_look(self, session, line):
    '查看在線用戶'
    session.push('Online Users:\n')
    for other in self.sessions:
      session.push(other.name + '\n')
class LogoutRoom(Room):
  """
  用戶退出時的房間
  """
  def add(self, session):
    '從服務器中移除'
    try:
      del self.server.users[session.name]
    except KeyError:
      pass

5.服務器端完整代碼

#!/usr/bin/python
# encoding: utf-8
from asyncore import dispatcher
from asynchat import async_chat
import socket, asyncore
PORT = 6666 #端口
class EndSession(Exception):
  """
  自定義會話結束時的異常
  """
  pass
class CommandHandler:
  """
  命令處理類
  """
  def unknown(self, session, cmd):
    '響應未知命令'
    session.push('Unknown command: %s\n' % cmd)
  def handle(self, session, line):
    '命令處理'
    if not line.strip():
      return
    parts = line.split(' ', 1)
    cmd = parts[0]
    try:
      line = parts[1].strip()
    except IndexError:
      line = ''
    meth = getattr(self, 'do_' + cmd, None)
    try:
      meth(session, line)
    except TypeError:
      self.unknown(session, cmd)
class Room(CommandHandler):
  """
  包含多個用戶的環境,負責基本的命令處理和廣播
  """
  def __init__(self, server):
    self.server = server
    self.sessions = []
  def add(self, session):
    '一個用戶進入房間'
    self.sessions.append(session)
  def remove(self, session):
    '一個用戶離開房間'
    self.sessions.remove(session)
  def broadcast(self, line):
    '向所有的用戶發送指定消息'
    for session in self.sessions:
      session.push(line)
  def do_logout(self, session, line):
    '退出房間'
    raise EndSession
class LoginRoom(Room):
  """
  剛登錄的用戶的房間
  """
  def add(self, session):
    '用戶連接成功的回應'
    Room.add(self, session)
    session.push('Connect Success')
  def do_login(self, session, line):
    '登錄命令處理'
    name = line.strip()
    if not name:
      session.push('UserName Empty')
    elif name in self.server.users:
      session.push('UserName Exist')
    else:
      session.name = name
      session.enter(self.server.main_room)
class ChatRoom(Room):
  """
  聊天用的房間
  """
  def add(self, session):
    '廣播新用戶進入'
    session.push('Login Success')
    self.broadcast(session.name + ' has entered the room.\n')
    self.server.users[session.name] = session
    Room.add(self, session)
  def remove(self, session):
    '廣播用戶離開'
    Room.remove(self, session)
    self.broadcast(session.name + ' has left the room.\n')
  def do_say(self, session, line):
    '客戶端發送消息'
    self.broadcast(session.name + ': ' + line + '\n')
  def do_look(self, session, line):
    '查看在線用戶'
    session.push('Online Users:\n')
    for other in self.sessions:
      session.push(other.name + '\n')
class LogoutRoom(Room):
  """
  用戶退出時的房間
  """
  def add(self, session):
    '從服務器中移除'
    try:
      del self.server.users[session.name]
    except KeyError:
      pass
class ChatSession(async_chat):
  """
  負責和單用戶通信
  """
  def __init__(self, server, sock):
    async_chat.__init__(self, sock)
    self.server = server
    self.set_terminator('\n')
    self.data = []
    self.name = None
    self.enter(LoginRoom(server))
  def enter(self, room):
    '從當前房間移除自身,然后添加到指定房間'
    try:
      cur = self.room
    except AttributeError:
      pass
    else:
      cur.remove(self)
    self.room = room
    room.add(self)
  def collect_incoming_data(self, data):
    '接受客戶端的數據'
    self.data.append(data)
  def found_terminator(self):
    '當客戶端的一條數據結束時的處理'
    line = ''.join(self.data)
    self.data = []
    try:
      self.room.handle(self, line)
    except EndSession:
      self.handle_close()
  def handle_close(self):
    async_chat.handle_close(self)
    self.enter(LogoutRoom(self.server))
class ChatServer(dispatcher):
  """
  聊天服務器
  """
  def __init__(self, port):
    dispatcher.__init__(self)
    self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
    self.set_reuse_addr()
    self.bind(('', port))
    self.listen(5)
    self.users = {}
    self.main_room = ChatRoom(self)
  def handle_accept(self):
    conn, addr = self.accept()
    ChatSession(self, conn)
if __name__ == '__main__':
  s = ChatServer(PORT)
  try:
    asyncore.loop()
  except KeyboardInterrupt:
    print

三、項目實戰(客戶端)

完成了服務器端后,就需要實現客戶端了,這里客戶端連接服務器使用了telnetlib模塊。

1.登錄窗口

這里的圖形界面包選擇了wxPython,前面有安裝說明,登錄窗口通過繼承wx.Frame類來實現,代碼如下:

class LoginFrame(wx.Frame):
  """
  登錄窗口
  """
  def __init__(self, parent, id, title, size):
    '初始化,添加控件并綁定事件'
    wx.Frame.__init__(self, parent, id, title)
    self.SetSize(size)
    self.Center()
    self.serverAddressLabel = wx.StaticText(self, label = "Server Address", pos = (10, 50), size = (120, 25))
    self.userNameLabel = wx.StaticText(self, label = "UserName", pos = (40, 100), size = (120, 25))
    self.serverAddress = wx.TextCtrl(self, pos = (120, 47), size = (150, 25))
    self.userName = wx.TextCtrl(self, pos = (120, 97), size = (150, 25))
    self.loginButton = wx.Button(self, label = 'Login', pos = (80, 145), size = (130, 30))
    self.loginButton.Bind(wx.EVT_BUTTON, self.login)
    self.Show()
  def login(self, event):
    '登錄處理'
    try:
      serverAddress = self.serverAddress.GetLineText(0).split(':')
      con.open(serverAddress[0], port = int(serverAddress[1]), timeout = 10)
      response = con.read_some()
      if response != 'Connect Success':
        self.showDialog('Error', 'Connect Fail!', (95, 20))
        return
      con.write('login ' + str(self.userName.GetLineText(0)) + '\n')
      response = con.read_some()
      if response == 'UserName Empty':
        self.showDialog('Error', 'UserName Empty!', (135, 20))
      elif response == 'UserName Exist':
        self.showDialog('Error', 'UserName Exist!', (135, 20))
      else:
        self.Close()
        ChatFrame(None, -2, title = 'ShiYanLou Chat Client', size = (500, 350))
    except Exception:
      self.showDialog('Error', 'Connect Fail!', (95, 20))
  def showDialog(self, title, content, size):
    '顯示錯誤信息對話框'
    dialog = wx.Dialog(self, title = title, size = size)
    dialog.Center()
    wx.StaticText(dialog, label = content)
    dialog.ShowModal()

2.聊天窗口

聊天窗口中最主要的就是向服務器發消息并接受服務器的消息,這里通過子線程來接受,代碼如下:

class ChatFrame(wx.Frame):
  """
  聊天窗口
  """
  def __init__(self, parent, id, title, size):
    '初始化,添加控件并綁定事件'
    wx.Frame.__init__(self, parent, id, title)
    self.SetSize(size)
    self.Center()
    self.chatFrame = wx.TextCtrl(self, pos = (5, 5), size = (490, 310), style = wx.TE_MULTILINE | wx.TE_READONLY)
    self.message = wx.TextCtrl(self, pos = (5, 320), size = (300, 25))
    self.sendButton = wx.Button(self, label = "Send", pos = (310, 320), size = (58, 25))
    self.usersButton = wx.Button(self, label = "Users", pos = (373, 320), size = (58, 25))
    self.closeButton = wx.Button(self, label = "Close", pos = (436, 320), size = (58, 25))
    self.sendButton.Bind(wx.EVT_BUTTON, self.send)
    self.usersButton.Bind(wx.EVT_BUTTON, self.lookUsers)
    self.closeButton.Bind(wx.EVT_BUTTON, self.close)
    thread.start_new_thread(self.receive, ())
    self.Show()
  def send(self, event):
    '發送消息'
    message = str(self.message.GetLineText(0)).strip()
    if message != '':
      con.write('say ' + message + '\n')
      self.message.Clear()
  def lookUsers(self, event):
    '查看當前在線用戶'
    con.write('look\n')
  def close(self, event):
    '關閉窗口'
    con.write('logout\n')
    con.close()
    self.Close()
  def receive(self):
    '接受服務器的消息'
    while True:
      sleep(0.6)
      result = con.read_very_eager()
      if result != '':
        self.chatFrame.AppendText(result)

3.客戶端完整代碼

#!/usr/bin/python
# encoding: utf-8
import wx
import telnetlib
from time import sleep
import thread
class LoginFrame(wx.Frame):
  """
  登錄窗口
  """
  def __init__(self, parent, id, title, size):
    '初始化,添加控件并綁定事件'
    wx.Frame.__init__(self, parent, id, title)
    self.SetSize(size)
    self.Center()
    self.serverAddressLabel = wx.StaticText(self, label = "Server Address", pos = (10, 50), size = (120, 25))
    self.userNameLabel = wx.StaticText(self, label = "UserName", pos = (40, 100), size = (120, 25))
    self.serverAddress = wx.TextCtrl(self, pos = (120, 47), size = (150, 25))
    self.userName = wx.TextCtrl(self, pos = (120, 97), size = (150, 25))
    self.loginButton = wx.Button(self, label = 'Login', pos = (80, 145), size = (130, 30))
    self.loginButton.Bind(wx.EVT_BUTTON, self.login)
    self.Show()
  def login(self, event):
    '登錄處理'
    try:
      serverAddress = self.serverAddress.GetLineText(0).split(':')
      con.open(serverAddress[0], port = int(serverAddress[1]), timeout = 10)
      response = con.read_some()
      if response != 'Connect Success':
        self.showDialog('Error', 'Connect Fail!', (95, 20))
        return
      con.write('login ' + str(self.userName.GetLineText(0)) + '\n')
      response = con.read_some()
      if response == 'UserName Empty':
        self.showDialog('Error', 'UserName Empty!', (135, 20))
      elif response == 'UserName Exist':
        self.showDialog('Error', 'UserName Exist!', (135, 20))
      else:
        self.Close()
        ChatFrame(None, -2, title = 'ShiYanLou Chat Client', size = (500, 350))
    except Exception:
      self.showDialog('Error', 'Connect Fail!', (95, 20))
  def showDialog(self, title, content, size):
    '顯示錯誤信息對話框'
    dialog = wx.Dialog(self, title = title, size = size)
    dialog.Center()
    wx.StaticText(dialog, label = content)
    dialog.ShowModal()
class ChatFrame(wx.Frame):
  """
  聊天窗口
  """
  def __init__(self, parent, id, title, size):
    '初始化,添加控件并綁定事件'
    wx.Frame.__init__(self, parent, id, title)
    self.SetSize(size)
    self.Center()
    self.chatFrame = wx.TextCtrl(self, pos = (5, 5), size = (490, 310), style = wx.TE_MULTILINE | wx.TE_READONLY)
    self.message = wx.TextCtrl(self, pos = (5, 320), size = (300, 25))
    self.sendButton = wx.Button(self, label = "Send", pos = (310, 320), size = (58, 25))
    self.usersButton = wx.Button(self, label = "Users", pos = (373, 320), size = (58, 25))
    self.closeButton = wx.Button(self, label = "Close", pos = (436, 320), size = (58, 25))
    self.sendButton.Bind(wx.EVT_BUTTON, self.send)
    self.usersButton.Bind(wx.EVT_BUTTON, self.lookUsers)
    self.closeButton.Bind(wx.EVT_BUTTON, self.close)
    thread.start_new_thread(self.receive, ())
    self.Show()
  def send(self, event):
    '發送消息'
    message = str(self.message.GetLineText(0)).strip()
    if message != '':
      con.write('say ' + message + '\n')
      self.message.Clear()
  def lookUsers(self, event):
    '查看當前在線用戶'
    con.write('look\n')
  def close(self, event):
    '關閉窗口'
    con.write('logout\n')
    con.close()
    self.Close()
  def receive(self):
    '接受服務器的消息'
    while True:
      sleep(0.6)
      result = con.read_very_eager()
      if result != '':
        self.chatFrame.AppendText(result)
'程序運行'
if __name__ == '__main__':
  app = wx.App()
  con = telnetlib.Telnet()
  LoginFrame(None, -1, title = "Login", size = (280, 200))
  app.MainLoop()

四、小結

最后就可以運行程序進行聊天了,注意需要先啟動服務器再啟動客戶端。這個項目中使用了asyncore的dispatcher來實現服務器,asynchat的asyn_chat來維護用戶的連接會話,用wxPython來實現圖形界面,用telnetlib來連接服務器,在子線程中接受服務器發來的消息,由此一個簡單的聊天室程序就完成了。

更多關于Python相關內容可查看本站專題:《Python Socket編程技巧總結》、《Python數據結構與算法教程》、《Python函數使用技巧總結》、《Python字符串操作技巧匯總》、《Python入門與進階經典教程》及《Python文件與目錄操作技巧匯總》

希望本文所述對大家Python程序設計有所幫助。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

翼城县| 宁海县| 瑞昌市| 南投市| 东乡| 兴安县| 讷河市| 宜兰县| 徐闻县| 繁峙县| 丹巴县| 安图县| 印江| 佳木斯市| 墨脱县| 泰州市| 平顶山市| 定安县| 祁东县| 延吉市| 都匀市| 深州市| 司法| 涿鹿县| 怀来县| 昌江| 治多县| 宜君县| 三台县| 招远市| 宣城市| 深水埗区| 全州县| 泉州市| 西贡区| 来凤县| 永清县| 平湖市| 康定县| 类乌齐县| 辽源市|