tornado实现高性能无阻塞udp通信(1)——server端实现

来源:互联网 发布:unity3d真实地形制作 编辑:程序博客网 时间:2024/06/05 11:33

    在高性能web架构中,往往需要在多个组件之间进行通信。很多人喜欢用HTTP协议进行通信(tornado官方也建议利用HTTP协议通信,将cpu占用高的计算任务交给后端服务器解决),但是在高并发的情况下TCP协议三次握手以及HTTP协议解析所带来的额外开销都是不容忽视的。其实在内网环境中网络环境相对稳定,所以我们完全可以利用UDP协议进行通信从而节约资源。下面就自己动手在tornado上实现无阻塞UDPSERVER。

    先贴上UDPSERVER的代码:

class UDPServer(object):    def __init__(self, io_loop=None):        self.io_loop = io_loop        self._sockets = {}  # fd -> socket object        self._pending_sockets = []        self._started = False    def add_sockets(self, sockets):        if self.io_loop is None:            self.io_loop = IOLoop.instance()        for sock in sockets:            self._sockets[sock.fileno()] = sock            add_accept_handler(sock, self._on_recive,                               io_loop=self.io_loop)    def bind(self, port, address=None, family=socket.AF_UNSPEC, backlog=25):        sockets = bind_sockets(port, address=address, family=family,                               backlog=backlog)        if self._started:            self.add_sockets(sockets)        else:            self._pending_sockets.extend(sockets)    def start(self, num_processes=1):        assert not self._started        self._started = True        if num_processes != 1:            process.fork_processes(num_processes)        sockets = self._pending_sockets        self._pending_sockets = []        self.add_sockets(sockets)    def stop(self):        for fd, sock in self._sockets.iteritems():            self.io_loop.remove_handler(fd)            sock.close()    def _on_recive(self, data, address):print datadef bind_sockets(port, address=None, family=socket.AF_UNSPEC, backlog=25):    sockets = []    if address == "":        address = None    flags = socket.AI_PASSIVE    if hasattr(socket, "AI_ADDRCONFIG"):        flags |= socket.AI_ADDRCONFIG    for res in set(socket.getaddrinfo(address, port, family, socket.SOCK_DGRAM,                                  0, flags)):        af, socktype, proto, canonname, sockaddr = res        sock = socket.socket(af, socktype, proto)        set_close_exec(sock.fileno())        if os.name != 'nt':            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)        if af == socket.AF_INET6:            if hasattr(socket, "IPPROTO_IPV6"):                sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)        sock.setblocking(0)        sock.bind(sockaddr)        sockets.append(sock)    return socketsif hasattr(socket, 'AF_UNIX'):    def bind_unix_socket(file, mode=0600, backlog=128):        sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)        set_close_exec(sock.fileno())        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)        sock.setblocking(0)        try:            st = os.stat(file)        except OSError, err:            if err.errno != errno.ENOENT:                raise        else:            if stat.S_ISSOCK(st.st_mode):                os.remove(file)            else:                raise ValueError("File %s exists and is not a socket", file)        sock.bind(file)        os.chmod(file, mode)        sock.listen(backlog)        return sockdef add_accept_handler(sock, callback, io_loop=None):    if io_loop is None:        io_loop = IOLoop.instance()    def accept_handler(fd, events):        while True:            try:                data, address = sock.recvfrom(2500)            except socket.error, e:                if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):                    return                raise            callback(data, address)    io_loop.add_handler(sock.fileno(), accept_handler, IOLoop.READ)



代码结构与tornado官方TCPSERVER相似,由bind()方法添加监听地址,调用start()方法时将socket加入到监听队列,add_sockets()函数将生成的socket与相应的回调函数注册到ioloop中。一旦该socket可读之后便会调用该回调函数_on_recive(),在这里为了简单起见仅仅把收到数据输出出来。
下面写一个简单的UDPCLIENT来验证一下我们UDPSERVER


import socketaddress = ('127.0.0.1',8888)s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)msg = 'hehehe's.sendto(msg,address)s.close()

运行一遍看看结果:


左边是server端,可以看到server已经成功收到了client所发送的数据,server也就算是完工了。然而光有接受端还是不足以完成最开始的设想——利用udp实现告诉通信,还需要有一个异步udpclient,所以下一篇将会实现一个基于tornado的异步udpclient。


ps:csdn的编辑器实在不好用,本来想在代码中写注释方便读懂代码的,结果搞不定排版,等什么时候搞定这个编辑器之后再补上吧。

                                             
0 0