2008 年的夏天,偶然在网上闲逛的时候发现了 Comet 技术,人云亦云间,姑且认为它是由 Dojo 的 Alex Russell 在 2006 年提出。在阅读了大量的资料后,萌发出写篇 blog 来说明什么是 Comet 的想法。哪知道这个想法到了半年后的今天才提笔,除了繁忙的工作拖延外,还有 Comet 本身带来的困惑。

Comet 能带来生产力的提升是有目共睹的。现在假设有 1000 个用户在使用某软件,轮询 (polling) 和 Comet 的设定都是 1s 、 10s 、 100s 的潜伏期,那么在相同的潜伏期内, Comet 所需要的带宽更小,如下图:

不仅仅是在带宽上的优势,每个用户所真正感受到的响应时间(潜伏期)更短,给人的感觉也就更加的实时,如下图:

再引用一篇 IBMDW 上的译文《使用 Jetty 和 Direct Web Remoting 编写可扩展的 Comet 应用程序》,其中说到:吸引人们使用 Comet 策略的其中一个优点是其显而易见的高效性。客户机不会像使用轮询方法那样生成烦人的通信量,并且事件发生后可立即发布给客户机。

上面一遍一遍的说到 Comet 技术的优势,那么我们可以替换现有的技术结构了?不幸的是,近半年的擦边球式的关注使我对 Comet 的理解越发的糊涂,甚至有人说 Comet 这个名词已被滥用。去年的一篇博文,《 The definition of Comet? 》使 Comet 更加扑朔迷离,甚至在维基百科上大家也对准确的 Comet 定义产生争论。还是等牛人们争论清楚再修改维基百科吧,在这里我想还是引用维基百科对 Comet 的定义:服务器推模式 (HTTP server push 、 streaming) 以及长轮询 (long polling) ,这两种模式都是 Comet 的实现。

除了对 Comet 的准确定义尚缺乏有效的定论外, Comet 还存在不少技术难题,随着 Tomcat 6 、 Jetty 6 的发布,他们基于 NIO 各自实现了异步 Servlet 机制。有兴趣的看官可以分别实现这两个容器的 Comet ,至少我还没玩转。

在编写服务器端的代码上面,我很困惑, http://tomcat.apache.org/tomcat-6.0-doc/aio.html 这里演示了如何在 Tomcat 6 中实现异步 Servlet ;我们再把目光换到 Jetty 6 上,还是前面提到的那篇 IBMDW 译文,如果你和我一样无聊,可以下载那边文章的 sample 代码。我惊奇的发现每个厂商对异步 Servlet 的封装是不同的,一个傻傻的问题:我的 Comet 服务器端的代码可移植么?至今我还在问这个问题!好吧,业界有规范么?有当然有,不过看起来有些争论会发生——那就是 Servlet 3.0 规范 (JSR-315) , Servlet 3.0 正在公开预览,它明确的支持了异步 Servlet ,《 Servlet 3.0 公开预览版引发争论》,又让我高兴不起来了:“来自 RedHat 的 Bill Burke 写的一篇博文,其中他批评了 Jetty 6 中的异步 servlet 实现 ......Greg Wilkins 宣布他致力于 Servlet 3.0 异步 servlet 的一个实现 ...... 虽然还需要更多测试,但是这个代码已经实现了基本的异步行为,不需要很复杂的重新分发请求或者前递方法。我相信这代表了 3.0 的合理折中方案。在我们从 3.0 的简单子集里获得经验之后,如果需要更多的特性,可以添加到 3.1 中 ........” 。牛人们还在做最佳范例,口水仗也还要继续打,看来要尝到 Comet 的甜头是很困难的。 STOP !我已经不想再分析如何写客户端的代码了,什么 dojo 、 extJs 、 DWR 、 ZK....... 都有自己的实现。我认为这一切都要等 Servelt 3.0 正式发布以后,如何编写客户端代码才能明朗点。

现在抛开绕来绕去的争执吧,既然 Ajax+Servlet 实现 Comet 很困难,何不换个思维呢。我这里倒是有个小小的 sample ,说明如何在 Adobe BlazeDS 中实现长轮询模式。关于 BlazeDS ,可以在这里找到些信息。为了说明什么是长轮询,首先来看看什么是轮询,既在一定间隔期内由 web 客户端发起请求到服务器端取回数据,如下图所示:

