前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python 全双工 socket聊天

python 全双工 socket聊天

作者头像
py3study
发布2020-01-07 15:18:56
1.1K0
发布2020-01-07 15:18:56
举报
文章被收录于专栏:python3python3

自学python一段时间,一直想弄个有意思的东西,所以就拿socket做一个聊天室,可以一对多,一对一全双工聊天。后续可能完善代码在鼓弄一个带gui界面的,比较有逼格

服务端:

使用socketserver模块,多线程异步处理客户端消息,接受客户消息并转发 既服务端为一个中转站。

加入了 登陆 注册 多人聊天 一对一聊天 防止同时在线

客户端:

主线程连接服务端,两个子线程分别负责读写

代码语言:javascript
复制
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()
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-09-19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档