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 服务端的建立 首先,在服 ...
随机推荐
- etherlime-4-Etherlime CLI
Etherlime CLI命令行界面 Installing & Help Syntax语法 npm i -g etherlime Install the global etherlime to ...
- regex_match
原型:bool regex_match(InputSequence[,MatchResults] , Regex[ , Flags]); 当模式匹配整个输入序列成功时,返回的是true,否则返回fal ...
- 最新Flume1.7 自定义 MongodbSink 结合TAILDIR Sources的使用
Flume MongodbSink 此mongodb支持3.0 github地址 MongodbSink flume-ng-mongodbsink An Apache Flume Sink that ...
- FFMpeg笔记(一) 使用FFmpeg将任意格式图片转换成任意格式图片
void SrcToDest(char* pSrc, char* pDest,unsigned int nSrcWidth, unsigned int nSrcHeight, AVPixelForma ...
- Linux Shell常用技巧(五)
十一. awk编程: 1. 变量: 在awk中变量无须定义即可使用,变量在赋值时即已经完成了定义.变量的类型可以是数字.字符串.根据使用的不同,未初始化变量的值为0或空白字符串&quo ...
- ARM Linux驱动篇 学习温度传感器ds18b20的驱动编写过程
ARM Linux驱动篇 学习温度传感器ds18b20的驱动编写过程 原文地址:http://www.cnblogs.com/NickQ/p/9026545.html 一.开发板与ds18b20的入门 ...
- 7款易上手C语言编程软件推荐
C语言是一门历史很长的编程语言,其编译器和开发工具也多种多样,其开发工具包括编译器,现举几个开发工具供大家选择,当然也要根据自己的操作系统来选择适合自己的开发工具. 好多刚开始接触c语言的朋友都想知道 ...
- Spring第三天——AOP注解实现与事务管理
大致内容: aspectJ的aop操作(基于注解,对比day02配置操作)(会用) *jdbcTemplate操作(实现CRUD) *spring配置连接池 *spring事务管理 一.AspectJ ...
- PyQt5 笔记(02):嵌套布局
如前一篇笔记,我们还是只讨论两层嵌套布局的情况. 前面的布局有一个缺点:有三个内层布局,则需要三个空部件.那若有十个内层布局呢?显然会让人不舒服. 刚才在玩 Qt Designer 时,发现了一个更好 ...
- Oracle保存带&的数据
在SQL*Plus中默认的"&"表示替代变量,也就是说,只要在命令中出现该符号,SQL*Plus就会要你输入替代值.这就意味着你无法将一个含有该符号的字符串输入数据库或赋给 ...