至于轮询的缺点,在前面的论述中已有覆盖,至于优点大家可以 google 一把,我觉得最大的优点就是技术上很好实现,下面是个 Ajax 轮询的例子,这是一个简单的聊天室,首先是 chat.html 代码,想必这些代码网上一抓就一大把,支持至少 IE6 、 IE7 、 FF3 浏览器,让人烦心的是乱码问题,在传递到 Servlet 之前要 encodeURI 一下 :

<!
DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"
>

<!--

chat page
    author rosen jiang
    since 2008/07/29
-->

<
html
>

<
head
>

<
meta 
http-equiv
="content-type"
 content
="text/html; charset=utf-8"
>

<
script 
type
="text/javascript"
>

//
servlets url

var
 url 
=
 
"
http://127.0.0.1:8080/ajaxTest/Ajax
"
;
    
//
bs version

var
 version 
=
 navigator.appName
+
"
 
"
+
navigator.appVersion;
    
//
if is IE

var
 isIE 
=
 
false
;

if
(version.indexOf(
"
MSIE 6
"
)
>
0
 
||
 version.indexOf(
"
MSIE 7
"
)
>
0
){
        isIE 
=
 
true
;
    }

//
Httprequest object

var
 Httprequest 
=
 
function
() {}
    
//
creatHttprequest function of Httprequest

Httprequest.prototype.creatHttprequest
=
function
(){
        
var
 request 
=
 
false
;
        
//
init XMLHTTP or XMLHttpRequest

if
 (isIE) {
            
try
 {
                request 
=
 
new
 ActiveXObject(
"
Msxml2.XMLHTTP
"
);
            } 
catch
 (e) {
                
try
 {
                    request 
=
 
new
 ActiveXObject(
"
Microsoft.XMLHTTP
"
);
                } 
catch
 (e) {}
            }
        }
else
 { 
//
Mozilla bs etc.

request 
=
 
new
 XMLHttpRequest();
        }
        
if
 (
!
request) {
            
return
 
false
;
        }
        
return
 request;
    }
    
//
sendMsg function of Httprequest

Httprequest.prototype.sendMsg
=
function
(msg){
        
var
 http_request 
=
    
this
.creatHttprequest();
        
var
 reslult 
=
 
""
;
        
var
 methed 
=
 
false
;
        
if
 (http_request) {    
            
if
 (isIE) {                
                http_request.onreadystatechange 
=

function
 (){
//
callBack function

if
 (http_request.readyState 
==
 
4
) {
                                
if
 (http_request.status 
==
 
200
) {
                                    reslult 
=
 http_request.responseText;
                                } 
else
 {
                                    alert(
"
您所请求的页面有异常。
"
);
                                }
                            }
                        };
            } 
else
 {
                http_request.onload 
=
 
                        
function
 (){
//
 callBack function of Mozilla bs etc.

if
 (http_request.readyState 
==
 
4
) {
                                
if
 (http_request.status 
==
 
200
) {
                                    reslult 
=
 http_request.responseText;
                                } 
else
 {
                                    alert(
"
您所请求的页面有异常。
"
);
                                }
                            }
                        };
            }
            
//
send msg

if
(msg
!=
null
 
&&
 msg
!=
""
){
                request_url 
=
 url
+
"
?
"
+
Math.random()
+
"
&msg=
"
+
msg;
                
//
encodeing utf-8 Character

request_url 
=
 encodeURI(request_url);
                http_request.open(
"
GET
"
, request_url, 
false
);
            }
else
{
                http_request.open(
"
GET
"
, url
+
"
?
"
+
Math.random(), 
false
);
            }
            http_request.setRequestHeader(
"
Content-type
"
,
"
charset=utf-8;
"
);
            http_request.send(
null
);
        }
        
return
 reslult;    
    }
</
script
>

</
head
>

<
body
>

<
div
>

<
input 
type
="text"
 id
="sendMsg"
></
input
>

<
input 
type
="button"
 value
="发送消息"
 onclick
="send()"
/>

<
br
/><
br
/>

<
div 
style
="width:470px;overflow:auto;height:413px;border-style:solid;border-width:1px;font-size:12pt;"
>

<
div 
id
="msg_content"
></
div
>

<
div 
id
="msg_end"
 style
="height:0px; overflow:hidden"
>
 
</
div
>

</
div
>

</
div
>

</
body
>

<
script 
type
="text/javascript"
>

var
 data_comp 
=
 
""
;
    
//
send button click

function
 send(){
        
var
 sendMsg 
=
 document.getElementById(
"
sendMsg
"
);
        
var
 hq 
=
 
new
 Httprequest();
        hq.sendMsg(sendMsg.value);
        sendMsg.value
=
""
;
    }
    
