# =========================server
import select
import socket
# select 和 poll 和epoll的区别
#
# select和poll有一个共同的机制,都是采用轮训的方式去询问内核,有没有数据准备好了
# select有一个最大监听事件的限制,32位机限制1024,,6位机限制2048
# poll没有,理论上poll可以开启无限大,1G内存大概够你开10W个事件去监听
#
# epoll是最好的,采用的是回调机制,解决了select和poll共同存在的问题
# 而且epoll理论上也可以开启无限多个监听事件
sock = socket.socket()
sock.bind(('127.0.0.1',8888))
sock.listen(5)
r_lst = [sock]
w_lst = []
msg_dict = {}
while r_lst:
# 监听socket请求
r,w,x = select.select(r_lst,w_lst,r_lst)# 第一个参数是我们需要监听可读的套接字, 第二个参数是我们需要监听可写的套接字, 第三个参数使我们需要监听异常的套接字, 第四个则是时间限制设置.
for i in r: # 判断是否有可读的请求
if i == sock: # 如果sock可读,说明有新的客户端前来链接
conn,addr = i.accept() # 链接新的客户端
r_lst.append(conn) # 将客户端的链接也放入可读套接字列表中继续监听
msg_dict[conn] = [] # 给每一个客户端都创建一个消息字典,回复消息的时候使用
else: # 如果不是sock那么,说明客户端有消息发过来
try:
msg = i.recv(1024) # 接收消息
w_lst.append(i) # 将连接加入可写监听
except ConnectionResetError: # 如果客户端意外断开连接,会爆这个错误
msg = b''
if msg == b'': # 如果客户端发过来的是空消息,那么代表客户端已经断开连接
r_lst.remove(i) # 在可读套接字列表中删除当前连接
i.close() # 关闭连接
del msg_dict[i] # 删除保存的消息记录
else:
print(msg)
msg_dict[i].append(msg.decode('utf-8').upper().encode('utf-8'))# 将客户端传过来的消息转换成大写,保存起来。当做回复消息
for i in w: # 判断是否有可写消息
msg = msg_dict.get(i) # 判断消息字典中是否存在 当前链接,如果不存在说明当前客户端已经关闭
if msg: # 如果存在则返回消息
i.send(msg.pop())
w_lst.remove(i)
else: # 如果不存在,则在监听列表中删除当前链接
if i in r_lst:
r_lst.remove(i)
for i in x: # 判断是否有出错的接连,如果有直接删除,并且关闭连接
if i in r_lst:
r_lst.remove(i)
if i in msg_dict:
del msg_dict[i]
i.close()
# ====================== client
import socket
sock = socket.socket()
sock.connect(('127.0.0.1',8888))
while True:
msg = input('>>>')
if msg == '':
continue
if msg == 'q':
break
sock.send(msg.encode('utf-8'))
print(sock.recv(1024))
sock.close()