<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Marshal&#039;s Blog &#187; java</title>
	<atom:link href="http://marshal.easymorse.com/archives/tag/java/feed" rel="self" type="application/rss+xml" />
	<link>http://marshal.easymorse.com</link>
	<description>It&#039;s swap of marshal&#039;s memory.</description>
	<lastBuildDate>Mon, 30 Jan 2012 07:03:45 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>CocoaAsyncSocket学习</title>
		<link>http://marshal.easymorse.com/archives/4533?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=cocoaasyncsocket%25e5%25ad%25a6%25e4%25b9%25a0</link>
		<comments>http://marshal.easymorse.com/archives/4533#comments</comments>
		<pubDate>Wed, 20 Jul 2011 08:25:27 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[cocoaasyncsocket]]></category>
		<category><![CDATA[ios socket]]></category>
		<category><![CDATA[ios socket multicast]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[java socket]]></category>
		<category><![CDATA[java socket multicast]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/4533</guid>
		<description><![CDATA[以下内容翻译自： http://code.google.com/p/cocoaasyncsocket/ CocoaAsyncSocket支持tcp和udp。其中： AsyncSocket类是支持TCP的 AsyncUdpSocket是支持UDP的 AsyncSocket是封装了CFSocket和CFSteam的TCP/IP socket网络库。它提供了异步操作，本地cocoa类的基于delegate的完整支持。主要有以下特性： 队列的非阻塞的读和写，而且可选超时。你可以调用它读取和写入，它会当完成后告知你 自动的socket接收。如果你调用它接收连接，它将为每个连接启动新的实例，当然，也可以立即关闭这些连接 委托（delegate）支持。错误、连接、接收、完整的读取、完整的写入、进度以及断开连接，都可以通过委托模式调用 基于run loop的，而不是线程的。虽然可以在主线程或者工作线程中使用它，但你不需要这样做。它异步的调用委托方法，使用NSRunLoop。委托方法包括socket的参数，可让你在多个实例中区分 自包含在一个类中。你无需操作流或者socket，这个类帮你做了全部 支持基于IPV4和IPV6的TCP流 AsyncUdpSocket是UDP/IP socket网络库，包装自CFSocket。它的工作很像TCP版本，只不过是用于处理UDP的。它包括基于非阻塞队列的发送接收操作，完整的委托支持，基于runloop，自包含的类，以及支持IPV4和IPV6。 以下内容是根据官方网站参考: http://code.google.com/p/cocoaasyncsocket/wiki/Reference_AsyncSocket 编写的示例。 &#160; 准备工作：如何在iOS项目中使用 可按照官网链接执行： http://code.google.com/p/cocoaasyncsocket/wiki/iPhone 基本上是两步： 将CocoaAsyncSocket项目中的.h和.m文件拖拽到自己项目的Classes目录中 添加framework：CFNetwork &#160; 编写简单的TCP连接 编写个简单的TCP连接应用。HTTP其实就是建立在TCP协议上的。这里就用向网站发起请求和获得响应来演示。 为了形象说明，先手工模拟一下HTTP。这需要用到telnet工具，这是个命令行工具，如果在命令行里敲： C:\Users\Marshal Wu&#62;telnet &#8216;telnet&#8217; 不是内部或外部命令，也不是可运行的程序 或批处理文件。 &#160; 说明你使用的是windows vista或者windows7，因为windows xp是默认安装该软件的。 我用的是Mac OSX，上面自带这个工具。如果你出现上面的问题，可参照vista下使用telnet的做法安装telnet。 然后，可以使用这个工具发出socket信息，并接收socket返回信息。下面说一下步骤，如图： 下面用CocoaAsyncSocket来实现。 首先是要实现相关的delegate： #import &#60;UIKit/UIKit.h&#62; #import &#34;AsyncSocket.h&#34; @interface SocketDemosViewController : UIViewController&#60;AsyncSocketDelegate&#62; &#160; 然后，在实现代码中： - [...]]]></description>
			<content:encoded><![CDATA[<p>以下内容翻译自：</p>
<blockquote><p><a title="http://code.google.com/p/cocoaasyncsocket/" href="http://code.google.com/p/cocoaasyncsocket/">http://code.google.com/p/cocoaasyncsocket/</a></p>
</blockquote>
<p>CocoaAsyncSocket支持tcp和udp。其中：</p>
<ul>
<li>AsyncSocket类是支持TCP的</li>
<li>AsyncUdpSocket是支持UDP的</li>
</ul>
<p><em><strong>AsyncSocke</strong></em>t是封装了CFSocket和CFSteam的TCP/IP socket网络库。它提供了异步操作，本地cocoa类的基于delegate的完整支持。主要有以下特性：</p>
<ul>
<li>队列的非阻塞的读和写，而且可选超时。你可以调用它读取和写入，它会当完成后告知你</li>
<li>自动的socket接收。如果你调用它接收连接，它将为每个连接启动新的实例，当然，也可以立即关闭这些连接</li>
<li>委托（delegate）支持。错误、连接、接收、完整的读取、完整的写入、进度以及断开连接，都可以通过委托模式调用</li>
<li>基于run loop的，而不是线程的。虽然可以在主线程或者工作线程中使用它，但你不需要这样做。它异步的调用委托方法，使用NSRunLoop。委托方法包括socket的参数，可让你在多个实例中区分</li>
<li>自包含在一个类中。你无需操作流或者socket，这个类帮你做了全部</li>
<li>支持基于IPV4和IPV6的TCP流</li>
</ul>
<p>AsyncUdpSocket是UDP/IP socket网络库，包装自CFSocket。它的工作很像TCP版本，只不过是用于处理UDP的。它包括基于非阻塞队列的发送接收操作，完整的委托支持，基于runloop，自包含的类，以及支持IPV4和IPV6。</p>
<p><span id="more-4533"></span>
<p>以下内容是根据官方网站参考:</p>
<blockquote><p><a title="http://code.google.com/p/cocoaasyncsocket/wiki/Reference_AsyncSocket" href="http://code.google.com/p/cocoaasyncsocket/wiki/Reference_AsyncSocket">http://code.google.com/p/cocoaasyncsocket/wiki/Reference_AsyncSocket</a></p>
</blockquote>
<p>编写的示例。</p>
<p>&#160;</p>
<h3>准备工作：如何在iOS项目中使用</h3>
<p>可按照官网链接执行：</p>
<blockquote><p><a title="http://code.google.com/p/cocoaasyncsocket/wiki/iPhone" href="http://code.google.com/p/cocoaasyncsocket/wiki/iPhone">http://code.google.com/p/cocoaasyncsocket/wiki/iPhone</a></p>
</blockquote>
<p>基本上是两步：</p>
<ol>
<li>将CocoaAsyncSocket项目中的.h和.m文件拖拽到自己项目的Classes目录中</li>
<li>添加framework：CFNetwork</li>
</ol>
<p>&#160;</p>
<h3>编写简单的TCP连接</h3>
<p>编写个简单的TCP连接应用。HTTP其实就是建立在TCP协议上的。这里就用向网站发起请求和获得响应来演示。</p>
<p>为了形象说明，先手工模拟一下HTTP。这需要用到telnet工具，这是个命令行工具，如果在命令行里敲：</p>
<blockquote><p>C:\Users\Marshal Wu&gt;telnet     <br />&#8216;telnet&#8217; 不是内部或外部命令，也不是可运行的程序      <br />或批处理文件。</p>
<p>&#160;</p>
</blockquote>
<p>说明你使用的是windows vista或者windows7，因为windows xp是默认安装该软件的。</p>
<p>我用的是Mac OSX，上面自带这个工具。如果你出现上面的问题，可参照<a href="http://marshal.easymorse.com/archives/526" title="vista下使用telnet">vista下使用telnet</a>的做法安装telnet。</p>
<p>然后，可以使用这个工具发出socket信息，并接收socket返回信息。下面说一下步骤，如图：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2011/07/image27.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px 8px 0px 4px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2011/07/image_thumb27.png" width="457" height="277" /></a></p>
<p>下面用CocoaAsyncSocket来实现。</p>
<p>首先是要实现相关的delegate：</p>
<blockquote><p>#import &lt;UIKit/UIKit.h&gt;</p>
<p><em><strong>#import &quot;AsyncSocket.h&quot;</strong></em></p>
<p>@interface SocketDemosViewController : UIViewController<em><strong>&lt;AsyncSocketDelegate&gt;</strong></em> </p>
<p>&#160;</p>
</blockquote>
<p>然后，在实现代码中：</p>
<blockquote><p>- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{     <br />&#160;&#160;&#160; NSLog(@&quot;did connect to host&quot;);      <br />}</p>
<p>- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{     <br />&#160;&#160;&#160; NSLog(@&quot;did read data&quot;);      <br />&#160;&#160;&#160; NSString* message = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];       <br />&#160;&#160;&#160; NSLog(@&quot;message is: \n%@&quot;,message);       <br />}</p>
<p>&#160;</p>
</blockquote>
<p>AsyncSocketDelegate中的方法都是可选的。我实现了对建立连接后以及读取数据的监听。</p>
<p>这些监听需要创建和使用AsyncSocket实例时才能被用到。下面就是这部分代码：</p>
<blockquote><p>- (void)viewDidLoad {     <br />&#160;&#160;&#160; [super viewDidLoad];      <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; AsyncSocket *socket=[[AsyncSocket alloc] initWithDelegate:self];      <br />&#160;&#160;&#160; [socket connectToHost:@&quot;www.baidu.com&quot; onPort:80 error:nil];      <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; [socket readDataWithTimeout:3 tag:1];      <br />&#160;&#160;&#160; [socket writeData:[@&quot;GET / HTTP/1.1\n\n&quot; dataUsingEncoding:NSUTF8StringEncoding] withTimeout:3 tag:1];</p>
<p>&#160;</p>
</blockquote>
<p>我把这部分代码直接写到controller的viewDidLoad中了。</p>
<p>执行的日志如下：</p>
<blockquote><p>2011-07-19 17:17:46.545 SocketDemos[27120:207] did connect to host     <br />2011-07-19 17:17:46.620 SocketDemos[27120:207] did read data      <br />2011-07-19 17:17:46.621 SocketDemos[27120:207] message is:       <br />HTTP/1.1 200 OK      <br />Date: Tue, 19 Jul 2011 09:17:46 GMT      <br />Server: BWS/1.0      <br />Content-Length: 7691      <br />Content-Type: text/html;charset=gb2312      <br />Cache-Control: private      <br />Expires: Tue, 19 Jul 2011 09:17:46 GMT      <br />Set-Cookie: BAIDUID=9389BA38262D7997D220A564154CCA87:FG=1; expires=Tue, 19-Jul-41 09:17:46 GMT; path=/; domain=.baidu.com      <br />P3P: CP=&quot; OTI DSP COR IVA OUR IND COM &quot;      <br />Connection: Keep-Alive</p>
</blockquote>
<p>这里的HTTP响应被截断了，因为我们不是要编写真的接收HTTP响应的功能，因此这个缺陷可以忽略。</p>
<p>本来HTTP请求应该是由服务器端来关闭，比如使用telent访问看到的是这样的结尾：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2011/07/image28.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px 8px 0px 4px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2011/07/image_thumb28.png" width="463" height="163" /></a></p>
<p>因此，HTTP响应没有完全接收下来，服务器端未断掉连接。可以在客户端关闭连接，这样：</p>
<blockquote><p>[socket readDataWithTimeout:3 tag:1];     <br />[socket writeData:[@&quot;GET / HTTP/1.1\n\n&quot; dataUsingEncoding:NSUTF8StringEncoding] withTimeout:3 tag:1];      <br /><em><strong>[socket disconnect];         <br /></strong></em></p>
</blockquote>
<p>另外，可以实现delegate中的这个方法：</p>
<blockquote><p>- (void)onSocketDidDisconnect:(AsyncSocket *)sock{     <br />&#160;&#160;&#160; NSLog(@&quot;socket did disconnect&quot;);      <br />}</p>
<p>&#160;</p>
</blockquote>
<p>这样就可以在日志中监控到关闭连接的信息。</p>
<p>&#160;</p>
<h3>TCP连接读取指定长度的数据</h3>
<p>socket连接，经常碰到这样的需求，读取固定长度的字节。这可以通过下面的示例实现。</p>
<p>还是基于HTTP连接做演示。比如取2次，每次50字节。然后停止socket。</p>
<p>可以这样写：</p>
<blockquote><p>- (void)viewDidLoad {     <br />&#160;&#160;&#160; [super viewDidLoad];      <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; socket=[[AsyncSocket alloc] initWithDelegate:self];      <br />&#160;&#160;&#160; [socket connectToHost:@&quot;www.baidu.com&quot; onPort:80 error:nil];      <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; data=[[NSMutableData dataWithLength:50] retain];      <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; [socket readDataToLength:50 withTimeout:5 tag:1];      <br />&#160;&#160;&#160; [socket readDataToLength:50 withTimeout:5 tag:2];      <br />&#160;&#160;&#160; [socket writeData:[@&quot;GET / HTTP/1.1\n\n&quot; dataUsingEncoding:NSUTF8StringEncoding] withTimeout:3 tag:1];</p>
<p>&#160;</p>
</blockquote>
<p>在delegate中，主要是这个方法起作用：</p>
<blockquote><p>- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)_data withTag:(long)tag{     <br />&#160;&#160;&#160; NSLog(@&quot;did read data&quot;);      <br />&#160;&#160;&#160; NSString* message = [[[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding] autorelease];       <br />&#160;&#160;&#160; NSLog(@&quot;message is: \n%@&quot;,message);       <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; if (tag==2) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; [socket disconnect];      <br />&#160;&#160;&#160; }      <br />}</p>
</blockquote>
<p>日志类似这样：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2011/07/image29.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px 8px 0px 4px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2011/07/image_thumb29.png" width="497" height="168" /></a></p>
<p>标红色的是两次取出的字节内容。</p>
<p>&#160;</p>
<h3>编写服务器端Socket</h3>
<p>编写了Echo示例，说明最简单的服务器端Socket写法。Echo就是回声，通过telnet发送什么，服务器端就返回什么。类似这样：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2011/07/image30.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px 8px 0px 4px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2011/07/image_thumb30.png" width="307" height="87" /></a></p>
<p>服务器端，需要监听客户端的连接。等待客户端发来信息。代码是这样的：</p>
<blockquote><p>socket=[[AsyncSocket alloc] initWithDelegate:self];     <br />NSError *err = nil;</p>
<p>if ([socket acceptOnPort:4322 error:&amp;err]) {     <br />&#160;&#160;&#160; NSLog(@&quot;accept ok.&quot;);      <br />}else {      <br />&#160;&#160;&#160; NSLog(@&quot;accept failed.&quot;);      <br />}</p>
<p>if (err) {     <br />&#160;&#160;&#160; NSLog(@&quot;error: %@&quot;,err);      <br />}      </p>
<p>&#160;</p>
</blockquote>
<p>这一步如果成功，应该只有一个日志信息：</p>
<blockquote><p>2011-07-20 12:27:03.228 SocketDemos[611:707] accept ok.</p>
</blockquote>
<p>这时如果有客户端与之建立连接，比如通过telnet。会依次调用AsyncSocket 的delegate的如下方法：</p>
<ul>
<li>onSocket:didAcceptNewSocket: AsyncSocket创建了新的Socket用于处理和客户端的请求，如果这个新socket实例你不打算保留（retain），那么将拒绝和该客户端连接</li>
<li>onSocket:wantsRunLoopForNewSocket:，提供线程的runloop实例给AsyncSocket，后者将使用这个runloop执行socket通讯的操作</li>
<li>onSocketWillConnect:，将要建立连接，这时可以做一些准备工作，如果需要的话</li>
<li>onSocket:didConnectToHost:port:，这个方法是建立连接后执行的，一般会在这里调用写入或者读取socket的操作</li>
</ul>
<p>在Echo示例中，不打算执行多线程，也不想支持多客户端连接，而且服务器端和客户端将建立长连接。直至客户端断开连接，服务器端才释放相应的socket。</p>
<p>代码如下：</p>
<blockquote><p>- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket{     <br />&#160;&#160;&#160; if (!acceptSocket) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; acceptSocket=[newSocket retain];      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; NSLog(@&quot;did accept new socket&quot;);      <br />&#160;&#160;&#160; }      <br />}</p>
<p>- (NSRunLoop *)onSocket:(AsyncSocket *)sock wantsRunLoopForNewSocket:(AsyncSocket *)newSocket{     <br />&#160;&#160;&#160; NSLog(@&quot;wants runloop for new socket.&quot;);      <br />&#160;&#160;&#160; return [NSRunLoop currentRunLoop];      <br />}</p>
<p>- (BOOL)onSocketWillConnect:(AsyncSocket *)sock{     <br />&#160;&#160;&#160; NSLog(@&quot;will connect&quot;);      <br />&#160;&#160;&#160; return YES;      <br />}</p>
<p>- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{     <br />&#160;&#160;&#160; NSLog(@&quot;did connect to host&quot;);      <br />&#160;&#160;&#160; [acceptSocket readDataWithTimeout:-1 tag:1];      <br />}</p>
<p>- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{     <br />&#160;&#160;&#160; NSLog(@&quot;did read data&quot;);      <br />&#160;&#160;&#160; NSString* message = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];      <br />&#160;&#160;&#160; NSLog(@&quot;message is: \n%@&quot;,message);      <br />&#160;&#160;&#160; [acceptSocket writeData:data withTimeout:2 tag:1];      <br />}</p>
<p>- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag{     <br />&#160;&#160;&#160; NSLog(@&quot;message did write&quot;);      <br />&#160;&#160;&#160; [acceptSocket readDataWithTimeout:-1 tag:1];      <br />}</p>
<p>- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err{      <br />&#160;&#160;&#160; NSLog(@&quot;onSocket:%p willDisconnectWithError:%@&quot;, sock, err);       <br />}</p>
<p>- (void)onSocketDidDisconnect:(AsyncSocket *)sock{     <br />&#160;&#160;&#160; NSLog(@&quot;socket did disconnect&quot;);      <br />&#160;&#160;&#160; [acceptSocket release];      <br />&#160;&#160;&#160; acceptSocket=nil;      <br />}      </p>
<p>&#160;</p>
</blockquote>
<p>这里timeout设置为-1，这样就可以保持长连接状态。</p>
<p>&#160;</p>
<h3>编写简单的UDP应用</h3>
<p>首先，编写发送UDP数据报的示例。这需要有个服务器端能接收到内容。用Java写了个简单的接收端：</p>
<blockquote><p>public static void main(String[] args) throws IOException {     <br />&#160;&#160;&#160; InetSocketAddress address = new InetSocketAddress(&quot;0.0.0.0&quot;, 5555);      <br />&#160;&#160;&#160; DatagramSocket datagramSocket=new DatagramSocket(address);      <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; System.out.println(&quot;start udp server&quot;);      <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; byte[] buffer=new byte[1024];      <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; for(;;){      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; DatagramPacket datagramPacket=new DatagramPacket(buffer, buffer.length);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; datagramSocket.receive(datagramPacket);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(&quot;receive data:&quot;+new String(datagramPacket.getData(),0,datagramPacket.getLength()));      <br />&#160;&#160;&#160; }      <br />}</p>
<p>&#160;</p>
</blockquote>
<p>下面写发送的代码：</p>
<blockquote><p>AsyncUdpSocket *socket=[[AsyncUdpSocket alloc]initWithDelegate:self];</p>
<p>NSData *data=[@&quot;Hello from iPhone&quot; dataUsingEncoding:NSUTF8StringEncoding];     <br />[socket sendData:data toHost:@&quot;192.168.0.165&quot; port:5555 withTimeout:-1 tag:1];      <br />NSLog(@&quot;send upd complete.&quot;);      </p>
<p>&#160;</p>
</blockquote>
<p>执行后，在接收端成功输出如下内容：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2011/07/image31.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2011/07/image_thumb31.png" width="244" height="90" /></a></p>
<p>下面，写个接收端的代码：</p>
<blockquote><p>AsyncUdpSocket *socket=[[AsyncUdpSocket alloc] initWithDelegate:self];</p>
<p>NSError *error = nil;     <br />[socket bindToPort:5555 error:&amp;error];</p>
<p>if (error) {     <br />&#160;&#160;&#160; NSLog(@&quot;error: %@&quot;,error);      <br />}</p>
<p>[socket receiveWithTimeout:-1 tag:1];     <br />NSLog(@&quot;start udp server&quot;);      </p>
</blockquote>
<p>另外，至少写这个delegate方法：</p>
<blockquote><p>- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock      <br />&#160;&#160;&#160;&#160; didReceiveData:(NSData *)data       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; withTag:(long)tag       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; fromHost:(NSString *)host       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; port:(UInt16)port{      <br />&#160;&#160;&#160; NSLog(@&quot;received data: %@&quot;,[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]);      <br />&#160;&#160;&#160; return YES;      <br />}</p>
<p>&#160;</p>
</blockquote>
<p>发送端，还是用java写个测试代码：</p>
<blockquote><p>public static void main(String[] args) throws IOException {     <br />&#160;&#160;&#160; DatagramSocket datagramSocket = new DatagramSocket();      <br />&#160;&#160;&#160; byte[] buffer = &quot;Hello!&quot;.getBytes();      <br />&#160;&#160;&#160; DatagramPacket datagramPacket = new DatagramPacket(buffer,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; buffer.length, new InetSocketAddress(&quot;192.168.0.144&quot;, 5555));      <br />&#160;&#160;&#160; datagramSocket.send(datagramPacket);      <br />}</p>
<p>&#160;</p>
</blockquote>
<p>在iPhone日志中：</p>
<blockquote><p>2011-07-20 15:23:33.571 SocketDemos[795:707] start udp server     <br />2011-07-20 15:23:47.395 SocketDemos[795:707] received data: Hello!</p>
</blockquote>
<p>收到了数据报。</p>
<p>&#160;</p>
<h3>使用UDP发送和接收组播</h3>
<p>这里主要关注的是接收，一方面是需求上要求，另一方面，碰到过<a href="http://marshal.easymorse.com/archives/4461" title="Android Wifi获取组播">Android Wifi获取组播</a>问题，担心iOS也有类似的机制。后来测试发现没有那么麻烦（打开组播锁）。</p>
<p>为了测试，还是用java编写了个发送UDP广播的简单代码：</p>
<blockquote><p>public static void main(String[] args) throws IOException {     <br />&#160;&#160;&#160; int port=3333;      <br />&#160;&#160;&#160; MulticastSocket socket=new MulticastSocket(port);      <br />&#160;&#160;&#160; InetAddress address=InetAddress.getByName(&quot;239.0.0.1&quot;);      <br />&#160;&#160;&#160; socket.joinGroup(address);      <br />&#160;&#160;&#160; byte[] data=&quot;Hello everyone.&quot;.getBytes();      <br />&#160;&#160;&#160; DatagramPacket datagramPacket=new DatagramPacket(data,data.length,address,port);      <br />&#160;&#160;&#160; socket.send(datagramPacket);      <br />&#160;&#160;&#160; System.out.println(&quot;send ok.&quot;);</p>
<p>&#160;</p>
</blockquote>
<p>编写的iOS代码：</p>
<blockquote><p>AsyncUdpSocket *socket=[[AsyncUdpSocket alloc] initWithDelegate:self];</p>
<p>NSError *error = nil;     <br />[socket bindToPort:3333 error:&amp;error];      <br />[socket enableBroadcast:YES error:&amp;error];      <br />[socket joinMulticastGroup:@&quot;239.0.0.1&quot; error:&amp;error];</p>
<p>if (error) {     <br />&#160;&#160;&#160; NSLog(@&quot;error: %@&quot;,error);      <br />}</p>
<p>[socket receiveWithTimeout:-1 tag:1];     <br />NSLog(@&quot;start udp server&quot;);      </p>
<p>&#160;</p>
</blockquote>
<p>delegate和上面接收普通UDP一模一样：</p>
<blockquote><p>- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock      <br />&#160;&#160;&#160;&#160; didReceiveData:(NSData *)data       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; withTag:(long)tag       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; fromHost:(NSString *)host       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; port:(UInt16)port{      <br />&#160;&#160;&#160; NSLog(@&quot;received data: %@&quot;,[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]);      <br />&#160;&#160;&#160; return YES;      <br />}</p>
<p>&#160;</p>
</blockquote>
<p>测试得到的日志：</p>
<blockquote><p>2011-07-20 16:14:30.338 SocketDemos[860:707] start udp server     <br />2011-07-20 16:14:42.829 SocketDemos[860:707] received data: Hello everyone.</p>
</blockquote>
<p>说明是收到了。</p>
<p>发送组播和前面的UDP发送类似，只是多了要做join group的操作。这里就不多说了。</p>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/4533/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Grails实现zip文件上传及加入文件方案</title>
		<link>http://marshal.easymorse.com/archives/4522?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=grails%25e5%25ae%259e%25e7%258e%25b0zip%25e6%2596%2587%25e4%25bb%25b6%25e4%25b8%258a%25e4%25bc%25a0%25e5%258f%258a%25e5%258a%25a0%25e5%2585%25a5%25e6%2596%2587%25e4%25bb%25b6%25e6%2596%25b9%25e6%25a1%2588</link>
		<comments>http://marshal.easymorse.com/archives/4522#comments</comments>
		<pubDate>Tue, 19 Jul 2011 02:54:07 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[grails]]></category>
		<category><![CDATA[grails file upload]]></category>
		<category><![CDATA[grails MarkupBuilder]]></category>
		<category><![CDATA[grails thread]]></category>
		<category><![CDATA[grails xml]]></category>
		<category><![CDATA[grails zip]]></category>
		<category><![CDATA[groovy]]></category>
		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/4522</guid>
		<description><![CDATA[使用Grails和Groovy，实现了这样的需求： zip文件上传 zip文件上传后，在zip文件中加入自定义文本文件 在Grails实现复杂的数据录入上实现的本示例。示例如图： &#160; 准备工作：文件上传 首先说一下文件上传，在Grails中，借助Spring MVC的底层支持，实现还是很容易的。 视图： &#60;body&#62; &#60;div style=&#34;margin-left: 15px;&#34;&#62; &#60;g:message code=&#34;${flash.message}&#34; /&#62; &#60;g:form action=&#34;uploadFile&#34; method=&#34;post&#34; enctype=&#34;multipart/form-data&#34;&#62; &#60;input type=&#34;file&#34; name=&#34;myFile&#34; /&#62; &#60;input type=&#34;submit&#34; value=&#34;上传&#34; /&#62; &#60;input type=&#34;hidden&#34; name=&#34;id&#34; value=&#34;1&#34;/&#62; &#60;/g:form&#62; &#60;/div&#62; &#60;/body&#62; &#160; 在这里hidden了一个参数，是用于服务器端使用的。 在服务器端获取文件对象和id： def uploadFile={ def id=params.id def file=request.getFile(&#34;myFile&#34;) 下面的问题是： 如何异步的处理zip文件，同步处理是不可能的，那样浏览器端可能长期得不到响应 怎样向zip文件中加入文本文件 怎样动态生成文本文件，本例中是xml文件 &#160; 异步并发的实现 先说异步问题，我希望尽量回避线程，简化开发，这样可以让具体实现人员比较容易上手。这里使用了java的并发api，可参见这里[cref 3801]。 写法类似这样： class BookController [...]]]></description>
			<content:encoded><![CDATA[<p>使用Grails和Groovy，实现了这样的需求：</p>
<ul>
<li>zip文件上传</li>
<li>zip文件上传后，在zip文件中加入自定义文本文件</li>
</ul>
<p>在<a href="http://marshal.easymorse.com/archives/4470" title="Grails实现复杂的数据录入">Grails实现复杂的数据录入</a>上实现的本示例。示例如图：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2011/07/image26.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2011/07/image_thumb26.png" width="244" height="147" /></a></p>
<p>&#160;</p>
<h3>准备工作：文件上传</h3>
<p>首先说一下文件上传，在Grails中，借助Spring MVC的底层支持，实现还是很容易的。</p>
<p>视图：</p>
<blockquote><p>&lt;body&gt;     <br />&lt;div style=&quot;margin-left: 15px;&quot;&gt;      <br />&lt;g:message code=&quot;${flash.message}&quot; /&gt;      <br />&lt;g:form action=&quot;uploadFile&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;      <br />&lt;input type=&quot;file&quot; name=&quot;myFile&quot; /&gt;      <br />&lt;input type=&quot;submit&quot; value=&quot;上传&quot; /&gt;      <br />&lt;input type=&quot;hidden&quot; name=&quot;id&quot; value=&quot;1&quot;/&gt;      <br />&lt;/g:form&gt;      <br />&lt;/div&gt;      <br />&lt;/body&gt;</p>
<p>&#160;</p>
</blockquote>
<p>在这里hidden了一个参数，是用于服务器端使用的。</p>
<p><span id="more-4522"></span>
<p>在服务器端获取文件对象和id：</p>
<blockquote><p>def uploadFile={     <br />def id=params.id      <br />def file=request.getFile(&quot;myFile&quot;)</p>
</blockquote>
<p>下面的问题是：</p>
<ul>
<li>如何异步的处理zip文件，同步处理是不可能的，那样浏览器端可能长期得不到响应</li>
<li>怎样向zip文件中加入文本文件</li>
<li>怎样动态生成文本文件，本例中是xml文件</li>
</ul>
<p>&#160;</p>
<h3>异步并发的实现</h3>
<p>先说异步问题，我希望尽量回避线程，简化开发，这样可以让具体实现人员比较容易上手。这里使用了java的并发api，可参见这里[cref 3801]。</p>
<p>写法类似这样：</p>
<blockquote><p>class BookController {</p>
<p>&#160;&#160;&#160; ExecutorService executorService=Executors.newFixedThreadPool(2)</p>
<p>&#160;</p>
</blockquote>
<p>创建了个实例变量，允许2个并发。</p>
<p>可这样调用：</p>
<blockquote><p>executorService.submit(new Runnable(){     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; public void run() {</p>
<p>&#160;</p>
</blockquote>
<p>&#160;</p>
<h3>如何向zip文件中加入文件</h3>
<p>时间原因，没时间看apache commons compress等api是否能实现这个功能了。直接招呼java标准zip的api了。</p>
<p>基本思路是创建新的zip文件，将原zip文件中的各个entry加入到新的zip文件输出流中，最后，将自己的文本文件加入。</p>
<p>这里补充一句，从Grails中得到的file对象，生命周期应该是request作用域的。即，当上传文件请求到达服务器端后，在临时目录创建文件保存请求中的文件流。这个文件不可能长期保存，因为那样系统可能会因为文件增长瘫痪的。一般是通过一个Filter在生成响应发送给浏览器后删除本地的临时文件。</p>
<p>这样的话，异步线程的生命周期肯定会超过这个生命周期，因此要在request作用域内把该临时文件先复制到另外的目录下：</p>
<blockquote><p>def path=request.session.servletContext.getRealPath(&quot;/WEB-INF&quot;)</p>
<p>def sourceFile=new File(&quot;&quot;&quot;${path}/${id}.zip&quot;&quot;&quot;)     <br />def distFile=new File(&quot;&quot;&quot;${path}/${id}-dist.zip&quot;&quot;&quot;)      </p>
<p><em><strong>file.transferTo(sourceFile)</strong></em>      </p>
</blockquote>
<p>下面是完整的生成新zip文件，并且加入文本文件的代码：</p>
<blockquote><p>executorService.submit(new Runnable(){     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; public void run() {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ZipFile zipFile=new ZipFile(sourceFile)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ZipOutputStream outputStream=new ZipOutputStream(new FileOutputStream(distFile))      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Enumeration entries=zipFile.entries()      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; while(entries.hasMoreElements()){      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ZipEntry entry=entries.nextElement()      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; entry=new ZipEntry(entry.name)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; println entry      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; outputStream.putNextEntry(entry)</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if(!entry.isDirectory()){     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; InputStream inputStream=zipFile.getInputStream(entry)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; byte[] buffer=new byte[1024*1024]      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; for(int i=inputStream.read(buffer);i!=-1;i=inputStream.read(buffer)){      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; outputStream.write(buffer,0,i)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; inputStream.close()      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; outputStream.closeEntry()     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; zipFile.close()</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ZipEntry attachEntry=new ZipEntry(&quot;book.plist&quot;)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; outputStream.putNextEntry(attachEntry)</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; InputStream inputStream=new FileInputStream(attachedFile)</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <em><strong> outputStream.write(getResults(id).getBytes())</strong></em></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; inputStream.close()</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; outputStream.closeEntry()</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; outputStream.close()     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; println &quot;&gt;&gt;&gt;&gt; zip create ok.&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; })</p>
</blockquote>
<p>其中黑体中调用的是解决动态生成文本的内容，这里假定write方面里面的参数是String即可，是String.getBytes()。</p>
<p>&#160;</p>
<h3>动态生成自定义xml文本</h3>
<p>这里可参阅<a href="http://marshal.easymorse.com/archives/1377" title="groovy生成xml">groovy生成xml</a>，这里使用到了MarkupBuilder。</p>
<p>代码如下：</p>
<blockquote><p>&#160;&#160;&#160; def getResults(def bookId){     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; def writer=new StringWriter()      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; def xmlResults=new MarkupBuilder(writer)</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; xmlResults.plist(version:1.0){     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; dict{      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; key &#8216;key&#8217;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string &#8217;12&#8242;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; def content=&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;     <br />&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;<a href="http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;">http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;</a>&gt;      <br />${writer}      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;&quot;&quot;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; return content     <br />&#160;&#160;&#160; }</p>
<p>&#160;</p>
</blockquote>
<p>生成的文本类似这样：</p>
<blockquote><p>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;     <br />&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;<a href="http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;">http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;</a>&gt;      <br />&lt;plist version=&#8217;1.0&#8242;&gt;      <br />&#160; &lt;dict&gt;      <br />&#160;&#160;&#160; &lt;key&gt;key&lt;/key&gt;      <br />&#160;&#160;&#160; &lt;string&gt;12&lt;/string&gt;      <br />&#160; &lt;/dict&gt;      <br />&lt;/plist&gt;</p>
</blockquote>
<p>完整源代码见：</p>
<blockquote><p><a title="http://easymorse.googlecode.com/svn/tags/BookProto-0.5/" href="http://easymorse.googlecode.com/svn/tags/BookProto-0.5/">http://easymorse.googlecode.com/svn/tags/BookProto-0.5/</a></p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/4522/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>测试Tomcat对断点续传的支持</title>
		<link>http://marshal.easymorse.com/archives/4488?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e6%25b5%258b%25e8%25af%2595tomcat%25e5%25af%25b9%25e6%2596%25ad%25e7%2582%25b9%25e7%25bb%25ad%25e4%25bc%25a0%25e7%259a%2584%25e6%2594%25af%25e6%258c%2581</link>
		<comments>http://marshal.easymorse.com/archives/4488#comments</comments>
		<pubDate>Mon, 11 Jul 2011 13:54:14 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[grails]]></category>
		<category><![CDATA[http client]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[java httpclient]]></category>
		<category><![CDATA[tomcat]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/4488</guid>
		<description><![CDATA[虽然看Tomcat api中提供了对断点续传的支持。但是要确定下来就需要做实验。 还是借助编写断点续传客户端代码中的示例。另外，启动Grails项目（Grails实现复杂的数据录入），先测试一下静态资源文件： 链接是： http://localhost:8080/BookProto/images/grails_logo.png Grails降级后（Grails从1.4m1版本回退到1.3.7）后会不会使用的是jetty啊，测试一下： 故意找了个错误的链接，好了，确定了Tomcat及其版本。 测试代码和编写断点续传客户端代码中基本一致，除了链接不一样： public static void receiveBinaryDemo() throws ClientProtocolException, IOException{ &#160;&#160;&#160; HttpClient httpclient = new DefaultHttpClient(); &#160;&#160; HttpGet httpget = new HttpGet(&#34;http://localhost:8080/BookProto/images/grails_logo.png&#34;); &#160;&#160;&#160; httpget.addHeader(&#34;Range&#34;, &#34;bytes=3-&#34;); &#160;&#160;&#160; HttpResponse response = httpclient.execute(httpget); &#160;&#160;&#160; HttpEntity entity=response.getEntity(); &#160;&#160;&#160; &#160;&#160;&#160; System.out.println(&#34;content length: &#34;+entity.getContentLength()); &#160;&#160;&#160; System.out.println(&#34;status code:&#34;+response.getStatusLine().getStatusCode()); &#160;&#160;&#160; &#160;&#160;&#160; if(entity!=null){ &#160;&#160;&#160;&#160;&#160;&#160;&#160; InputStream inputStream=entity.getContent(); &#160;&#160;&#160;&#160;&#160;&#160;&#160; for(int i=inputStream.read(),j=1;i!=-1&#38;&#38;j&#60;12;i=inputStream.read(),j++){ [...]]]></description>
			<content:encoded><![CDATA[<p>虽然看Tomcat api中提供了对断点续传的支持。但是要确定下来就需要做实验。</p>
<p>还是借助<a href="http://marshal.easymorse.com/archives/4483" title="编写断点续传客户端代码">编写断点续传客户端代码</a>中的示例。另外，启动Grails项目（<a href="http://marshal.easymorse.com/archives/4470" title="Grails实现复杂的数据录入">Grails实现复杂的数据录入</a>），先测试一下静态资源文件：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2011/07/image18.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2011/07/image_thumb18.png" width="242" height="244" /></a></p>
<p>链接是：</p>
<blockquote><p><a title="http://localhost:8080/BookProto/images/grails_logo.png" href="http://localhost:8080/BookProto/images/grails_logo.png">http://localhost:8080/BookProto/images/grails_logo.png</a></p>
</blockquote>
<p>Grails降级后（<a href="http://marshal.easymorse.com/archives/4435" title="Grails从1.4m1版本回退到1.3.7">Grails从1.4m1版本回退到1.3.7</a>）后会不会使用的是jetty啊，测试一下：</p>
<p><span id="more-4488"></span>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2011/07/image19.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2011/07/image_thumb19.png" width="244" height="141" /></a></p>
<p>故意找了个错误的链接，好了，确定了Tomcat及其版本。</p>
<p>测试代码和<a href="http://marshal.easymorse.com/archives/4483" title="编写断点续传客户端代码">编写断点续传客户端代码</a>中基本一致，除了链接不一样：</p>
<blockquote><p>public static void receiveBinaryDemo() throws ClientProtocolException, IOException{     <br />&#160;&#160;&#160; HttpClient httpclient = new DefaultHttpClient();      <br />&#160;&#160; HttpGet httpget = new HttpGet(&quot;<a href="http://localhost:8080/BookProto/images/grails_logo.png&quot;);">http://localhost:8080/BookProto/images/grails_logo.png&quot;);</a>      <br />&#160;&#160;&#160; httpget.addHeader(&quot;Range&quot;, &quot;bytes=3-&quot;);      <br />&#160;&#160;&#160; HttpResponse response = httpclient.execute(httpget);      <br />&#160;&#160;&#160; HttpEntity entity=response.getEntity();      <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; System.out.println(&quot;content length: &quot;+entity.getContentLength());      <br />&#160;&#160;&#160; System.out.println(&quot;status code:&quot;+response.getStatusLine().getStatusCode());      <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; if(entity!=null){      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; InputStream inputStream=entity.getContent();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; for(int i=inputStream.read(),j=1;i!=-1&amp;&amp;j&lt;12;i=inputStream.read(),j++){      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(j+&quot;:&quot;+i);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160; }      <br />}      </p>
</blockquote>
<p>取消屏蔽和屏蔽后的结果分别为：</p>
<blockquote><p>content length: 10172     <br />status code:200      <br />1:137      <br />2:80      <br />3:78      <br />4:71      <br />5:13      <br />6:10      <br />7:26      <br />8:10      <br />9:0      <br />10:0      <br />11:0</p>
<p>content length: 10169     <br />status code:206      <br />1:71      <br />2:13      <br />3:10      <br />4:26      <br />5:10      <br />6:0      <br />7:0      <br />8:0      <br />9:13      <br />10:73      <br />11:72</p>
<p>&#160;</p>
</blockquote>
<p>说明静态资源是支持断点续传的。</p>
<p>那Tomcat是否支持动态二进制内容断点续传呢？还是需要自己开发实现？这个疑问等下一篇来实现。</p>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/4488/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>编写断点续传客户端代码</title>
		<link>http://marshal.easymorse.com/archives/4483?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e7%25bc%2596%25e5%2586%2599%25e6%2596%25ad%25e7%2582%25b9%25e7%25bb%25ad%25e4%25bc%25a0%25e5%25ae%25a2%25e6%2588%25b7%25e7%25ab%25af%25e4%25bb%25a3%25e7%25a0%2581</link>
		<comments>http://marshal.easymorse.com/archives/4483#comments</comments>
		<pubDate>Mon, 11 Jul 2011 13:40:06 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[http client]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[java httpclient]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/4483</guid>
		<description><![CDATA[在HttpClient下载二进制文件的基础是编写断点续传代码。 断点续传的基本原理是，客户端在发送请求中可加入比如这样的请求头： Range: bytes=3- 表示，要服务器发送从第四个字节开始的内容。如果服务器端支持断点续传，则返回响应的码不再是200，而是206。并且发送的内容是从第四个字节开始。 下面编写了两段代码，其中第一段，模拟正常请求，但是在读取到11个字节时停止。 代码如下： public class HttpClientDemo { &#160;&#160;&#160; public static void main(String[] args) throws ClientProtocolException, &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; IOException { &#160;&#160;&#160;&#160;&#160;&#160;&#160; receiveBinaryDemo(); &#160;&#160;&#160; } &#160;&#160;&#160; &#160;&#160;&#160; public static void receiveBinaryDemo() throws ClientProtocolException, IOException{ &#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpClient httpclient = new DefaultHttpClient(); &#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpGet httpget = new HttpGet(&#34;http://labs.renren.com/apache-mirror//activemq/apache-activemq/5.5.0/activemq-parent-5.5.0-source-release.zip&#34;); &#160;&#160;&#160;&#160;&#160;&#160; //httpget.addHeader(&#34;Range&#34;, &#34;bytes=3-&#34;); &#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpResponse response = httpclient.execute(httpget); [...]]]></description>
			<content:encoded><![CDATA[<p>在<a href="http://marshal.easymorse.com/archives/4482" title="HttpClient下载二进制文件">HttpClient下载二进制文件</a>的基础是编写断点续传代码。</p>
<p>断点续传的基本原理是，客户端在发送请求中可加入比如这样的请求头：</p>
<blockquote><p>Range: bytes=3-</p>
</blockquote>
<p>表示，要服务器发送从第四个字节开始的内容。如果服务器端支持断点续传，则返回响应的码不再是200，而是206。并且发送的内容是从第四个字节开始。</p>
<p> 下面编写了两段代码，其中第一段，模拟正常请求，但是在读取到11个字节时停止。</p>
<p><span id="more-4483"></span>
<p>代码如下：</p>
<blockquote><p>public class HttpClientDemo {     <br />&#160;&#160;&#160; public static void main(String[] args) throws ClientProtocolException,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; IOException {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; receiveBinaryDemo();      <br />&#160;&#160;&#160; }      <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; public static void receiveBinaryDemo() throws ClientProtocolException, IOException{      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpClient httpclient = new DefaultHttpClient();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpGet httpget = new HttpGet(&quot;<a href="http://labs.renren.com/apache-mirror//activemq/apache-activemq/5.5.0/activemq-parent-5.5.0-source-release.zip&quot;);">http://labs.renren.com/apache-mirror//activemq/apache-activemq/5.5.0/activemq-parent-5.5.0-source-release.zip&quot;);</a>      <br />&#160;&#160;&#160;&#160;&#160;&#160; <em><strong> //httpget.addHeader(&quot;Range&quot;, &quot;bytes=3-&quot;);         <br /></strong></em>&#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpResponse response = httpclient.execute(httpget);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpEntity entity=response.getEntity();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(&quot;content length: &quot;+entity.getContentLength());      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(&quot;status code:&quot;+response.getStatusLine().getStatusCode());      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if(entity!=null){      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; InputStream inputStream=entity.getContent();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; for(int i=inputStream.read(),j=1;i!=-1&amp;&amp;j&lt;12;i=inputStream.read(),j++){      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(j+&quot;:&quot;+i);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160; }</p>
<p>&#160;</p>
</blockquote>
<p>打印出来的内容：</p>
<blockquote><p>content length: 13683667     <br />status code:200      <br />1:80      <br />2:75      <br />3:3      <br />4:4      <br />5:10      <br />6:0      <br />7:0      <br />8:0      <br />9:0      <br />10:0      <br />11:115</p>
<p>&#160;</p>
</blockquote>
<p>如果把上述代码斜体字行的注释取消，相当于从第四个字节开始读取，输出结果是：</p>
<blockquote><p>content length: 13683664     <br />status code:206      <br />1:4      <br />2:10      <br />3:0      <br />4:0      <br />5:0      <br />6:0      <br />7:0      <br />8:115      <br />9:152      <br />10:124      <br />11:62      </p>
<p>&#160;</p>
</blockquote>
<p>说明断点续传成功。</p>
<p>用这个原理，还可以多线程并发下载。</p>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/4483/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HttpClient下载二进制文件</title>
		<link>http://marshal.easymorse.com/archives/4482?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=httpclient%25e4%25b8%258b%25e8%25bd%25bd%25e4%25ba%258c%25e8%25bf%259b%25e5%2588%25b6%25e6%2596%2587%25e4%25bb%25b6</link>
		<comments>http://marshal.easymorse.com/archives/4482#comments</comments>
		<pubDate>Mon, 11 Jul 2011 13:15:43 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[http client]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[java httpclient]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/4482</guid>
		<description><![CDATA[在最简单的HttpClient应用基础上，编写了从服务器下载二进制文件的示例。示例链接为： http://labs.renren.com/apache-mirror//activemq/apache-activemq/5.5.0/activemq-parent-5.5.0-source-release.zip 在国内，而且比较快，文件尺寸也合适。 public class HttpClientDemo { &#160;&#160;&#160; public static void main(String[] args) throws ClientProtocolException, &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; IOException { &#160;&#160;&#160;&#160;&#160;&#160;&#160; receiveBinaryDemo(); &#160;&#160;&#160; } &#160;&#160;&#160; &#160;&#160;&#160; public static void receiveBinaryDemo() throws ClientProtocolException, IOException{ &#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpClient httpclient = new DefaultHttpClient(); &#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpGet httpget = new HttpGet(&#34;http://labs.renren.com/apache-mirror//activemq/apache-activemq/5.5.0/activemq-parent-5.5.0-source-release.zip&#34;); &#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpResponse response = httpclient.execute(httpget); &#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpEntity entity=response.getEntity(); &#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160;&#160;&#160;&#160;&#160;&#160;&#160; if(entity!=null){ &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; [...]]]></description>
			<content:encoded><![CDATA[<p>在<a href="http://marshal.easymorse.com/archives/4481" title="最简单的HttpClient应用">最简单的HttpClient应用</a>基础上，编写了从服务器下载二进制文件的示例。示例链接为：</p>
<blockquote><p><a title="http://labs.renren.com/apache-mirror//activemq/apache-activemq/5.5.0/activemq-parent-5.5.0-source-release.zip" href="http://labs.renren.com/apache-mirror//activemq/apache-activemq/5.5.0/activemq-parent-5.5.0-source-release.zip">http://labs.renren.com/apache-mirror//activemq/apache-activemq/5.5.0/activemq-parent-5.5.0-source-release.zip</a></p>
</blockquote>
<p>在国内，而且比较快，文件尺寸也合适。</p>
<p><span id="more-4482"></span><br />
<blockquote>
<p>public class HttpClientDemo {     <br />&#160;&#160;&#160; public static void main(String[] args) throws ClientProtocolException,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; IOException {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; receiveBinaryDemo();      <br />&#160;&#160;&#160; }      <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; public static void receiveBinaryDemo() throws ClientProtocolException, IOException{      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpClient httpclient = new DefaultHttpClient();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpGet httpget = new HttpGet(&quot;<a href="http://labs.renren.com/apache-mirror//activemq/apache-activemq/5.5.0/activemq-parent-5.5.0-source-release.zip&quot;);">http://labs.renren.com/apache-mirror//activemq/apache-activemq/5.5.0/activemq-parent-5.5.0-source-release.zip&quot;);</a>      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpResponse response = httpclient.execute(httpget);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpEntity entity=response.getEntity();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if(entity!=null){      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; InputStream inputStream=entity.getContent();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; for(int i=inputStream.read(),j=0;i!=-1&amp;&amp;j&lt;502;i=inputStream.read(),j++){      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(i);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160; }</p>
</blockquote>
<p>这个代码没有什么实际意义，从这个链接下载502个字节。是为我后面编写的代码创建环境。</p>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/4482/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>最简单的HttpClient应用</title>
		<link>http://marshal.easymorse.com/archives/4481?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e6%259c%2580%25e7%25ae%2580%25e5%258d%2595%25e7%259a%2584httpclient%25e5%25ba%2594%25e7%2594%25a8</link>
		<comments>http://marshal.easymorse.com/archives/4481#comments</comments>
		<pubDate>Mon, 11 Jul 2011 12:55:57 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[http client]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[java httpclient]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/4481</guid>
		<description><![CDATA[使用httpclient编写最简单的应用，获取我博客的主页。 httpclient在： http://hc.apache.org/ 首先要下载分发包。然后将分发包解压缩，将lib目录下的jar文件，导入到项目中来，比如这样： 代码很简单： public class HttpClientDemo { &#160;&#160;&#160; public static void main(String[] args) throws ClientProtocolException, &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; IOException { &#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpClient httpclient = new DefaultHttpClient(); &#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpGet httpget = new HttpGet(&#34;http://marshal.easymorse.com/&#34;); &#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpResponse response = httpclient.execute(httpget); &#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpEntity entity=response.getEntity(); &#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160;&#160;&#160;&#160;&#160;&#160;&#160; if(entity!=null){ &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(EntityUtils.toString(entity)); &#160;&#160;&#160;&#160;&#160;&#160;&#160; }&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160;&#160;&#160; } 打印效果：]]></description>
			<content:encoded><![CDATA[<p>使用httpclient编写最简单的应用，获取我博客的主页。</p>
<p>httpclient在：</p>
<blockquote><p><a title="http://hc.apache.org/" href="http://hc.apache.org/">http://hc.apache.org/</a></p>
</blockquote>
<p>首先要下载分发包。然后将分发包解压缩，将lib目录下的jar文件，导入到项目中来，比如这样：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2011/07/image16.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2011/07/image_thumb16.png" width="204" height="244" /></a></p>
<p><span id="more-4481"></span>
<p>代码很简单：</p>
<blockquote><p>public class HttpClientDemo {     <br />&#160;&#160;&#160; public static void main(String[] args) throws ClientProtocolException,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; IOException {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpClient httpclient = new DefaultHttpClient();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpGet httpget = new HttpGet(&quot;<a href="http://marshal.easymorse.com/&quot;);">http://marshal.easymorse.com/&quot;);</a>      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpResponse response = httpclient.execute(httpget);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpEntity entity=response.getEntity();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if(entity!=null){      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(EntityUtils.toString(entity));      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160; }</p>
</blockquote>
<p>打印效果：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2011/07/image17.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2011/07/image_thumb17.png" width="436" height="89" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/4481/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>列出当前设备的网卡以及Socket绑定特定网卡</title>
		<link>http://marshal.easymorse.com/archives/4426?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e5%2588%2597%25e5%2587%25ba%25e5%25bd%2593%25e5%2589%258d%25e8%25ae%25be%25e5%25a4%2587%25e7%259a%2584%25e7%25bd%2591%25e5%258d%25a1%25e4%25bb%25a5%25e5%258f%258asocket%25e7%25bb%2591%25e5%25ae%259a%25e7%2589%25b9%25e5%25ae%259a%25e7%25bd%2591%25e5%258d%25a1</link>
		<comments>http://marshal.easymorse.com/archives/4426#comments</comments>
		<pubDate>Wed, 06 Jul 2011 10:11:21 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[java network]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/4426</guid>
		<description><![CDATA[这里是用Java写的示例。如何知道当前的设备有几个网卡： Enumeration&#60;NetworkInterface&#62; enumeration= NetworkInterface.getNetworkInterfaces(); while(enumeration.hasMoreElements()){ &#160;&#160;&#160;&#160; NetworkInterface networkInterface=enumeration.nextElement(); &#160;&#160;&#160;&#160; System.out.println(networkInterface); } &#160; 在我本机上显示： name:en1 (en1) index: 5 addresses: /192.168.0.172; /fe80:0:0:0:7aca:39ff:feb5:a315%5; name:lo0 (lo0) index: 1 addresses: /127.0.0.1; /fe80:0:0:0:0:0:0:1%1; /0:0:0:0:0:0:0:1; &#160; 如果有多个网卡，可以用如下代码指定Socket绑定哪个网卡： NetworkInterface networkInterface=NetworkInterface.getByName(&#34;tiwlan0&#34;); Socket socket=new Socket(); socket.bind(new InetSocketAddress(networkInterface.getInetAddresses().nextElement(),0)); socket.connect(new InetSocketAddress(&#34;www.baidu.com&#34;, 80)); 上面代码使用了网卡名为tiwlan0，然后用这个网卡的第一个ip地址绑定到socket上，使用端口为0表示，让系统来决定具体的端口号。本地端口号是无所谓的。然后连接到百度的80端口。]]></description>
			<content:encoded><![CDATA[<p>这里是用Java写的示例。如何知道当前的设备有几个网卡：</p>
<blockquote><p> Enumeration&lt;NetworkInterface&gt; enumeration= NetworkInterface.getNetworkInterfaces();     <br /> while(enumeration.hasMoreElements()){      <br />&#160;&#160;&#160;&#160; NetworkInterface networkInterface=enumeration.nextElement();      <br />&#160;&#160;&#160;&#160; System.out.println(networkInterface);      <br /> }</p>
<p>&#160;</p>
</blockquote>
<p>在我本机上显示：</p>
<blockquote><p>name:en1 (en1) index: 5 addresses:     <br />/192.168.0.172;      <br />/fe80:0:0:0:7aca:39ff:feb5:a315%5;</p>
<p>name:lo0 (lo0) index: 1 addresses:     <br />/127.0.0.1;      <br />/fe80:0:0:0:0:0:0:1%1;      <br />/0:0:0:0:0:0:0:1;</p>
<p>&#160;</p>
</blockquote>
<p><span id="more-4426"></span>
<p>如果有多个网卡，可以用如下代码指定Socket绑定哪个网卡：</p>
<blockquote><p>NetworkInterface networkInterface=NetworkInterface.getByName(&quot;tiwlan0&quot;);</p>
<p>Socket socket=new Socket();     <br />socket.bind(new InetSocketAddress(networkInterface.getInetAddresses().nextElement(),0));      <br />socket.connect(new InetSocketAddress(&quot;www.baidu.com&quot;, 80));</p>
</blockquote>
<p>上面代码使用了网卡名为tiwlan0，然后用这个网卡的第一个ip地址绑定到socket上，使用端口为0表示，让系统来决定具体的端口号。本地端口号是无所谓的。然后连接到百度的80端口。</p>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/4426/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>java特殊格式的时间处理</title>
		<link>http://marshal.easymorse.com/archives/4003?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=java%25e7%2589%25b9%25e6%25ae%258a%25e6%25a0%25bc%25e5%25bc%258f%25e7%259a%2584%25e6%2597%25b6%25e9%2597%25b4%25e5%25a4%2584%25e7%2590%2586</link>
		<comments>http://marshal.easymorse.com/archives/4003#comments</comments>
		<pubDate>Mon, 24 Jan 2011 02:14:02 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/4003</guid>
		<description><![CDATA[在java里，对数据进行格式化处理的对象在java.text包中。比如：MessageFormat。一般来说，如果去某一天中的某个时间的格式显示可以： System.out.println(MessageFormat.format(&#34;当前时间：{0,time}&#34;, new Date())); 或者，用长整型数也可以： System.out.println(MessageFormat.format(&#34;当前时间：{0,time}&#34;, 123456789)); 得到的显示类似这样： 当前时间：10:25:44 不过，这个时间显示是带默认时区的，比如在中国，如果按照格林尼治时间是凌晨1点，会调整为上午9点，因为中国是东八区。 在特殊的场合，这种显示就不符合需求了。比如要写个播放器，里面需要显示视频播放的时长。 当然只要会java就可以写出格式化的方法。不过要注意的是，要考虑国际化/本地化的问题，如果在代码中写了本地化的文字提示，或者当前应用的文字，就不大好复用了。 这里写一个简单的代码，供有此需求的人参考： long time = 1000 * 3600 * 25 + 1000 * 60 * 5 + 1000 * 62; long sec = 1000; long min = 1000 * 60; long hour = min * 60; System.out.println(MessageFormat.format( &#160;&#160;&#160;&#160;&#160;&#160;&#160; &#34;{0,number,00}:{1,number,00}:{2,number,00}&#34;, time / hour, time &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; [...]]]></description>
			<content:encoded><![CDATA[<p>在java里，对数据进行格式化处理的对象在java.text包中。比如：MessageFormat。一般来说，如果去某一天中的某个时间的格式显示可以：</p>
<blockquote><p>System.out.println(MessageFormat.format(&quot;当前时间：{0,time}&quot;, new Date()));</p>
</blockquote>
<p>或者，用长整型数也可以：</p>
<blockquote><p>System.out.println(MessageFormat.format(&quot;当前时间：{0,time}&quot;, 123456789));</p>
</blockquote>
<p>得到的显示类似这样：</p>
<blockquote><p>当前时间：10:25:44</p>
</blockquote>
<p>不过，这个时间显示是带默认时区的，比如在中国，如果按照格林尼治时间是凌晨1点，会调整为上午9点，因为中国是东八区。</p>
<p>在特殊的场合，这种显示就不符合需求了。比如要写个播放器，里面需要显示视频播放的时长。</p>
<p>当然只要会java就可以写出格式化的方法。不过要注意的是，要考虑国际化/本地化的问题，如果在代码中写了本地化的文字提示，或者当前应用的文字，就不大好复用了。</p>
<p>这里写一个简单的代码，供有此需求的人参考：</p>
<blockquote><p>long time = 1000 * 3600 * 25 + 1000 * 60 * 5 + 1000 * 62;     <br />long sec = 1000;      <br />long min = 1000 * 60;      <br />long hour = min * 60;      <br />System.out.println(MessageFormat.format(      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;{0,number,00}:{1,number,00}:{2,number,00}&quot;, time / hour, time      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; % hour / min, time % hour % min / sec));</p>
</blockquote>
<p>这里的：</p>
<blockquote><p>{0,number,00}:{1,number,00}:{2,number,00}</p>
</blockquote>
<p>是个字符串，是可以抽取出来放在国际化的资源文件里的。</p>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/4003/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>java对DWORD和TCHAR的处理</title>
		<link>http://marshal.easymorse.com/archives/3636?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=java%25e5%25af%25b9dword%25e5%2592%258ctchar%25e7%259a%2584%25e5%25a4%2584%25e7%2590%2586</link>
		<comments>http://marshal.easymorse.com/archives/3636#comments</comments>
		<pubDate>Fri, 19 Nov 2010 07:06:34 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[c]]></category>
		<category><![CDATA[dword]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[tchar]]></category>
		<category><![CDATA[vc]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/3636</guid>
		<description><![CDATA[对端是WinCE，传输struct结构体内存数据的字节流（类似这样将struct结构数据保存到文件中）。这边是java，需要读取并用类似方式回传数据。 考虑了一下，有两种解决思路： 使用JNI，java通过socket获取到流的字节数组，然后通过JNI用本地C编程转换成java可用的数据结构，比如整型数和char数组，这种办法肯定可行，但是需要开发人员会运用C编程，另外这里是Android，还要牵扯到NDK，部署起来也不灵活； 使用纯java解决，java将字节数组通过转换和移位等操作转换为java可用的数据，比如long和String，这种办法较好，技术跨度小，部署和移植性好。 本文最终选择了后者实现了处理。 我们碰到的struct结构类似这样： #define STAFF_ITEM_MAX_LEN&#160;&#160;&#160;&#160;&#160; (63) typedef struct _STAFF_INFO_T {&#160;&#160;&#160; &#160;&#160;&#160; DWORD dwStaffID; &#160;&#160;&#160; TCHAR szTitle[STAFF_ITEM_MAX_LEN + 1]; &#160;&#160;&#160; TCHAR szName[STAFF_ITEM_MAX_LEN + 1]; } STAFF_INFO, * PSTAFF_INFO; typedef const PSTAFF 对端的c处理代码： &#160;&#160;&#160; STAFF_INFO stf1 = { 0 }; &#160;&#160;&#160; STAFF_INFO stf2 = { 0 }; &#160;&#160;&#160; stf1.dwStaffID = 0&#215;12345678; &#160;&#160;&#160; _tcscpy_s(stf1.szName, STAFF_ITEM_MAX_LEN, [...]]]></description>
			<content:encoded><![CDATA[<p>对端是WinCE，传输struct结构体内存数据的字节流（类似这样<a href="http://marshal.easymorse.com/archives/3627" title="将struct结构数据保存到文件中">将struct结构数据保存到文件中</a>）。这边是java，需要读取并用类似方式回传数据。</p>
<p>考虑了一下，有两种解决思路：</p>
<ul>
<li>使用JNI，java通过socket获取到流的字节数组，然后通过JNI用本地C编程转换成java可用的数据结构，比如整型数和char数组，这种办法肯定可行，但是需要开发人员会运用C编程，另外这里是Android，还要牵扯到NDK，部署起来也不灵活； </li>
<li>使用纯java解决，java将字节数组通过转换和移位等操作转换为java可用的数据，比如long和String，这种办法较好，技术跨度小，部署和移植性好。 </li>
</ul>
<p>本文最终选择了后者实现了处理。</p>
<p>我们碰到的struct结构类似这样：</p>
<p>  <span id="more-3636"></span><br />
<blockquote>
<p>#define STAFF_ITEM_MAX_LEN&#160;&#160;&#160;&#160;&#160; (63)      <br />typedef struct _STAFF_INFO_T       <br />{&#160;&#160;&#160; <br />&#160;&#160;&#160; DWORD dwStaffID;       <br />&#160;&#160;&#160; TCHAR szTitle[STAFF_ITEM_MAX_LEN + 1];       <br />&#160;&#160;&#160; TCHAR szName[STAFF_ITEM_MAX_LEN + 1];       <br />} STAFF_INFO, * PSTAFF_INFO;       <br />typedef const PSTAFF</p>
</blockquote>
<p>对端的c处理代码：</p>
<blockquote><p>&#160;&#160;&#160; STAFF_INFO stf1 = { 0 };      <br />&#160;&#160;&#160; STAFF_INFO stf2 = { 0 };       <br />&#160;&#160;&#160; stf1.dwStaffID = 0&#215;12345678;       <br />&#160;&#160;&#160; _tcscpy_s(stf1.szName, STAFF_ITEM_MAX_LEN, _T(&quot;Staff Name&quot;));       <br />&#160;&#160;&#160; _tcscpy_s(stf1.szTitle, STAFF_ITEM_MAX_LEN, _T(&quot;Staff Title&quot;));       <br />&#160;&#160;&#160; stf2.dwStaffID = 0xABCDEF00;       <br />&#160;&#160;&#160; _tcscpy_s(stf2.szName, STAFF_ITEM_MAX_LEN, _T(&quot;Staff 名称&quot;));       <br />&#160;&#160;&#160; _tcscpy_s(stf2.szTitle, STAFF_ITEM_MAX_LEN, _T(&quot;Staff 头衔&quot;));       <br />&#160;&#160;&#160; FILE * pf = fopen(&quot;R:\\staff_sample.dat&quot;, &quot;wb&quot;);       <br />&#160;&#160;&#160; if( NULL != pf )       <br />&#160;&#160;&#160; {       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; fwrite(&amp;stf1, 1, sizeof(stf1), pf);       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; fwrite(&amp;stf2, 1, sizeof(stf2), pf);       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; fclose(pf);       <br />&#160;&#160;&#160; }</p>
</blockquote>
<p>这里需要处理的数据类型是2种，DWORD和TCHAR。DWORD是无符号的4字节整数，TCHAR在这里确定是Unicode，即wchar（wchar_t）。</p>
<p>查看一下生成的文件情况，DWORD类型的0&#215;12345678，生成的4字节情况：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/11/image20.png"><img style="background-image: none; border-right-width: 0px; margin: 0px 8px 0px 4px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/11/image_thumb20.png" width="93" height="29" /></a></p>
<p>TCHAR字符的字节，这里拿“头”这个字举例，2字节表示为：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/11/image21.png"><img style="background-image: none; border-right-width: 0px; margin: 0px 8px 0px 4px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/11/image_thumb21.png" width="48" height="22" /></a></p>
<h3>针对DWORD的转换</h3>
<p>DWORD类型如果映射到java基本型的话，只有long类型可以对应，long是8字节的有符号表示整数，DWORD是4字节表示无符号整数。其中：</p>
<ul>
<li>long的取值范围是2<sup>63</sup>-1~-2<sup>63</sup>，这里63是8位/字节*8个字节-1得来的 </li>
<li>DWORD就简单了，是2<sup>63</sup> </li>
</ul>
<p>因此这种转换将无法表示DWORD的最大值。</p>
<p>转换的原理是通过左移位运算，将4字节合并到一个long型值中，如果四字节分别是：｛0&#215;12,0&#215;34,0&#215;56,0&#215;78｝，那么先0&#215;12转换为0&#215;12000000，这样它需要左移3个字节长度即3*8=24位，以此类推，然后相加，即0&#215;12000000+0&#215;340000+0&#215;5600+0&#215;78，这样就得到了0&#215;12345678。</p>
<p>这里需要注意，上面说的字节数组示例是为了方便理解，实际上得到的是倒排序的，即｛0&#215;78,0&#215;56,0&#215;34,0&#215;12｝。</p>
<p>从字节数组转long的方法：</p>
<blockquote><p>public static long dwordBytesToLong(byte[] data) {      <br />&#160;&#160;&#160; return (data[3] &lt;&lt; 8 * 3) + (data[2] &lt;&lt; 8 * 2) + (data[1] &lt;&lt; 8 )       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; + data[0];       <br />}       </p>
<p>&#160;</p>
</blockquote>
<p>从long到字节数组的方法：</p>
<blockquote><p>public static byte[] longToDword(long value){      <br />&#160;&#160;&#160; byte[] data=new byte[4];       <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; for(int i=0;i&lt;data.length;i++){       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; data[i]=(byte) (value&gt;&gt;(8*i));       <br />&#160;&#160;&#160; }       <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; return data;       <br />}       </p>
</blockquote>
<p>&#160;</p>
<h3>针对TCHAR的转换</h3>
<p>VC里的TCHAR转换，如果无条件转换，无论是java还是*nux c都是比较麻烦的。因为TCHAR即可以是ASCII码，也可以是宽字符比如UNICODE，在*nix c中是区分的。还在这里对端肯定传的是unicode，使问题得到简化。</p>
<p>这里不能直接用String的参数是byte[]数组的构造方法，因为String会认为字节数组是字符集编码得到的字节，会自动用默认字符集解码，而struct的TCHAR是c从内存中直接将unicode字符的内存复制出来，就相当于把java的String字符在内存中复制下来一样。</p>
<p>unicode是内存中的字符表示，字符集比如GBK、UTF-8是unicode存储时的编码，二者有区别。</p>
<p>因为内存中unicode是两字节表示一个字符（char），因此将两个字节合并成java中的一个char，然后char数组再转换为字符串。这里要注意，java中的unicode的表示和c内存中位置颠倒，拿上面“头”字举例，字节的16进制表示是：0&#215;5934。</p>
<p>转换TCHAR到字符串的代码：</p>
<blockquote><p>public static String wcharUnicodeBytesToString(byte[] data) {      <br />&#160;&#160;&#160; StringBuilder builder = new StringBuilder();</p>
<p>&#160;&#160;&#160; for (int i = 0; i &lt; data.length; i += 2) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (data[i] == 0xfffffffe) {       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; byte[] temp = new byte[2];       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; temp[0] = data[i];       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; temp[1] = data[i + 1];       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; builder.append(wcharUnicodeBytesToChar(temp));       <br />&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160; return builder.toString();      <br />}       </p>
<p>&#160;</p>
<p>private static char wcharUnicodeBytesToChar(byte[] data) {      <br />&#160;&#160;&#160; char c = (char) (((data[1]) &lt;&lt; 8 ) + data[0]);       <br />&#160;&#160;&#160; return c;       <br />}</p>
</blockquote>
<p><font style="background-color: #f4f4f4"></font>将java字符串转换为TCHAR的byte数组代码：</p>
<blockquote><p>/**     <br /> * 将java字符串转换为wchar数组      <br /> * @param value      <br /> * @param arraySize      <br /> * @return      <br /> */      <br />public static byte[] stringToWcharUnicodeBytes(String value,int arraySize){      <br />&#160;&#160;&#160; char[] valueChars=value.toCharArray();      <br />&#160;&#160;&#160; byte[] data=new byte[arraySize*2];      <br />&#160;&#160;&#160; Arrays.fill(data, (byte)0xFE);      <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; for(int i=0;i&lt;valueChars.length &amp;&amp; i&lt;arraySize;i++){      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; data[i*2]=(byte) valueChars[i];      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; data[i*2+1]=(byte)(valueChars[i]&gt;&gt;8);      <br />&#160;&#160;&#160; }      <br />&#160;&#160;&#160; <br />&#160;&#160;&#160; return data;      <br />}      </p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/3636/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>编写push notification之服务器端发送通知</title>
		<link>http://marshal.easymorse.com/archives/3329?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e7%25bc%2596%25e5%2586%2599push-notification%25e4%25b9%258b%25e6%259c%258d%25e5%258a%25a1%25e5%2599%25a8%25e7%25ab%25af%25e5%258f%2591%25e9%2580%2581%25e9%2580%259a%25e7%259f%25a5</link>
		<comments>http://marshal.easymorse.com/archives/3329#comments</comments>
		<pubDate>Mon, 27 Sep 2010 13:21:28 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[ios notification]]></category>
		<category><![CDATA[ios push notification]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[objc]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/3329</guid>
		<description><![CDATA[在编写push notification之获取device token中拿到device token以后，需要把token字符串发送给应用的服务器端，即provider。 provider将token号、通知内容、通知形式（比如是否弹出提示窗口、是否发声等）发送给苹果的服务器（apns）。 最简单的provider实现，其实就是通过证书，和苹果服务器建立安全连接（tsl或ssl），通过认证建立连接后，向苹果服务器发送符合苹果要求的数据流。 获得证书 苹果提供两种接入方式的证书： developer，用于测试 production，用于产品 如果是内部测试，使用developer方式即可。 下载证书，通过ios provisioning portal： 这要求： 登录的apple developer program帐号必须是级别最高的agent（这是针对企业帐号来说的，如果是个人帐号就无所谓了），agent帐号即创始帐号，否则看不到configure链接； 必须经过configure操作，已经enable了developer和product。 然后进入configure链接，点击download按钮即可： &#160; 处理证书 如果是编写在mac下跑的objc程序，无需对证书做处理，可跳过这一步。 如果是在java下使用，需要把打证书用的私有专用密钥和上述的支持通知的证书（注意，不是iphone developer证书）合并导出。 生成证书： 点击存储的时候，会提示生成一个文件密码： 当然可以密码为空。 之后会提示： 这里需要输入mac登录用户的密码。 文件生成。 编写发送通知的实例 如果是编写mac代码，有一个现成的项目可用：http://stefan.hafeneger.name/download/PushMeBabySource.zip 导入到xcode中，只需将： deviceToken填写成设备的token字符串，另外，pathForResource改为上面图中的： aps_developer_identity 另外，要把刚才获得证书步骤中下载的证书复制到xcode项目Resources目录下： 可以看到文件名和上面的pathForResource的参数一致。 之后运行程序就可以在设备上收到推送通知。 如果是用java编写，可以用第三方库，见： http://code.google.com/p/javapns/ 编写简单的发送通知代码： import org.json.JSONException; import javapns.back.PushNotificationManager; import javapns.back.SSLConnectionHelper; import javapns.data.Device; import javapns.data.PayLoad; public class Main [...]]]></description>
			<content:encoded><![CDATA[<p>在<a href="http://marshal.easymorse.com/archives/3312" title="编写push notification之获取device token">编写push notification之获取device token</a>中拿到device token以后，需要把token字符串发送给应用的服务器端，即provider。</p>
<p>provider将token号、通知内容、通知形式（比如是否弹出提示窗口、是否发声等）发送给苹果的服务器（apns）。</p>
<p>最简单的provider实现，其实就是通过证书，和苹果服务器建立安全连接（tsl或ssl），通过认证建立连接后，向苹果服务器发送符合苹果要求的数据流。</p>
<h4>获得证书</h4>
<p>苹果提供两种接入方式的证书：</p>
<ul>
<li>developer，用于测试 </li>
<li>production，用于产品 </li>
</ul>
<p> <span id="more-3329"></span>
<p>如果是内部测试，使用developer方式即可。</p>
<p>下载证书，通过ios provisioning portal：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image65.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb65.png" width="522" height="45" /></a> </p>
<p>这要求：</p>
<ul>
<li>登录的apple developer program帐号必须是级别最高的agent（这是针对企业帐号来说的，如果是个人帐号就无所谓了），agent帐号即创始帐号，否则看不到configure链接； </li>
<li>必须经过configure操作，已经enable了developer和product。 </li>
</ul>
<p>然后进入configure链接，点击download按钮即可：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image66.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb66.png" width="437" height="123" /></a> </p>
<p>&#160;</p>
</p>
<h4>处理证书</h4>
<p>如果是编写在mac下跑的objc程序，无需对证书做处理，可跳过这一步。</p>
<p>如果是在java下使用，需要把打证书用的私有专用密钥和上述的支持通知的证书（注意，不是iphone developer证书）合并导出。</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image67.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb67.png" width="495" height="139" /></a> </p>
<p>生成证书：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image68.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb68.png" width="442" height="161" /></a> </p>
<p>点击存储的时候，会提示生成一个文件密码：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image69.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb69.png" width="341" height="222" /></a> </p>
<p>当然可以密码为空。</p>
<p>之后会提示：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image70.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb70.png" width="306" height="156" /></a> </p>
<p>这里需要输入mac登录用户的密码。</p>
<p>文件生成。</p>
<h4>编写发送通知的实例</h4>
</p>
</p>
<p>如果是编写mac代码，有一个现成的项目可用：<a title="http://stefan.hafeneger.name/download/PushMeBabySource.zip" href="http://stefan.hafeneger.name/download/PushMeBabySource.zip">http://stefan.hafeneger.name/download/PushMeBabySource.zip</a></p>
<p>导入到xcode中，只需将：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image71.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb71.png" width="449" height="147" /></a> </p>
<p>deviceToken填写成设备的token字符串，另外，pathForResource改为上面图中的：</p>
<blockquote><p>aps_developer_identity</p>
</blockquote>
<p>另外，要把刚才获得证书步骤中下载的证书复制到xcode项目Resources目录下：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image72.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb72.png" width="244" height="203" /></a> </p>
<p>可以看到文件名和上面的pathForResource的参数一致。</p>
<p>之后运行程序就可以在设备上收到推送通知。</p>
<p>如果是用java编写，可以用第三方库，见：</p>
<blockquote><p><a title="http://code.google.com/p/javapns/" href="http://code.google.com/p/javapns/">http://code.google.com/p/javapns/</a></p>
</blockquote>
<p>编写简单的发送通知代码：</p>
<blockquote><p>import org.json.JSONException; </p>
<p>import javapns.back.PushNotificationManager;      <br />import javapns.back.SSLConnectionHelper;       <br />import javapns.data.Device;       <br />import javapns.data.PayLoad; </p>
<p>public class Main { </p>
<p>&#160;&#160;&#160; /**      <br />&#160;&#160;&#160;&#160; * @param args       <br />&#160;&#160;&#160;&#160; * @throws Exception       <br />&#160;&#160;&#160;&#160; */       <br />&#160;&#160;&#160; public static void main(String[] args) throws Exception {       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; PayLoad simplePayLoad = new PayLoad();       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; // Get PushNotification Instance       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; PushNotificationManager pushManager = PushNotificationManager.getInstance();       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; // Link iPhone&#8217;s UDID (64-char device token) to a stringName       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; pushManager.addDevice(&quot;iPhone&quot;, &quot;00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 &quot;);       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; simplePayLoad.addAlert(&quot;My alert message测试&quot;);       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; simplePayLoad.addBadge(1);       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; simplePayLoad.addSound(&quot;default&quot;);       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Device client = PushNotificationManager.getInstance().getDevice(&quot;iPhone&quot;);       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; PushNotificationManager.getInstance().initializeConnection(&quot;gateway.sandbox.push.apple.com&quot;, 2195, &quot;/home/ubuntu/mypush.p12&quot;, &quot;password&quot;, SSLConnectionHelper.KEYSTORE_TYPE_PKCS12);       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; PushNotificationManager.getInstance().sendNotification(client, simplePayLoad);</p>
<p>&#160;</p>
</blockquote>
<p>测试中文没有乱码问题。</p>
<p>编写比较复杂的使用示例（可以控制通知是否有提示窗口、是否有提醒声音）：</p>
<ul>
<li>aPayload.addBadge( 2)，显示在手机应用图标上的数字 </li>
<li>aPayload.addAlert(&quot;软件版本有更新&quot;)，显示提示窗口文字 </li>
<li>aPayload.addSound(&quot;default.wav&quot;)，指定提示声音 </li>
</ul>
<p>另外，也可以使用php的第三方实现，比如：</p>
<blockquote><p><a title="http://code.google.com/p/php-apns" href="http://code.google.com/p/php-apns">http://code.google.com/p/php-apns</a></p>
</blockquote>
<p>基本原理是启动一个php服务，监控memcacheq队列，如果有消息就发送给苹果服务器。</p>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/3329/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

