一个简单的单线程异步服务器

使用内select模块构造单线程异步服务器

关键:

poll(),生成器

服务器模块:

#!/usr/bin/env python3
#-*- encoding:UTF -*-

import argparse,socket,time

aphorisms = {b‘Beautiful is better than?‘:b‘Ugly.‘,
             b‘Explicit is better than?‘:b‘Implicit.‘,
             b‘Simple is better than?‘:b‘Complex.‘}

def get_answer(aphorism):
    return aphorisms.get(aphorism,b‘Error:unknow aphorism‘)

def parse_command_line(description):
    parser = argparse.ArgumentParser(description=description)
    parser.add_argument(‘host‘,help=‘IP or hostname‘)
    parser.add_argument(‘-p‘,metavar=‘port‘,type=int,default=1060,
                        help=‘TCP port (default 1060)‘)
    args = parser.parse_args()
    address =(args.host,args.p)
    return address

def create_srv_socket(address):
    listener = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    listener.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    listener.bind(address)
    listener.listen(64)
    print(‘Listening at {}‘.format(address))
    return listener

def accept_connections_forever(listener):
    while True:
        sock,address = listener.accpet()
        print(‘Accepted connection from {}‘.format(address))
        handle_conversation(sock,address)

def handle_conversation(sock,address):
    try:
        while True:
            handle_request(sock)
    except EOFError:
        print(‘Client socket to {} has closed‘.format(address))
    except Exception as e:
        print(‘Client {} error :{}‘.format(address,e))
    finally:
        socket.close()

def handle_request(sock):
    aphorism = recv_until(sock,b‘?‘)
    answer = get_answer(aphorism)
    sock.sendall(answer)

def recv_until(sock,suffix):
    message = sock.recv(4096)
    if not message:
        raise EOFError(‘socket closed‘)
    while not message.endswith(suffix):
        data = sock.recv(4096)
        if not data:
            raise IOError(‘received {!r} then socket closed‘.format(message))
        message += data
    return message

客户端模块:

#!/usr/bin/env python3
#-*- encding:UTF-8 -*-

import argparse,random,socket,zen_utils

def client(address,cause_error=False):
    sock =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    sock.connect(address)
    aphorisms = list(zen_utils.aphorisms)
    if cause_error:
        sock.sendall(aphorisms[0][:-1])
        return
    for aphorism in random.sample(aphorisms,3):
        sock.sendall(aphorism)
        print(aphorism,zen_utils.recv_until(sock,b‘.‘))
    sock.close()

if __name__ == ‘__main__‘:
    parser = argparse.ArgumentParser(description = ‘Example client‘)
    parser.add_argument(‘host‘,help=‘IP or Hostname‘)
    parser.add_argument(‘-p‘,metavar=‘port‘,type=int,default=1060,
                        help=‘TCP Port number‘)
    parser.add_argument(‘-e‘,action=‘store_true‘,help=‘cause an error‘)
    args = parser.parse_args()
    address = (args.host,args.p)
    client(address,args.e)

异步服务器:

#!/usr/bin/env python3
#-*- encoding:UTF -*-

import select,zen_utils

def all_events_forever(poll_object):
    while True:
        for fd,event in poll_object.poll():
            yield fd,event

def serve(listener):
    sockets = {listener.fileno():listener}
    addresses = {}
    bytes_received = {}
    bytes_to_send = {}

    poll_object = select.poll()
    poll_object.register(listener,select.POLLIN)

    for fd,event in all_events_forever(poll_object):
        sock = sockets[fd]

        if event &(select.POLLHUP | select.POLLERR | select.POLLNVAL):
            address = addresses.pop(sock)
            rb = bytes_received.pop(sock,b‘‘)
            sb = bytes_to_send.pop(sock,b‘‘)
            if rb:
                print(‘Client {} sent {} but then closed‘.format(address,rb))
            elif sb:
                print(‘Client {} closed before we sent {}‘.format(address,sb))
            else:
                print(‘Client {} closed socket normally‘.format(address))
            poll_object.unregister(fd)
            del sockets[fd]

        elif sock is listener:
            sock,address = sock.accept()
            print(‘Accepted connection from {}‘.format(address))
            sock.setblocking(False)
            sockets[sock.fileno()] = sock
            addresses[sock] = address
            poll_object.register(sock,select.POLLIN)

        elif event & select.POLLIN:
            more_data = sock.recv(4096)
            if not more_data:
                sock.close()
                continue
            data = bytes_received.pop(sock,b‘‘) + more_data
            if data.endswith(b‘?‘):
                bytes_to_send[sock] = zen_utils.get_answer(data)
                poll_object.modify(sock,select.POLLOUT)

        elif event & select.POLLOUT:
            data = bytes_to_send.pop(sock)
            n = sock.send(data)
            if n < len(data):
                bytes_to_send[sock] = data[n:]
            else:
                poll_object.modify(sock,select.POLLIN)

if __name__ == ‘__main__‘:
    address = zen_utils.parse_command_line(‘Low-level async server‘)
    listener = zen_utils.create_srv_socket(address)
    serve(listener)

