python的多路复用实现聊天群
在我的《python高级编程和异步io编程》中我讲解了socket编程,这里贴一段用socket实现聊天室的功能的源码,因为最近工作比较忙,后期我会将这里的代码细节分析出来,目前先把代码贴上。大家自行研究运行一下。
server端:
"""
server select
"""
import sys
import time
import socket
import select
import logging
from queue import Queue
import queue
g_select_timeout = 10
class Server(object):
    def __init__(self, host='0.0.0.0', port=3333, timeout=2, client_nums=10):
        self.__host = host
        self.__port = port
        self.__timeout = timeout
        self.__client_nums = client_nums
        self.__buffer_size = 1024
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.setblocking(False)
        self.server.settimeout(self.__timeout)
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)  # keepalive
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 端口复用
        server_host = (self.__host, self.__port)
        try:
            self.server.bind(server_host)
            self.server.listen(self.__client_nums)
        except:
            raise
        self.inputs = [self.server]  # select 接收文件描述符列表
        self.outputs = []  # 输出文件描述符列表
        self.message_queues = {}  # 消息队列
        self.client_info = {}
    def run(self):
        while True:
            readable, writable, exceptional = select.select(self.inputs, self.outputs, self.inputs, g_select_timeout)
            if not (readable or writable or exceptional):
                continue
            for s in readable:
                if s is self.server:  # 是客户端连接
                    connection, client_address = s.accept()
                    # print "connection", connection
                    print("%s connect." % str(client_address))
                    connection.setblocking(0)  # 非阻塞
                    self.inputs.append(connection)  # 客户端添加到inputs
                    self.client_info[connection] = str(client_address)
                    self.message_queues[connection] = Queue()  # 每个客户端一个消息队列
                else:  # 是client, 数据发送过来
                    try:
                        data = s.recv(self.__buffer_size)
                    except:
                        err_msg = "Client Error!"
                        logging.error(err_msg)
                    if data:
                        # print data
                        data = "%s %s say: %s" % (time.strftime("%Y-%m-%d %H:%M:%S"), self.client_info[s], data)
                        self.message_queues[s].put(data)  # 队列添加消息
                        if s not in self.outputs:  # 要回复消息
                            self.outputs.append(s)
                    else:  # 客户端断开
                        # Interpret empty result as closed connection
                        print
                        "Client:%s Close." % str(self.client_info[s])
                        if s in self.outputs:
                            self.outputs.remove(s)
                        self.inputs.remove(s)
                        s.close()delself.message_queues[s]delself.client_info[s]for s in writable:# outputs 有消息就要发出去了try:
                    next_msg =self.message_queues[s].get_nowait()# 非阻塞获取except queue.Empty:
                    err_msg ="Output Queue is Empty!"# g_logFd.writeFormatMsg(g_logFd.LEVEL_INFO, err_msg)self.outputs.remove(s)exceptExceptionas e:# 发送的时候客户端关闭了则会出现writable和readable同时有数据,会出现message_queues的keyerror
                    err_msg ="Send Data Error! ErrMsg:%s"% str(e)
                    logging.error(err_msg)if s inself.outputs:self.outputs.remove(s)else:for cli inself.client_info:# 发送给其他客户端if cli isnot s:try:
                                cli.sendall(next_msg.encode("utf8"))exceptExceptionas e:# 发送失败就关掉
                                err_msg ="Send Data to %s  Error! ErrMsg:%s"%(str(self.client_info[cli]), str(e))
                                logging.error(err_msg)print"Client: %s Close Error."% str(self.client_info[cli])if cli inself.inputs:self.inputs.remove(cli)
                                    cli.close()if cli inself.outputs:self.outputs.remove(s)if cli inself.message_queues:delself.message_queues[s]delself.client_info[cli]for s in exceptional:
                logging.error("Client:%s Close Error."% str(self.client_info[cli]))if s inself.inputs:self.inputs.remove(s)
                    s.close()if s inself.outputs:self.outputs.remove(s)if s inself.message_queues:delself.message_queues[s]delself.client_info[s]if"__main__"== __name__:Server().run()
client端
#!/usr/local/bin/python
# *-* coding:utf-8 -*-
"""
client.py
"""
import sys
import time
import socket
import threading
class Client(object):
    def __init__(self, host, port=3333, timeout=1, reconnect=2):
        self.__host = host
        self.__port = port
        self.__timeout = timeout
        self.__buffer_size = 1024
        self.__flag = 1
        self.client = None
        self.__lock = threading.Lock()
    @property
    def flag(self):
        return self.__flag
    @flag.setter
    def flag(self, new_num):
        self.__flag = new_num
    def __connect(self):
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # client.bind(('0.0.0.0', 12345,))
        client.setblocking(True)
        client.settimeout(self.__timeout)
        client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 端口复用
        server_host = (self.__host, self.__port)
        try:
            client.connect(server_host)
        except:
            raise
        return client
    def send_msg(self):
        if not self.client:
            return
        while True:
            time.sleep(0.1)
            # data = raw_input()
            data = sys.stdin.readline().strip()
            if "exit" == data.lower():
                with self.__lock:
                    self.flag = 0
                break
            self.client.sendall(data.encode("utf8"))
        return
    def recv_msg(self):
        if not self.client:
            return
        while True:
            data = None
            with self.__lock:
                if not self.flag:
                    print('ByeBye~~')
                    break
            try:
                data = self.client.recv(self.__buffer_size)
            except socket.timeout:
                continue
            except:
                raise
            if data:
                print("%s\n" % data)
            time.sleep(0.1)
        return
    def run(self):
        self.client = self.__connect()
        send_proc = threading.Thread(target=self.send_msg)
        recv_proc = threading.Thread(target=self.recv_msg)
        recv_proc.start()
        send_proc.start()
        recv_proc.join()
        send_proc.join()
        self.client.close()
