from socket import * #1.创建socket serSocket = socket(AF_INET, SOCK_STREAM) #2. 绑定本地ip以及port localAddr = ('', 7788) serSocket.bind(localAddr) #3. 让这个socket 变为非堵塞 serSocket.setblocking(False) #4. 将socket变为监听(被动)套接字 serSocket.listen(100) # 用来保存所有已经连接的客户端的信息 clientAddrList = [] while True: #等待一个新的客户端的到来(即完成3次握手的客户端) try: clientSocket,clientAddr = serSocket.accept() except: pass else: print("一个新的客户端到来:%s"%str(clientAddr)) clientSocket.setblocking(False) clientAddrList.append((clientSocket,clientAddr)) for clientSocket,clientAddr in clientAddrList: try: recvData = clientSocket.recv(1024) except: pass else: if len(recvData)>0: print("%s:%s"%(str(clientAddr), recvData)) else: clientSocket.close() clientAddrList.remove((clientSocket, clientAddr)) print("%s 已经下线"%str(clientAddr))
select,轮询最多可监听1024个人,poll无默认值,但是也是轮询
import select import socket import sys server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('', 7788)) server.listen(5) inputs = [server, sys.stdin] running = True while True: # 调用 select 函数,阻塞等待 readable, writeable, exceptional = select.select(inputs, [], []) # 数据抵达,循环 for sock in readable: # 监听到有新的连接 if sock == server: conn, addr = server.accept() # select 监听的socket inputs.append(conn) # 监听到键盘有输入 elif sock == sys.stdin: cmd = sys.stdin.readline() running = False break # 有数据到达 else: # 读取客户端连接发送的数据 data = sock.recv(1024) if data: sock.send(data) else: # 移除select监听的socket inputs.remove(sock) sock.close() # 如果检测到用户输入敲击键盘,那么就退出 if not running: break server.close()
epoll非轮询,无限制,事件通知机制
- EPOLLIN (可读)
- EPOLLOUT (可写)
- EPOLLET (ET模式)
epoll对文件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger)。LT模式是默认模式,LT模式与ET模式的区别如下:
LT模式:当epoll检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll时,会再次响应应用程序并通知此事件。
ET模式:当epoll检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll时,不会再次响应应用程序并通知此事件。
import socket import select # 创建套接字 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 设置可以重复使用绑定的信息 s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 绑定本机信息 s.bind(("",7788)) # 变为被动 s.listen(10) # 创建一个epoll对象 epoll=select.epoll() # 测试,用来打印套接字对应的文件描述符 # print s.fileno() # print select.EPOLLIN|select.EPOLLET # 注册事件到epoll中 # epoll.register(fd[, eventmask]) # 注意,如果fd已经注册过,则会发生异常 # 将创建的套接字添加到epoll的事件监听中 epoll.register(s.fileno(),select.EPOLLIN|select.EPOLLET) connections = {} addresses = {} # 循环等待客户端的到来或者对方发送数据 while True: # epoll 进行 fd 扫描的地方 -- 未指定超时时间则为阻塞等待 epoll_list=epoll.poll() # 对事件进行判断 for fd,events in epoll_list: # print fd # print events # 如果是socket创建的套接字被激活 if fd == s.fileno(): conn,addr=s.accept() print('有新的客户端到来%s'%str(addr)) # 将 conn 和 addr 信息分别保存起来 connections[conn.fileno()] = conn addresses[conn.fileno()] = addr # 向 epoll 中注册 连接 socket 的 可读 事件 epoll.register(conn.fileno(), select.EPOLLIN | select.EPOLLET) elif events == select.EPOLLIN: # 从激活 fd 上接收 recvData = connections[fd].recv(1024) if len(recvData)>0: print('recv:%s'%recvData) else: # 从 epoll 中移除该 连接 fd epoll.unregister(fd) # server 侧主动关闭该 连接 fd connections[fd].close() print("%s---offline---"%str(addresses[fd]))
gevent协程服务器
import sys import time import gevent from gevent import socket,monkey monkey.patch_all() def handle_request(conn): while True: data = conn.recv(1024) if not data: conn.close() break print("recv:", data) conn.send(data) def server(port): s = socket.socket() s.bind(('', port)) s.listen(5) while True: cli, addr = s.accept() gevent.spawn(handle_request, cli) if __name__ == '__main__': server(7788)