//
processing wnen message recevied

function
 writeData(){
        
var
 msg_content 
=
 document.getElementById(
"
msg_content
"
);
        
var
 msg_end 
=
 document.getElementById(
"
msg_end
"
);
        
var
 hq 
=
 
new
 Httprequest();
        
var
 value 
=
 hq.sendMsg();
        
if
(data_comp 
!=
 value){
            data_comp 
=
 value;
            msg_content.innerHTML 
=
 value;
            msg_end.scrollIntoView();
        }
        setTimeout(
"
writeData()
"
, 
1000
);
    }
    
//
init load writeData

onload 
=
 writeData;
</
script
>

</
html
>

接下来是
Servlet
,如果你是用的
Tomcat
,在这里注意下编码问题,否则又是乱码,另外我使用
LinkedList
实现了一个队列,该队列的最大长度是
30
,也就是最多能保存
30
条聊天信息,旧的将被丢弃,另外新的客户端进来后能读取到最近的信息:

package
 org.rosenjiang.ajax;

import
 java.io.IOException;
import
 java.io.PrintWriter;
import
 java.text.SimpleDateFormat;
import
 java.util.Date;
import
 java.util.LinkedList;

import
 javax.servlet.ServletException;
import
 javax.servlet.http.HttpServlet;
import
 javax.servlet.http.HttpServletRequest;
import
 javax.servlet.http.HttpServletResponse;

/**

* 
 * 
@author
 rosen jiang
 * 
@since
 2009/02/06
 * 
 
*/

public
 
class
 Ajax 
extends
 HttpServlet {
    
private
 
static
 
final
 
long
 serialVersionUID 
=
 
1L
;
    
//
 the length of queue

private
 
static
 
final
 
int
 QUEUE_LENGTH 
=

;
    
//
 queue body

private
 
static
 LinkedList
<
String
>
 queue 
=
 
new
 LinkedList
<
String
>
();
    
    
/**

* response chat content
     * 
     * 
@param
 request
     * 
@param
 response
     * 
@throws
 ServletException
     * 
@throws
 IOException
     
*/

public
 
void
 doGet(HttpServletRequest request, HttpServletResponse response)
            
throws
 ServletException, IOException {
        
//
parse msg content

String msg 
=
 request.getParameter(
"
msg
"
);
        SimpleDateFormat sdf 
=
 
new
 SimpleDateFormat(
"
yyyy-MM-dd HH:mm:ss
"
);
        
//
push to the queue

if
 (msg 
!=
 
null
 
&&
 
!
msg.equals(
""
)) {
            
byte
[] b 
=
 msg.getBytes(
"
ISO_8859_1
"
);
            msg 
=
 sdf.format(
new
 Date()) 
+
"
  
"
+
new
 String(b, 
"
utf-8
"
)
+
"
<br>
"
;
            
if
(queue.size() 
==
 QUEUE_LENGTH){
                queue.removeFirst();
            }
            queue.addLast(msg);
        }
        
//
response client

response.setContentType(
"
text/html
"
);
        response.setCharacterEncoding(
"
utf-8
"
);
        PrintWriter out 
=
 response.getWriter();
        msg 
=
 
""
;
        
//
loop queue

for
(
int
 i
=

; i
<
queue.size(); i
++
){
            msg 
=
 queue.get(i);
            out.println(msg
==
null
 
?
 
""
 : msg);
        }
        out.flush();
        out.close();
    }

/**

* The doPost method of the servlet.
     *
     * 
@param
 request
     * 
@param
 response
     * 
@throws
 ServletException
     * 
@throws
 IOException
     
*/

public
 
void
 doPost(HttpServletRequest request, HttpServletResponse response)
            
throws
 ServletException, IOException {
        
this
.doGet(request, response);
    }
}

打开浏览器,实验下效果,将就用吧,稍微有些延迟。还是看看长轮询吧,长轮询有三个显著的特征:

1.

服务器端会阻塞请求直到有数据传递或超时才返回。

2.

客户端响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。

3.

当客户端处理接收的数据、重新建立连接时,服务器端可能有新的数据到达;这些信息会被服务器端保存直到客户端重新建立连接,客户端会一次把当前服务器端所有的信息取回。

下图很好的说明了以上特征:

既然关注的是
BlazeDS
如何实现长轮询,那么有必要稍微了解下。
BlazeDS
包含了两个重要的服务,进行远端方法调用的
RPC
service
和传递异步消息的
Messaging
Service
,我们即将探讨的长轮询属于
Messaging
Service
。
Messaging
Service
使用
producer
consumer
模式来分别定义消息的发送者
(producer)
和消费者
(consumer)
,具体到
Flex
代码,有
Producer
和
Consumer
两个组件对应。在广阔的互联网上有很多
BlazeDS
入门的中文教材,我就不再废话了。假设你已经装好
BlazeDS
,打开
WEB-INF/flex/services-config.xml
文件,在
channels
节点内加一个
channel
声明长轮询频道,关于
channel
和
endpoint
请参阅

About
channels and endpoints

章节:

<
channel-definition 
id
="long-polling-amf"
 class
="mx.messaging.channels.AMFChannel"
>

<
endpoint 
url
="http://{server.name}:{server.port}/{context.root}/messagebroker/longamfpolling"
 class
="flex.messaging.endpoints.AMFEndpoint"
/>

<
properties
>

<
polling-enabled
>
true
</
polling-enabled
>

<
wait-interval-millis
>

</
wait-interval-millis
>

<
polling-interval-millis
>

</
polling-interval-millis
>

<
max-waiting-poll-requests
>

</
max-waiting-poll-requests
>

</
properties
>

</
channel-definition
>

如何实现长轮询的玄机就在上面的
properties
节点内,

polling-enabled =
true
,打开轮询模式;
wait-interval-millis
=
6000
服务器端的潜伏期,也就是服务器会保持与客户端的连接,直到超时或有新消息返回(恩,看来这就是长轮询了);
polling-interval-millis
= 0
表示客户端请求服务器端的间隔期,
0
表示没有任何的延迟;
max-waiting-poll-requests
=
150
表示服务器能承受的最大长连接用户数,超过这个限制,新的客户端就会转变为普通的轮询方式(至于这个数值最大能有多大,这和你的
web
服务器设置有关了,而
web
服务器的最大连接数就和操作系统有关了,这方面的话题不在本文内探讨)。

其实这样设置之后,长轮询的代码已经实现了一半了。恩,不错!看起来比异步
Servlet
实现起来简单多了。不过要实现和之前
Ajax
轮询一样的效果,还得实现自己的
ServiceAdapter
,这就是
Adapter
的用处:

package
 org.rosenjiang.flex;

import
 java.text.SimpleDateFormat;
import
 java.util.Date;
import
 java.util.LinkedList;

import
 flex.messaging.io.amf.ASObject;
import
 flex.messaging.messages.Message;
import
 flex.messaging.services.MessageService;
import
 flex.messaging.services.ServiceAdapter;

/**

* 
 * 
@author
 rosen jiang
 * 
@since
 2009/02/06
 * 
 
*/

public
 
class
 MyMessageAdapter 
extends
 ServiceAdapter {

//
 the length of queue

private
 
static
 
final
 
int
 QUEUE_LENGTH 
=

;
    
//
 queue body

private
 
static
 LinkedList
<
String
>
 queue 
=
 
new
 LinkedList
<
String
>
();

/**

* invoke method
     * 
     * 
@param
 message Message
     * 
@return
 Object
     
*/

public
 Object invoke(Message message) {
        SimpleDateFormat sdf 
=
 
new
 SimpleDateFormat(
"
yyyy-MM-dd HH:mm:ss
"
);
        MessageService msgService 
=
 (MessageService) getDestination()
            .getService();
        
//
message Object

ASObject ao 
=
 (ASObject) message.getBody();
        
//
chat message

String msg 
=
 (String) ao.get(
"
chatMessage
"
);
        
if
 (msg 
!=
 
null
 
&&
 
!
msg.equals(
""
)) {
            msg 
=
 sdf.format(
new
 Date()) 
+
 
"
  
"
 
+
 msg 
+
 
"
\r
"
;
            
if
(queue.size() 
==
 QUEUE_LENGTH){
                queue.removeFirst();
            }
            queue.addLast(msg);
        }
        msg 
=
 
""
;
        
//
loop queue

for
(
int
 i
=

; i
<
queue.size(); i
++
){
            String chatData 
=
 queue.get(i);
            
if
 (chatData 
!=
 
null
) {
                msg 
+=
 chatData;
            }
        }
        ao.put(
"
chatMessage
"
, msg);
        message.setBody(ao);
        msgService.pushMessageToClients(message, 
false
);
        
return
 
null
;
    }
}

接下来注册该
Adapter
,打开
WEB-INF/flex/messaging-config.xml
文件,在
adapters
节点内加入一个
adapter-definition
来声明自定义
Adapter
:

