python 全雙工 socket聊天

自學python一段時間,一直想弄個有意思的東西,因此就拿socket作一個聊天室,能夠一對多,一對一全雙工聊天。後續可能完善代碼在鼓弄一個帶gui界面的,比較有逼格j_0061.gifpython


服務端:多線程

使用socketserver模塊,多線程異步處理客戶端消息,接受客戶消息並轉發 既服務端爲一箇中轉站。app

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


客戶端:socket

主線程鏈接服務端,兩個子線程分別負責讀寫ide


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%s\n[%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%s\n[%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('格式錯誤\n\n')
            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()
相關文章
相關標籤/搜索