用 twisted 创建 tcp socket 长链接 amf server 服务

# Author:pako
# Email/gtalk:zealzpc@gmail.com
# 文章链接:http://pako.iteye.com/blog/1096501

这是pyamf安装包里的例子,总的来说用pyamf来写server的各种例子还是很全的,包括django,gae,twisted,web2py等等,基本python常用的网络框架都有。现在很多网页游戏都是前端flash,那么和server端通信基本就用 amf了,如果server端的功能只是存储一些统计,或者对数据库的增删改查,那么一般的http短连接服务就能搞定了,以上提到的这些框架也都写起来比较简单,比如可以看django的例子http://www.pyamf.org/tutorials/gateways/django.html,寥寥数行就搞定了,python嘛。

但有时候需求不是这样的需要flash和server保持一个长连接来不断的进行通信改怎么搞呢?此时在pyamf的网站上看到了sockey这一段,正是我们想要的。

先看下server端的代码:server.py

# Copyright (c) The PyAMF Project. 
# See LICENSE.txt for details. 
 
"""
Example socket server using Twisted.

@see: U{Documentation for this example<http://pyamf.org/tutorials/actionscript/socket.html>}

@since: 0.1
""" 
 
 
try: 
    import twisted 
except ImportError: 
    print "This examples requires the Twisted framework. Download it from http://twistedmatrix.com" 
    raise SystemExit 
 
from twisted.internet.protocol import Protocol, Factory 
from twisted.internet import reactor 
 
from datetime import datetime 
import pyamf 
 
 
class TimerProtocol(Protocol): 
    interval = 1.0 # 客户端链接到server后,server往客户端发送时间的间隔 
    encoding = pyamf.AMF3 
    timeout = 20 #客户端链接到server后多少时间不操作就断开链接的timeout 
 
    def __init__(self): 
        self.started = False 
        #设置编码器 
        self.encoder = pyamf.get_encoder(self.encoding)、 
        #设置server端将数据编码成amf后存放的缓存地址 
        self.stream = self.encoder.stream 
 
    def connectionLost(self, reason): 
        Protocol.connectionLost(self, reason) 
        print "locst connection:",reason 
        #客户端没断开一个链接,总连接数-1 
        self.factory.number_of_connections -= 1 
        print "number_of_connections:",self.factory.number_of_connections 
    def connectionMade(self): 
        #如果服务器连接数超过最大连接数,拒绝新链接建立 
        if self.factory.number_of_connections >= self.factory.max_connections: 
            self.transport.write('Too many connections, try again later') 
            self.transport.loseConnection() 
 
            return 
        #总连接数+1 
        self.factory.number_of_connections += 1 
        self.timeout_deferred = reactor.callLater(TimerProtocol.timeout, self.transport.loseConnection) 
 
    def dataReceived(self, data): 
        #去除server收到client数据两端的空格 
        data = data.strip() 
        #如果收到的是'start'命令 
        if data == 'start': 
            # start sending a date object that contains the current time 
            if not self.started: 
                self.start() 
        elif data == 'stop': 
            self.stop() 
        #每次执行完客户端请求后重置timeout,重新开始计算无操作时间。 
        if self.timeout_deferred: 
            self.timeout_deferred.cancel() 
            self.timeout_deferred = reactor.callLater(TimerProtocol.timeout, self.transport.loseConnection) 
 
    def start(self): 
        self.started = True 
        self.sendTime() 
 
    def stop(self): 
        self.started = False 
 
    def sendTime(self): 
        if self.started: 
            #往缓存流里写入信息,用编码器进行amf编码 
            self.encoder.writeElement(datetime.now()) 
            #返回给客户端编码后的信息 
            self.transport.write(self.stream.getvalue()) 
            #重置缓存流 
            self.stream.truncate() 
            #每隔self.interval的时间再发送一次amf信息 
            reactor.callLater(self.interval, self.sendTime) 
 
 
class TimerFactory(Factory): 
    protocol = TimerProtocol 
    #最大链接数 
    max_connections = 1000 
 
    def __init__(self): 
        self.number_of_connections = 0 
 
 
class SocketPolicyProtocol(Protocol): 
    """
    Serves strict policy file for Flash Player >= 9,0,124.
    
    @see: U{http://adobe.com/go/strict_policy_files}
    """ 
    def connectionMade(self): 
        self.buffer = '' 
 
    def dataReceived(self, data): 
        self.buffer += data 
 
        if self.buffer.startswith('<policy-file-request/>'): 
            self.transport.write(self.factory.getPolicyFile(self)) 
            self.transport.loseConnection() 
 
 
class SocketPolicyFactory(Factory): 
    protocol = SocketPolicyProtocol 
 
    def __init__(self, policy_file): 
        """
        @param policy_file: Path to the policy file definition
        """ 
        self.policy_file = policy_file 
 
    def getPolicyFile(self, protocol): 
        return open(self.policy_file, 'rt').read() 
 