<
adapter-definition 
id
="myad"
 class
="org.rosenjiang.flex.MyMessageAdapter"
/>

接着定义一个
destination
,以便
Flex
客户端能订阅聊天室,组装好之前定义的长轮询频道和
adapter
:

<
destination 
id
="chat"
>

<
channels
>

<
channel 
ref
="long-polling-amf"
/>

</
channels
>

<
adapter 
ref
="myad"
/>

</
destination
>

服务器端就算搞定了,接着搞定
Flex
那边的代码吧,灰常灰常的简单。先到

Building
your client-side application

学习如何创建和
BlazeDS
通讯的
Flex
项目。然后在
chat.mxml
中写下:

<?
xml version="1.0" encoding="utf-8"
?>

<
mx:Application 
xmlns:mx
="http://www.adobe.com/2006/mxml"
 creationComplete
="consumer.subscribe();send()"
>

<
mx:Script
>

<![CDATA[

import mx.messaging.messages.AsyncMessage;
            import mx.messaging.messages.IMessage;
            
            private function send():void
            {
                var message:IMessage = new AsyncMessage();
                message.body.chatMessage = msg.text;
                producer.send(message);
                msg.text = "";
            }
                        
            private function messageHandler(message:IMessage):void
            {
                log.text = message.body.chatMessage + "\n";
            }
            
        
]]>

</
mx:Script
>

<
mx:Producer 
id
="producer"
 destination
="chat"
/>

<
mx:Consumer 
id
="consumer"
 destination
="chat"
 message
="messageHandler(event.message)"
/>

<
mx:Panel 
title
="Chat"
 width
="100%"
 height
="100%"
>

<
mx:TextArea 
id
="log"
 width
="100%"
 height
="100%"
/>

<
mx:ControlBar
>

<
mx:TextInput 
id
="msg"
 width
="100%"
 enter
="send()"
/>

<
mx:Button 
label
="Send"
 click
="send()"
/>
 
        
</
mx:ControlBar
>

</
mx:Panel
>

</
mx:Application
>

之前我们说到的
Producer
和
Consumer
组件在这里出现了,由于我们要订阅的是同一个聊天室,所以
destination="chat"
,而
Consumer
组件则注册回调函数
messageHandler()
,处理异步消息的到来。当打开这个聊天客户端的时候,在
creationComplete
初始化完成后,立即进行
consumer.subscribe()
,其实接下来应该就能直接收到服务器端回馈的聊天记录了,但是我没仔细学习如何监听客户端的订阅,所以在这里我直接
send()
了一个空消息以便服务器端能回馈已有的聊天记录,接下来我就不用再讲解了,都能看懂。

现在打开浏览器,感受下长轮询的效果吧。不过遇到个问题,如果
FF
同时开两个聊天窗口,第二个打开的会有延迟感,
IE
也是,按照牛人们的说法,当一个浏览器开两个以上长连接的时候才会有延迟感,不解。
BlazeDS
的长轮询也不是十全十美,有人说它不是真正的“实时”

The
Truth About BlazeDS and Push
Messaging

,随即引发出口水仗,里面提到的
RTMP
协议在
2009
年
1
月已开源,相信以后
BlazeDS
会更“实时”;接着又有人说
BlazeDS
不是非阻塞式的,这个问题后来也没人来对应。罢了,毕竟BlazeDS才开源不久,容忍一下吧。最后,我想说的是,不论
BlazeDS
到底有什么问题,至少实现起来是轻松的,在
Servlet
3.0
没发布之前,是个不错的选择。


请注意!引用、转贴本文应注明原作者:Rosen Jiang 以及出处:

http://www.blogjava.net/rosen

