python 全双工 socket聊天

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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%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()



本文出自 “13275081” 博客,请务必保留此出处http://13285081.blog.51cto.com/13275081/1964230

以上是关于python 全双工 socket聊天的主要内容,如果未能解决你的问题,请参考以下文章

python 全双工 socket聊天

Python写的简陋版一对一聊天工具,全双工

Python实现多用户全双工聊天(一对一)

用socket模拟实现全双工通信

Socket实现一个简单的半双工通信

socket api- c/s模式:全双工 ;IO模式:同步阻塞,epoll,多路复用。