这个异步服务器的核心是它的缓冲区:

在等待某个请求完成时,会将受到的数据存储在bytes_received字典中;在等待操作系统安排发送数据时,会将要发送的字节存储在bytes_to_send字典中。

这两个缓冲区与我们告知poll()要在每个套接字上等待的事件一起形成了一个完整的状态机,用于一步一步的处理客户端会话。

时间: 2024-12-11 00:01:56

一个简单的单线程异步服务器的相关文章

一个简单的Java web服务器实现

一个简单的Java web服务器实现,比较简单,基于java.net.Socket和java.net.ServerSocket实现: 程序执行步骤 创建一个ServerSocket对象: 调用ServerSocket对象的accept方法,等待连接,连接成功会返回一个Socket对象,否则一直阻塞等待: 从Socket对象中获取InputStream和OutputStream字节流,这两个流分别对应request请求和response响应: 处理请求:读取InputStream字节流信息,转成字

漫谈:一个简单的单线程基于epoll的echo服务器(附简单的性能测试)

为什么使用epoll 这个是老生常谈了,四个字,多路复用,要不单线程只能停等排队.另外select和poll不如epoll强大好用. 程序结构漫谈 代码很简陋,基本属于玩具.但是还是随便谈谈. 在单线程模型下使用epoll,只能使用一个epoll的instance同时监听socket描述符和connection描述符.当socket描述符就位时,就调用accept处理三次握手建立连接,同时将调用epoll_ctl将这个connfd加入epoll的事件监听表中.如果connfd就位,就调用recv

一个简单的时间获取服务器程序

程序执行流程: 1.创建TCP套接字 listenfd = Socket(AF_INET, SOCK_STREAM, 0) 2.清空sockaddr_in  servaddr结构体 bzero(&servaddr, sizeof(servaddr)) 3.填写网际套接字地址结构 我们指定IP地址为INADDR_ANY,这样要是服务器主机有多个网络接口,服务器进程就可以在任意网络接口上接受客户连接 servaddr.sin_family      = AF_INET; servaddr.sin_a

UNIX网络编程1.5一个简单的时间获取服务器程序1.6客户服务器程序索引表

#include "../lib/unpsunyj.h" #include <time.h> int main(int argc, char ** argv) { int listenfd; int connfd; sockaddr_in servaddr; char buff[MAXLINE]; time_t ticks; // TCP套接字的创建 // listenfd = Socket(AF_INET, SOCK_STREAM, 0); if ((listenfd =

一个简单的判断远端服务器端口是否通的Python脚本

import socket  #导入socket模块 sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.settimeout(1) try:     sk.connect(('192.168.0.1',21))    #连接21号端口,并作出判断     print 'Server port 21 OK!' except Exception:     print 'Server port 21 not connect!' sk.c

typescript-koa-postgresql 实现一个简单的rest风格服务器 —— typescript 开发环境配置

最近需要用 nodeJS 写一个后台程序,为了能够获得 IDE 的更多代码提示,决定用 typescript 来编写,随便也学习下 ts,在这记录下实现过程. 1.新建文件夹 typescript-koa-postgresql,初始化项目 yarn init -y 2.安装 typescript yarn add typescript @types/node --dev 3.配置  typescript 编译环境,在项目根目录下新建文件 tsconfig.json 1 { 2 "compiler

typescript-koa-postgresql 实现一个简单的rest风格服务器 —— 连接 postgresql 数据库

接上一篇,这里使用 sequelize 来连接 postgresql 数据库 1.安装 sequelize,数据库驱动 pg yarn add sequelize sequelize-typescript pg reflect-metadata 2.新建配置文件夹 conf 及 配置文件 db.conf.ts /** * @name: 数据库配置 * @param : undefined * @return : undefined */ export const dbConfig = { hos

Windows 上静态编译 Libevent 2.0.10 并实现一个简单 HTTP 服务器(无数截图)

[文章作者:张宴 本文版本:v1.0 最后修改:2011.03.30 转载请注明原文链接:http://blog.s135.com/libevent_windows/] 本文介绍了如何在 Windows 操作系统中,利用微软 Visual Studio 2005 编译生成 Libevent 2.0.10 静态链接库,并利用 Libevent 静态链接库,实现一个简单的 HTTP Web服务器程序:httpd.exe. 假设 Visual Studio 2005 的安装路径为“D:\Program

Windows 上静态编译 Libevent 2.0.10 并实现一个简单 HTTP 服务器(图文并茂,还有实例下载)

[文章作者:张宴 本文版本:v1.0 最后修改:2011.03.30 转载请注明原文链接:http://blog.s135.com/libevent_windows/] 本文介绍了如何在 Windows 操作系统中,利用微软 Visual Studio 2005 编译生成 Libevent 2.0.10 静态链接库,并利用 Libevent 静态链接库,实现一个简单的 HTTP Web服务器程序:httpd.exe. 假设 Visual Studio 2005 的安装路径为“D:\Program