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()