GAE1.4尝新

GAE1.4尝鲜
GAE1.4发布了,带来了很多新的功能。不过我最在意的有两个:一是支持代码下载;二是支持及时通讯(channel api),现在我们来玩玩这两个东西。

1、代码下载

我们一般通过appcfg.py来上传、更新代码,但是没有下载的功能。如果换了一台机器,又看到不代码,真的很窝火。不过,现在GAE提供了,真的很方便,赞一个GAE。下载代码很简单,看一个简单的例子:

Usage: appcfg.py [options] download_app -A app_id [ -V version ] <out-dir>

C:\Users\Administrator\Desktop>appcfg.py download_app -A flyingzl -V 2 flyingzl-project
D:\program\python\GAE\appcfg.py:42: DeprecationWarning: the sha module is deprec
ated; use the hashlib module instead
  os.path.join(DIR_PATH, 'lib', 'django'),
D:\program\python\GAE\google\appengine\tools\dev_appserver_login.py:33: Deprecat
ionWarning: the md5 module is deprecated; use hashlib instead
  import md5
Server: appengine.google.com.
Fetching file list...
Fetching files...
[1/23] chat.py
[2/23] send_mail.py
[3/23] hello_template.html
[4/23] hello_user.py
...


2、即时通讯支持(channel API)

即时通信,即我们常说的comet,用于模拟和远程服务端长连接,常见的技术有轮询(poll)、推送(push)、websocket(html5支持,不过现在大部分服务器还不支持,GAE以后也会支持这个!)。关于后台的实现,各种各样,开源的技术也不少,我们看看google的实现
GAE1.4尝新

从图可以看出,在我们和GAE进行通信时,中间其实还有一个基于XMLPP的google talk,我们每次通信首先走的是google talk然后google talk再和gae进行通信。。。在之后的列子中,通过firebug的console我们可以看到加载google talk脚本的过程。

接着看图:

GAE1.4尝新

可以看到,在browser端,我们是和在一个看不到的iframe在通信,我们把数据传给这个隐藏的iframe,然后iframe再和google talk通信,然后再和GAE通信,貌似有点小复杂。不过没有关系,这些都是对用户透明的。说了这么多废话,直接看api,哈哈

GAE1.4尝新

如果大家熟悉html5的websocket,可以看到channel api的实现完全和websocket一致。

GAE1.4尝新

很简单吧,现在我们做一个样例,就是做一个非常简单的聊天室。有兴趣的童鞋可以好好改造下,界面如下:

GAE1.4尝新

3、代码实现聊天室

首先创建服务端
#coding=utf-8
'''
Created on 2010-12-4
@author: flyingzl
'''
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util,template
from google.appengine.api import channel
from hashlib import md5
import time

class Chat(webapp.RequestHandler):
    
    def get(self):
        self.response.headers['Content-Type']='text/html;charset=utf-8'
        key=md5('1234567890').hexdigest()
        client_id=channel.create_channel(key)
        self.response.out.write(template.render("templates/chat.html", {'channel_id':client_id}))
        
        
class ChatSender(webapp.RequestHandler):
    def get(self):
        self.response.headers['Content-Type']='text/html;charset=utf-8'
        self.error(500)
        self.response.out.write('只支持Post请求')
    
    def post(self):
        key=md5('1234567890').hexdigest()
        message=self.request.get('message','')
        now=time.strftime("%Y-%m-%d %H:%M:%S")
        ip=self.request.environ['REMOTE_ADDR']
        try:
            channel.send_message(key, "%s(%s)-->%s"%(ip,now,message))
        except channel.InvalidChannelClientIdError:
            self.error(500)
            self.response.out.write("Channel标识符不合法")
        except channel.InvalidMessageError:
            self.error(500)
            self.response.out.write("发送的消息太长,最大长度不能超过")+channel.MAXIMUM_MESSAGE_LENGTH+"个字节"
        
            
        
def main():
    app=webapp.WSGIApplication([('/chat',Chat),('/chat/sender',ChatSender)],debug=True);
    util.run_wsgi_app(app)

if __name__=='__main__':
    main()


代码比较简单,主要有两个过程,一个是创建channel,另外一个是通过channel发送消息。

再看看客户端:

<!doctype>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></meta>
		<title>Chat</title>
		<style type="text/css">
			#mainDiv{
				width:600px;
				height:400px;
				padding:5px;
				margin:10px auto;
				border:1px solid lightblue;
			}
			
			#message{
				width:100%;
				height:370px;
				margin-bottom:5px;
			}
			
			#userMessage{
				width:450px;
				margin-right:2px;
			}
		</style>
	</head>
	<body>
		<div id="mainDiv">
			<textarea id='message'></textarea>
			<input id="userMessage"  /><button id='btn' disabled onClick="sendMessage()">正在连接服务器……</button>
		</div>
		<script type="text/javascript" src="/_ah/channel/jsapi"></script>
		<script type="text/javascript" src="/js/jquery-1.4.2.min.js"></script>
		<script>
			var channel_id='{{channel_id}}',
				timeoutID='';
			$(function(){
				var channel=new goog.appengine.Channel(channel_id),
					socket=channel.open();
				socket.onopen=function(){
					$('#btn').html("发送").removeAttr('disabled');
				}
				
				socket.onmessage=function(data){
					var message=$("#message").val();
					$.trim(message)?$("#message").val(message+"\n"+data.data):$("#message").val(data.data);
				}
				
			});
			
			function sendMessage(message){
				if(timeoutID){
					alert("您发送的太快了,休息一下下……");
					return;
				}
				var message=$('#userMessage').val();
				if(!$.trim(message)){
					alert("请输入要发送的消息!");
					$('#userMessage').focus();
					return;
				}
				$.ajax({
					type:'POST',
					data:"message="+message,
					url:'/chat/sender',
					error:function(err){
						alert(err.responseText);
					}
				});
				$('#userMessage').val('')
				timeoutID=setTimeout(function(){
					timeoutID='';
				},500);
			}
			
		</script>
	</body>
</html>


创建channel的过程是不是和websocket很像?我们只需要监听onopen、onmessage就可以获得服务器传来的消息;不过我有点没搞明白,为什么不可以像websocket那样通过socket.send方法发送数到远程呢?或许本身gae就支持的不好。

到此,一个简单的聊天室就做好了,几行代码,就完成了一个我们之前要花费很大气力才能做完的,GAE真的是太赞了,继续关注!

代码本身很简单,我就不上传代码了。大家看明白就可以咯!祝大家周末愉快~~
1 楼 sumaolin 2010-12-08  
呵呵……
那以后在浏览器端就可以直接聊天了,不用QQ什么的客户端啦!
开源才是走向统一
2 楼 dengzhangtao 2010-12-09  
那java怎么弄?
3 楼 fm_974 2010-12-09  
一定要裝Google Talk客戶端?
4 楼 qiaoqinqie 2010-12-09  
GAE 被封锁了?
5 楼 maltose 2010-12-10  
已经被封了