Ajax轮询以及Comet模式—写在Servlet 3.0发布之前(转)的更多相关文章

  1. COMET探索系列二【Ajax轮询复用模型】

    写在前面:Ajax轮询相信大家都信手拈来在用,可是有这么一个问题,如果一个网站中同时有好多个地方需要用到这种轮询呢?就拿我们网站来说,有一个未读消息数提醒.还有一个时实时加载最新说说.昨天又加了一个全 ...

  2. Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE

    1. 前言 Web端即时通讯技术因受限于浏览器的设计限制,一直以来实现起来并不容易,主流的Web端即时通讯方案大致有4种:传统Ajax短轮询.Comet技术.WebSocket技术.SSE(Serve ...

  3. 【Javascript】解决Ajax轮询造成的线程阻塞问题(过渡方案)

    一.背景 开发Web平台时,经常会需要定时向服务器轮询获取数据状态,并且通常不仅只开一个轮询,而是根据业务需要会产生数个轮询.这种情况下,性能低下的Ajax长轮询已经不能满足需求,频繁的访问还会造成线 ...

  4. 闲话ajax,例ajax轮询,ajax上传文件[开发篇]

    引语:ajax这门技术早已见怪不怪了,我本人也只是就自己真实的经验去总结一些不足道的话.供不是特别了解的朋友参考参考! 本来早就想写一篇关于ajax的文章的,但是前段时间一直很忙,就搁置了,趁着元旦放 ...

  5. WebSocket和long poll、ajax轮询的区别,ws协议测试

    WebSocket和long poll.ajax轮询的区别,ws协议测试 WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连 ...

  6. AJAX轮询的实时监控画面

    上一篇我们通过异步刷新Ajax 技术,来实现监控页面监控位号值的刷新,采用Ajax (Asynchronous Javascript And XML)技术,是指一种创建交互式.快速动态网页应用的网页开 ...

  7. 浅谈Websocket、Ajax轮询和长连接(long pull)

    最近看到了一些介绍Websocket的文章,觉得挺有用,所以在这里将自己的对其三者的理解记录一下. 1.什么是Websocket Websocket是HTML5中提出的新的协议,注意,这里是协议,可以 ...

  8. WebSocket原理及与http1.0/1.1 long poll和 ajax轮询的区别【转自知乎】

    一.WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算)首先HTTP有1.1和1.0之说,也就是所谓的ke ...

  9. ajax轮询实时获取数据

    最近做一个评论功能时,想要实现实时异步刷新评论功能,于是使用了ajax轮询,这里简单记录一下ajax轮询的原理及使用方法. ajax轮询的原理就是客户端定时向服务端发送ajax请求,服务器接到请求后马 ...

随机推荐

  1. VS2010/MFC编程入门之四(MFC应用程序框架分析)

    VS2010/MFC编程入门之四(MFC应用程序框架分析)-软件开发-鸡啄米 http://www.jizhuomi.com/software/145.html   上一讲鸡啄米讲的是VS2010应用 ...

  2. Intellij Idea @Autowired取消提示

    在使用mybatis的代理进行开发时 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> ...

  3. 浅谈Linux下的五种I/O模型

     一.关于I/O模型的引出 我们都知道,为了OS的安全性等的考虑,进程是无法直接操作I/O设备的,其必须通过系统调用请求内核来协助完成I/O动作,而内核会为每个I/O设备维护一个buffer.如下图所 ...

  4. [2014-03-13 08:46:42 - Dex Loader] Unable to execute dex: java.nio.BufferOverflowException. Check the Eclipse log for stack trace.

    Unable to execute dex: java.nio.BufferOverflowException. Check the Eclipse log for stack trace. 问题提示 ...

  5. IP地址的定义和含义

    IP的定义 ip 是32位无符号整数,最小,最大分别是- 0.0.0.0 - 255.255.255.255 具体来说,由一个ip由 Net-ID+Host-ID 两部分组成,Net-ID 相同,那么 ...

  6. C# List.sort排序详解(多权重,升序降序)

    很多人可能喜欢Linq的orderBy排序,可惜U3D里面linq在Ios上会报错,所以就必须使用list的排序. 其实理解了并不难 升序降序比较 sort有三种结果 1,-1,0分别是大,小,相等. ...

  7. WIN764位主机的虚拟机安装的xp系统串口添加

    WIN764位主机的虚拟机安装的xp系统串口添加 我的电脑安装的是64位的WIN7系统,今天为了验证一个问题,需要用到6410开发板,但在安装USB驱动时无法成功安装,估计是S3C6410的USB驱动 ...

  8. Web Servers in Visual Studio for ASP.NET Web Projects

    https://msdn.microsoft.com/en-us/library/58wxa9w5(v=vs.120).aspx When you develop web projects in Vi ...

  9. [Python]计算豆瓣电影TOP250的平均得分

    用python写的爬虫练习,感觉比golang要好写一点. import re import urllib origin_url = 'https://movie.douban.com/top250? ...

  10. 《OD大数据实战》HDFS入门实例

    一.环境搭建 1.  下载安装配置 <OD大数据实战>Hadoop伪分布式环境搭建 2. Hadoop配置信息 1)${HADOOP_HOME}/libexec:存储hadoop的默认环境 ...