#设置域名,端口。 
host = 'localhost' 
appPort = 8000 
policyPort = 843 
policyFile = 'socket-policy.xml' 
 
 
if __name__ == '__main__': 
    from optparse import OptionParser 
    #设置server启动选项 
    parser = OptionParser() 
    parser.add_option("--host", default=host, 
        dest="host", help="host address [default: %default]") 
    parser.add_option("-a", "--app-port", default=appPort, 
        dest="app_port", help="Application port number [default: %default]") 
    parser.add_option("-p", "--policy-port", default=policyPort, 
        dest="policy_port", help="Socket policy port number [default: %default]") 
    parser.add_option("-f", "--policy-file", default=policyFile, 
        dest="policy_file", help="Location of socket policy file [default: %default]") 
    (opt, args) = parser.parse_args() 
 
    print "Running Socket AMF gateway on %s:%s" % (opt.host, opt.app_port) 
    print "Running Policy file server on %s:%s" % (opt.host, opt.policy_port) 
      
    reactor.listenTCP(int(opt.app_port), TimerFactory(), interface=opt.host) 
    reactor.listenTCP(int(opt.policy_port), SocketPolicyFactory(opt.policy_file), 
                      interface=opt.host) 
    reactor.run()  

里面最主要干活的就是TimerProtocol这个类。

 

在例子中还提供了另外一个文件:time.tac。懂twisted的人应该就知道了,这是一份twistd脚本,可以直接通过twisted自带的twistd来执行。里面import了  server.py中已经写好的TimerFactory,然后定义了一个application类,这样就可以通过twistd命令来执行了,twistd是什么可以参考官方文档http://twistedmatrix.com/documents/current/core/howto/application.html 本文就不细说了。

debug模式启动server:twistd -noy timer.tac

daemo模式启动server:twistd -oy timer.tac

你也可以直接启动server.py文件:python server.py、

区别就是第二种直接后台运行了在linux,你看不到报错和调试信息,当然windows上不行。 

# Copyright (c) The PyAMF Project. 
# See LICENSE for details. 
 
import sys, os 
 
sys.path.append(os.getcwd()) 
 
from twisted.application import internet, service 
from server import TimerFactory, SocketPolicyFactory 
from server import appPort, policyPort 
 
 
timer = TimerFactory() 
policy = SocketPolicyFactory('socket-policy.xml') 
 
# this is the important bit 
application = service.Application('pyamf-socket-example') 
 
timerService = internet.TCPServer(appPort, timer) 
socketPolicyService = internet.TCPServer(policyPort, policy) 
 
timerService.setServiceParent(application) 
socketPolicyService.setServiceParent(application)  

最后就是找个客户端来连接测测,例子里也提供了一个写好的flash,你打开通一个目录下的index.html就可以看看效果了。

例子里也给你准备了python的客户端。

# Copyright (c) The PyAMF Project. 
# See LICENSE for details. 
 
"""
Python client for socket example.

@since: 0.5
""" 
 
 
import socket 
import pyamf 
 
from server import appPort, host 
 
 
class AmfSocketClient(object): 
    def __init__(self): 
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
 
    def connect(self, host, port): 
        print "Connecting to socket server on %s:%d" % (host, port) 
        try: 
            self.sock.connect((host, port)) 
            print "Connected to server.\n" 
        except socket.error, e: 
            raise Exception("Can't connect: %s" % e[1]) 
 
    def start(self): 
        msg = '' 
 
        # tell server we started listening 
        print "send request: start" 
        try: 
            self.sock.send('start') 
        except socket.error, e: 
            raise Exception("Can't connect: %s" % e[1]) 
 
        while len(msg) < 1024: 
            # read from server 
            amf = self.sock.recv(1024) 
 
            if amf == '': 
                print "Connection closed." 
 
            msg = msg + amf 
 
            for obj in pyamf.decode(amf): 
                print obj 
 
        return msg 
 
    def stop(self): 
        print "send request: stop" 
        self.sock.send('stop') 
 
 
if __name__ == '__main__': 
    from optparse import OptionParser 
 
    parser = OptionParser() 
    parser.add_option("-p", "--port", default=appPort, 
        dest="port", help="port number [default: %default]") 
    parser.add_option("--host", default=host, 
        dest="host", help="host address [default: %default]") 
    (options, args) = parser.parse_args() 
 
    host = options.host 
    port = int(options.port) 
 
    client = AmfSocketClient() 
    client.connect(host, port) 
 
    try: 
        client.start() 
    except KeyboardInterrupt: 
        client.stop()     

原文地址:http://www.pyamf.org/tutorials/actionscript/socket.html

代码就在pyamf安装包的pyamf / doc / tutorials / examples / actionscript / socket这个目录里。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。