if "__main__" == __name__:
    Client('localhost').run()
运行方式:电动叉车公司
启动server
python server.py启动client1
python client.py- 启动client2
python client.py 
在client1的console中输入任何字符串,client2中立马就可以收到
python的多路复用实现聊天群的更多相关文章
- 怎么用python 3 开发钉钉群机器人
		
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:Python绿色通道 PS:如有需要Python学习资料的小伙伴可以加 ...
 - Python——IO多路复用之select模块epoll方法
		
Python——IO多路复用之select模块epoll方法 使用epoll方法实现IO多路复用,使用方法基本与poll方法一致,epoll效率要高于select和poll. .├── epoll_c ...
 - Python——IO多路复用之select模块poll方法
		
Python——IO多路复用之select模块poll方法 使用poll方法实现IO多路复用 .├── poll_client.py├── poll_server.py└── settings.py ...
 - Python——IO多路复用之select模块select方法
		
Python——IO多路复用之select模块select方法 使用select模块的select方法实现Python——IO多路复用 实现同时将终端输入的文本以及客户端传输的文本写入文本文件中: w ...
 - python 连接 redis cluster 集群
		
一. redis集群模式有多种, cluster模式只是其中的一种实现方式, 其原理请自行谷歌或者百度, 这里只举例如何使用Python操作 redis cluster 集群 二. python 连接 ...
 - python连接redis哨兵集群
		
一.redis集群模式有多种, 哨兵模式只是其中的一种实现方式, 其原理请自行谷歌或者百度 二.python 连接 redis 哨兵集群 1. 安装redis包 pip install redis 2 ...
 - 利用python获取自己的qq群成员信息
		
利用python获取自己的qq群成员信息! 首先说明一下需要使用的工具以及技术:python3 + selenium selenium安装方法:pip install selenium 前提:获取自己 ...
 - 今天建了一个Python学习交流的QQ群,求喜欢python的一起来交流。
		
版权归作者所有,任何形式转载请联系作者.作者:枫(来自豆瓣)来源:https://www.douban.com/note/666182545/ 现在学python的人越来越多了,我也开始学习了,大群里 ...
 - python实现简单的聊天小程序
		
概要 这是一个使用python实现一个简单的聊天室的功能,里面包含群聊,私聊两种聊天方式.实现的方式是使用套接字编程的一个使用TCP协议 c/s结构的聊天室 实现思路 x01 服务端的建立 首先,在服 ...
 
随机推荐
- 集合之ArrayList
			
一.ArrayList概述 ArrayList是实现List接口的动态数组,所谓动态就是它的大小是可变的.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口外,此类 ...
 - TensorFlow Activation Function 1
			
部分转自:https://blog.csdn.net/caicaiatnbu/article/details/72745156 激活函数(Activation Function)运行时激活神经网络中某 ...
 - Spring源码分析(二十二)功能扩展
			
摘要: 本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 目录 一.增加SPEL语言的支持 二.增加属性注册编辑器 1. 使用自 ...
 - Linux Shell常用技巧(二)
			
七. grep家族: 1. grep退出状态: 0: 表示成功: 1: 表示在所提供的文件无法找到匹配的pattern: 2: 表示参数中提供的文件不存在. 见如 ...
 - 渲染引擎,HTML解析
			
这是how browser to work 的翻译 转自:携程设计委员会 渲染引擎 渲染引擎的职责是……渲染,也就是把请求的内容显示到浏览器屏幕上. 默认情况下渲染引擎可以显示HTML,XML文档以及 ...
 - 三、用Delphi10.3 创建一条JSON数据的第三种方法,非常简洁的写法
			
一.用Delphi10.3构造一个JSON数据的第三种方法,并格式化输出,代码如下: uses // System.JSON, System.JSON.Types, System.JSON.Write ...
 - 关于"为什么说Arduino是玩具?"的回答
			
最开始从51入门.之后MSP.ARM.FriendARM等等和使用keil(MDK).iar等工具.之后Arduino.Raspberry Pi的人想说: "说'Arduino是玩具,和Ar ...
 - SAP库龄表
			
&---------------------------------------------------------------------* *& Report ZFIR005 *& ...
 - lua协程实现
			
协程是个很好的东西,它能做的事情与线程相似,区别在于:协程是使用者可控的,有API给使用者来暂停和继续执行,而线程由操作系统内核控制:另外,协程也更加轻量级.这样,在遇到某些可能阻塞的操作时,可以使用 ...
 - 天天沉迷于皇上本宫的都是sb
			
天天沉迷于皇上.本宫.奴才.太后的都是sb,时不时还要被某王和某平民的爱情感动的落泪.这是病,要治,最有效的治疗方法是38度的夏天去搬砖. 拍这些电视的人真不傻,知道真sb多,这种电视剧才能爆款.