在我的《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()

运行方式:电动叉车公司

  1. 启动server
    python server.py

  2. 启动client1
    python client.py

  3. 启动client2
    python client.py

在client1的console中输入任何字符串,client2中立马就可以收到

python的多路复用实现聊天群的更多相关文章

  1. 怎么用python 3 开发钉钉群机器人

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:Python绿色通道 PS:如有需要Python学习资料的小伙伴可以加 ...

  2. Python——IO多路复用之select模块epoll方法

    Python——IO多路复用之select模块epoll方法 使用epoll方法实现IO多路复用,使用方法基本与poll方法一致,epoll效率要高于select和poll. .├── epoll_c ...

  3. Python——IO多路复用之select模块poll方法

    Python——IO多路复用之select模块poll方法 使用poll方法实现IO多路复用 .├── poll_client.py├── poll_server.py└── settings.py ...

  4. Python——IO多路复用之select模块select方法

    Python——IO多路复用之select模块select方法 使用select模块的select方法实现Python——IO多路复用 实现同时将终端输入的文本以及客户端传输的文本写入文本文件中: w ...

  5. python 连接 redis cluster 集群

    一. redis集群模式有多种, cluster模式只是其中的一种实现方式, 其原理请自行谷歌或者百度, 这里只举例如何使用Python操作 redis cluster 集群 二. python 连接 ...

  6. python连接redis哨兵集群

    一.redis集群模式有多种, 哨兵模式只是其中的一种实现方式, 其原理请自行谷歌或者百度 二.python 连接 redis 哨兵集群 1. 安装redis包 pip install redis 2 ...

  7. 利用python获取自己的qq群成员信息

    利用python获取自己的qq群成员信息! 首先说明一下需要使用的工具以及技术:python3 + selenium selenium安装方法:pip install selenium 前提:获取自己 ...

  8. 今天建了一个Python学习交流的QQ群,求喜欢python的一起来交流。

    版权归作者所有,任何形式转载请联系作者.作者:枫(来自豆瓣)来源:https://www.douban.com/note/666182545/ 现在学python的人越来越多了,我也开始学习了,大群里 ...

  9. python实现简单的聊天小程序

    概要 这是一个使用python实现一个简单的聊天室的功能,里面包含群聊,私聊两种聊天方式.实现的方式是使用套接字编程的一个使用TCP协议 c/s结构的聊天室 实现思路 x01 服务端的建立 首先,在服 ...

随机推荐

  1. Django提示Unknown database处理方法

    cmd.exe运行别人的程序 C:\Python27\Python.exe E:\Django\Guest\Guest\manage.py runserver 提示 django.db.utils.I ...

  2. HDU 1257 最少拦截系统(贪心 or LIS)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1257 最少拦截系统 Time Limit: 2000/1000 MS (Java/Others)   ...

  3. zdump 命令查看时区和夏令时

    zdump -v 时区名称1. 查看中国PRC时区的2007年的时区规则# zdump -v PRC | grep 2009PRC Mon Sep 17 06:03:55 2007 UTC = Mon ...

  4. Mybatis的使用与流程解析

    1. 什么是MyBatis MyBatis的前身是Apache的一个开源项目ibatis,后来迁移到Google code就改名为MyBatis. 官方解释: MyBatis是一款优秀的持久层框架,它 ...

  5. java.sql.SQLSyntaxErrorException: ORA-01722: 无效数字

    ### Error updating database. Cause: java.sql.SQLSyntaxErrorException: ORA-01722: 无效数字 ### The error ...

  6. 使用CBrother做TCP服务器与C++客户端通信

    使用CBrother脚本做TCP服务器与C++客户端通信 工作中总是会遇到一些对于服务器压力不是特别大,但是代码量比较多,用C++写起来很不方便.对于这种需求,我选择用CBrother脚本做服务器,之 ...

  7. C# 获取北京时间 (根据纪元时间(1970/1/1)转换为DateTime)

    根据纪元时间(1970/1/1)转换为DateTime WebClient wc = new WebClient(); s= wc.DownloadString("http://api.ti ...

  8. 20155222 c语言实现pwd命令

    20155222 c语言实现linux的pwd命令 1.学习pwd命令在Linux层次结构中,用户可以在被授权的任意目录下利用mkdir命令创建新目录,也可以利用cd命令从一个目录转换到另一个目录.然 ...

  9. JavaEE笔记(一)

    Hibernate Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自 ...

  10. Keil 中的Code,RO-data,RW-data,ZI-data

    我们开发项目中,编译.链接完成后,都要看一下RAM和Flash的利用情况,尤其在有bootloader的情况下,确保自己的单片机选型能符合项目的要求.其中ARM程序的组成,是很容易混淆的.此处所说的“ ...