python 全雙工 socket聊天

自學python一段時間,一直想弄個有意思的東西,所以就拿socket做一個聊天室,可以一對多,一對一全雙工聊天。後續可能完善程式碼在鼓弄一個帶gui介面的,比較有逼格

服務端:

使用socketserver模組,多執行緒非同步處理客戶端消息,接受客戶消息並轉發 既服務端為一個中轉站。

加入了 登陸 註冊 多人聊天 一對一聊天 防止同時在線

客戶端:

主執行緒連接服務端,兩個子執行緒分別負責讀寫

sercer:    # _*_ coding:utf-8 _*_  import SocketServer  from time import ctime  import threading, traceback  import Queue  from db import DB    lock = threading.Lock()  local_school = threading.local()      class Handler(object):      queue = []      db = DB()      user_name = {}        def __init__(self, sock):          self.sock = sock          self.input = [self.sock]          # self.queue.append(self.sock)        def recv(self):          data = self.sock.recv(1024).strip()          return data        def send(self, data):          self.sock.sendall(data)        def stop(self):          self.send('ByeBye')          self.sock.close()          self.queue.remove(self.sock)          del self.user_name[local_school.user]        def exit(self):          self.sock.close()          try:              self.queue.remove(self.sock)              del self.user_name[local_school.user]          except ValueError:              pass        def broadcast(self, user, data):          for sock in self.queue:              sock.sendall('[%s] %s::%s' % (ctime(), user, data))        def one(self, user_sock, user, data):          self.user_name[user_sock].sendall('--------------n%sn[%s]::(%s)n---------------' % (ctime(), user, data))        def yiduiduo(self, user, data):          time_data = ctime()          for sock in [x for x in self.queue if x != self.sock]:              sock.sendall('----------------n%sn[%s]::(%s)n----------------' % (time_data, user, data))        def handler(self):          funcdict = {              'login': self.login,              'registered': self.registered,          }          try:  ###異常處理 當客戶端異常斷開連接或者正常斷開連接:服務端處理異常              self.sock.send('請選擇::login/registered/exit')              data = self.recv()              if data == 'exit':                  self.stop()                  # self.send('exit')              elif data in funcdict:                  funcdict[data]()              else:                  self.handler()          except:              if self.queue:                  self.exit()                else:                  pass        def login(self):          self.send('輸入帳號密碼 格式:  user passwd  /server')          data = self.recv()          if data == 'server':              self.send('選擇 exit/handler')              data = self.recv()              if data == 'exit':                  self.stop()              elif data == 'handler':                  self.handler()              else:                  self.login()          user_data = data.split()          if len(user_data) == 2:              user = user_data[0]              passwd = user_data[1]              user_data = self.db.get_data() or {}              data_scok = self.user_name.get(user)  # 檢測該用戶是否在別處登陸 存在則登陸中  獲得登陸的sock              if data_scok:                  try:                      data_scok.sendall('帳號在別處登陸,被迫下線')                      data_scok.close()                      self.queue.remove(data_scok)                      del self.user_name[local_school.user]                  except:  ##異常處理  捕獲此處所有異常不做處理                      pass              if user in user_data and user_data[user] == passwd:                  local_school.user = user                  self.send('歡迎加入聊天室')                  self.queue.append(self.sock)                  self.broadcast('systemctl', '[%s]加入聊天室n' % user)                  self.user_name[user] = self.sock  ##用戶——sock 映射                  self.send('選擇:單(d)/多(s)')                  data = self.recv()                  if data == 's':                      self.Ltian()                  elif data == 'd':                      self.one_to_one()                  else:                      self.send('錯誤n')                      self.handler()                else:                  self.send('帳號或密碼不正確!n')                  self.login()            else:              self.send('格式錯誤!n')              self.login()        def registered(self):          self.send('註冊帳號密碼-格式 user passwd  /server')          data = self.recv()          if data == 'server':              self.send('選擇 exit/handler')              data = self.recv()              if data == 'exit':                  self.stop()                  self.send('exit')              else:                  self.handler()          user_data = data.split()          if len(user_data) == 2:              user = user_data[0]              passwd = user_data[1]                db_data = self.db.get_data() or {}              if user in db_data:                  self.send('用戶已註冊!')                  self.registered()              else:                  db_data[user] = passwd                  local_school.user = user                  lock.acquire()  # 添加執行緒鎖,防止執行緒同時修改  數據文件                  try:                      self.db.put_data(db_data)                  finally:                      lock.release()                  self.queue.append(self.sock)                  self.broadcast('system', '[%s]加入聊天室n' % user)                  self.user_name[user] = self.sock                  self.send('選擇:單人聊天(d)/多人聊天(s)n')                  data = self.recv()                  if data == 's':                      self.Ltian()                      print self.queue                  elif data == 'd':                      self.one_to_one()                  else:                      self.send('錯誤!n')                      self.handler()          else:              self.send('格式錯誤nn')              self.registered()        def Ltian(self, ):  # 多人聊天          print self.queue          print self.user_name          self.send('kaishiliaotian')          while True:              data = self.recv()              if data == 'exit':                  print 'queue1 ::%s' % self.queue                  self.stop()                  # self.send('關閉++++++++')                    print 'queue2 ::%s' % self.queue                  break              self.yiduiduo(local_school.user, data)  # 組播消息        def one_to_one(self):          self.send('選擇對象:to:user')          user_data = self.recv()[3:]          if user_data == local_school.user:              self.one_to_one()          if user_data in self.db.get_data():                if self.user_name.get(user_data) and self.user_name[user_data] in self.queue:                  self.send('kaishiliaotian')                  while True:                      data = self.recv()                        # if  data is None:                      if data == 'server':                          self.send('選擇:exit/Ltian(s)')                          data = self.recv()                          if data == 'exit':                              self.one(user_data, local_school.user, '已下線')                              self.stop()                              break                          elif data == 's':                              self.Ltian()                      elif not data == '' and self.user_name.get(user_data):  # 判斷 數據不為空 且用戶狀態在線否                          self.one(user_data, local_school.user, data)              else:                  self.send('用戶不在線')                  self.one_to_one()          else:              self.send('用戶不存在!n')              self.one_to_one()      class MyServer(SocketServer.BaseRequestHandler):      def handle(self):          print self.client_address          self.mysock = Handler(self.request)          print self.mysock.queue          self.mysock.handler()      if __name__ == '__main__':      host = '127.0.0.1'      port = 9999      addr = (host, port)      server = SocketServer.ThreadingTCPServer(addr, MyServer)      server.request_queue_size = 4399      server.serve_forever()        server.shutdown()      client:    # _*_ coding:utf-8 _*_  from socket import *  import threading  threads=[]  class Client_Handler(object):      def __init__(self, ipadr='127.0.0.1', port=9999):          self.sock = socket(AF_INET, SOCK_STREAM)          self.sock.connect((ipadr, port))          self.input=[self.sock]          print self.input      def send(self,data):          self.sock.sendall(data)        def recv(self):          data = self.sock.recv(1024).strip()          print data          return  data          def write(self):          while True:              try:                  data=raw_input('>>>')                  if data=='exit':                      self.send('exit')                      self.sock.close()                      break                  self.send(data)              except socket.error: #加入異常處理  當服務端斷開sock連接時跳出while循環                  break              except:                  break      def read(self):          while  True:              try:                  self.recv()              except socket.error:                  break              except:                  break  a1=Client_Handler()  chat = threading.Thread(target=a1.write)  threads.append(chat)  chat = threading.Thread(target=a1.read)  threads.append(chat)  print threads  for i in range(len(threads)):      threads[i].start()