﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-周游世界</title><link>http://www.blogjava.net/yanzhou/</link><description>喂马, 劈柴, 周游世界</description><language>zh-cn</language><lastBuildDate>Wed, 29 Apr 2026 16:48:39 GMT</lastBuildDate><pubDate>Wed, 29 Apr 2026 16:48:39 GMT</pubDate><ttl>60</ttl><item><title>HTML控件的方法和属性</title><link>http://www.blogjava.net/yanzhou/archive/2006/12/30/91030.html</link><dc:creator>周游世界</dc:creator><author>周游世界</author><pubDate>Sat, 30 Dec 2006 08:57:00 GMT</pubDate><guid>http://www.blogjava.net/yanzhou/archive/2006/12/30/91030.html</guid><wfw:comment>http://www.blogjava.net/yanzhou/comments/91030.html</wfw:comment><comments>http://www.blogjava.net/yanzhou/archive/2006/12/30/91030.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanzhou/comments/commentRss/91030.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanzhou/services/trackbacks/91030.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: API 参考								对象参考												此部分显示了 Google Gadgets API 中与脚本相关的不同对象，包括它们的属性、方法和相关事件。注意，许多对象继承了 basicElement 的属性、方法和事件。														同样，如果说一个方法返回了一个元素，这意味着在小工具的 XML 定义中定义了一个元素，它继承自 ...&nbsp;&nbsp;<a href='http://www.blogjava.net/yanzhou/archive/2006/12/30/91030.html'>阅读全文</a><img src ="http://www.blogjava.net/yanzhou/aggbug/91030.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanzhou/" target="_blank">周游世界</a> 2006-12-30 16:57 <a href="http://www.blogjava.net/yanzhou/archive/2006/12/30/91030.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>几种证书格式说明及Keytool命令</title><link>http://www.blogjava.net/yanzhou/archive/2006/12/12/87208.html</link><dc:creator>周游世界</dc:creator><author>周游世界</author><pubDate>Tue, 12 Dec 2006 04:50:00 GMT</pubDate><guid>http://www.blogjava.net/yanzhou/archive/2006/12/12/87208.html</guid><wfw:comment>http://www.blogjava.net/yanzhou/comments/87208.html</wfw:comment><comments>http://www.blogjava.net/yanzhou/archive/2006/12/12/87208.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanzhou/comments/commentRss/87208.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanzhou/services/trackbacks/87208.html</trackback:ping><description><![CDATA[.cer 包含证书主体的信息及证书的公钥，不包括私钥，可以公开。<br />.keystore 证书库，包含用户的公钥、私钥和证书。<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080"> 1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">            FileInputStream in </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> FileInputStream(Path </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">RGCA.keystore</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080"> 2</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            KeyStore ks </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> KeyStore.getInstance(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">JKS</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080"> 3</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            ks.load(in, pwd.toCharArray());<br /></span><span style="COLOR: #008080"> 4</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            Certificate certificate </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> ks.getCertificate(alias);<br /></span><span style="COLOR: #008080"> 5</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            System.out.println(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">从证书库中取得:</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">RadiusUtil.getHexString(certificate.getEncoded()));<br /></span><span style="COLOR: #008080"> 6</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            System.out.println(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">从证书库中取得PublicKey：</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">RadiusUtil.getHexString(certificate.getPublicKey().getEncoded()));<br /></span><span style="COLOR: #008080"> 7</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            X509Certificate x </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> (X509Certificate)certificate;<br /></span><span style="COLOR: #008080"> 8</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            System.out.println(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">从证书库中取得Signature：</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">RadiusUtil.getHexString(x.getSignature()));<br /></span><span style="COLOR: #008080"> 9</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            PrivateKey caprk </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> (PrivateKey)ks.getKey(alias, pwd.toCharArray());<br /></span><span style="COLOR: #008080">10</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            System.out.println(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">私钥:</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">RadiusUtil.getHexString(caprk.getEncoded()));</span></div><img src ="http://www.blogjava.net/yanzhou/aggbug/87208.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanzhou/" target="_blank">周游世界</a> 2006-12-12 12:50 <a href="http://www.blogjava.net/yanzhou/archive/2006/12/12/87208.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ]认证方式探讨 EAP-MSCHAPV2</title><link>http://www.blogjava.net/yanzhou/archive/2006/11/12/80703.html</link><dc:creator>周游世界</dc:creator><author>周游世界</author><pubDate>Sun, 12 Nov 2006 07:24:00 GMT</pubDate><guid>http://www.blogjava.net/yanzhou/archive/2006/11/12/80703.html</guid><wfw:comment>http://www.blogjava.net/yanzhou/comments/80703.html</wfw:comment><comments>http://www.blogjava.net/yanzhou/archive/2006/11/12/80703.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanzhou/comments/commentRss/80703.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanzhou/services/trackbacks/80703.html</trackback:ping><description><![CDATA[
		<p align="center">
				<strong>
						<font size="5">认证方式探讨 EAP-MSCHAPV2</font>
				</strong>
				<br />
				<br />
		</p>
		<p>MSCHAP方式是,首先服务器发一个challenge给用户,用户向RADIUS发一个用MD4加密的(password,challenge)给RADIUS(叫response),radius的MSCHAP模块向LDAP询问NTPASSWORD,然后自己再用challenge和NTPASSWORD,来计算一个response,两个response相比较完成验证.如果LDAP无法给出NTPASSWORD或送出SHA加密的NTPASSWORD,MS-CHAP就无法验证了.唯一可行的办法是把LDAP的SCHEMA中加入NTPASSWORD域,并明文存放,才能满足MS-CHAP的要求.</p>
		<p>
				<br />PEAP-MSCHAPV2流程： <br />  1、创建一个连接后，AP发送一个EAP-Request/Identity消息给客户端。<br />  2、客户端回复一个EAP-Response/Identity消息，包含客户端的标识，通常是名字。<br />  3、AP把这个消息传递给Radius服务器。从现在开始，逻辑通信就从Radius服务器到无线客户端了，AP,AC是一个中介设备。<br />  4、Radius服务器发送一个EAP-Request/Start PEAP消息给客户端。<br />  5、无线客户端和Radius服务器发送一系列的TLS消息通过协商建立的隧道。Raiuds服务器发送一个证书链让客户端认证。最后，Radius服务器已经认证了无线客户端。两端都决定使用的加密信息。<br />  在PEAP TLS隧道创建后，采用MS-CHAPV2认证。<br />  6、Radius服务器发送一个EAP-Request/Identity消息。<br />  7、无线客户端发送一个EAP-Response/Identity 消息，消息包用户名。<br />  8、Radius服务器发送一个EAP-Request/EAP-MS-CHAP-V2挑战消息，包含挑战字符串。<br />  9、无线客户端回复一个EAP-Response/EAP-MS-CHAPV2回复消息，包含对这个挑战的应答和一个自己的挑战。<br />  10、Radius服务器发送一个EAP-Request/EAP-MS-CHAPV2成功的消息，指出无线客户端的回应是正确的，且包含无线客户端的挑战字符串。<br />  11、无线客户端回应一个EAP-Response/EAP-MS-CHAPV2的ACK消息，指示Radius服务器的回应消息是正确的。<br />  12、Radius服务器发送一个EAP-Success消息。</p>
		<p>EAP -TLS是一个IETF标准。TLS即传输层安全（Transport Layer Security），也称为SSL。它原本是一种传输层的安全协议，主要实现两个功能：身份鉴别与信息加密。TLS可实现基于证书的单向身份鉴别（只鉴别服务器）和双向身份鉴别（同时鉴别客户端与服务器）。TLS在完成身份鉴别的同时，还交换密钥信息，通过密钥信息可导出会话密钥用于信息加密。<br />需要指出的是，在这里TLS并不是作为一个安全传输层协议跑在TCP/IP层之上，而是将TLS的Handshake Record直接嵌套在EAP数据包中，作为EAP Request/Response的数据来传送，通过TLS的Handshake Record完成单向或双向的身份鉴别。EAP－TLS只利用了TLS的身份鉴别功能，并没有利用TLS建立的加密通道。<br />为了能够进一步利用 TLS建立的安全通道交换EAP身份鉴别信息，IETF随后出台了PEAP（Protected EAP Protocol）标准草案。PEAP不但通过EAP Request/Response数据包传送TLS的Handshake Record完成身份鉴别，并且完成身份鉴别后进一步通过TLS的Data Record再传送EAP身份鉴别协议。</p>
		<p>对于密钥：<br />der  “专有编码规则”文件。.der 文件包含证书的二进制表示，包含其公共密钥，但不包含其专用密钥。它与 .arm 文件非常相似，除了表示是二进制的，而非 ASCII。 <br />.p12  "PKCS 12" 文件，其中 PKCS 代表“公共密钥密码术标准 (Public-Key CryptographyStandards)”。.p12 文件包含证书的二进制表示，包含其公共密钥和专用密钥。一个 .p12 文件中也可能包含多个证书；例如，证书、发出证书的 CA 的证书、CA 证书的发出者以及他的发出者等等。因为 .p12 文件包含专用密钥，它是受口令保护的。<br /></p>
<img src ="http://www.blogjava.net/yanzhou/aggbug/80703.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanzhou/" target="_blank">周游世界</a> 2006-11-12 15:24 <a href="http://www.blogjava.net/yanzhou/archive/2006/11/12/80703.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关注的CVS, SVN </title><link>http://www.blogjava.net/yanzhou/archive/2006/11/06/79471.html</link><dc:creator>周游世界</dc:creator><author>周游世界</author><pubDate>Mon, 06 Nov 2006 12:56:00 GMT</pubDate><guid>http://www.blogjava.net/yanzhou/archive/2006/11/06/79471.html</guid><wfw:comment>http://www.blogjava.net/yanzhou/comments/79471.html</wfw:comment><comments>http://www.blogjava.net/yanzhou/archive/2006/11/06/79471.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanzhou/comments/commentRss/79471.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanzhou/services/trackbacks/79471.html</trackback:ping><description><![CDATA[Hibernate3： <br />:pserver:anonymous@cvs.sourceforge.net:/cvsroot/hibernate 模块Hibernate3<br /><br />Spring<br />:pserver:anonymous@cvs.sourceforge.net:/cvsroot/springframework 模块spring<br /><br />Groovy<br />http://svn.codehaus.org/groovy/<br /><br />Grails<br />http://svn.codehaus.org/grails/<br /><br />Seam<br />:pserver:anonymous@anoncvs.forge.jboss.com:/cvsroot/jboss 模块jboss-seam-head<br /><br />MyFaces<br />https://svn.apache.org/repos/asf/myfaces/current<br /><br />Taconite<br />:pserver:anonymous@cvs.sourceforge.net:/cvsroot/taconite 模块taconite<br /><br />Liferay<br />:pserver:anonymous@cvs.sourceforge.net:/cvsroot/lportal 模块portal<br /><br />XFire<br />:pserver:anonymous@cvs.xfire.codehaus.org:/home/projects/xfire/scm 模块xfire<br /><br />GlassFish<br />:pserver:guest@cvs.dev.java.net:/cvs 模块glassfish<br /><br />Roller<br />:pserver:guest@cvs.dev.java.net:/cvs 模块roller<br /><br />SpringModules<br />:pserver:guest@cvs.dev.java.net:/cvs 模块springmodules<img src ="http://www.blogjava.net/yanzhou/aggbug/79471.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanzhou/" target="_blank">周游世界</a> 2006-11-06 20:56 <a href="http://www.blogjava.net/yanzhou/archive/2006/11/06/79471.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA教程 第八讲 Java网络编程(三)</title><link>http://www.blogjava.net/yanzhou/archive/2006/11/05/79186.html</link><dc:creator>周游世界</dc:creator><author>周游世界</author><pubDate>Sun, 05 Nov 2006 04:36:00 GMT</pubDate><guid>http://www.blogjava.net/yanzhou/archive/2006/11/05/79186.html</guid><wfw:comment>http://www.blogjava.net/yanzhou/comments/79186.html</wfw:comment><comments>http://www.blogjava.net/yanzhou/archive/2006/11/05/79186.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanzhou/comments/commentRss/79186.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanzhou/services/trackbacks/79186.html</trackback:ping><description><![CDATA[
		<span id="BlogViewId">
				<div>8.3.10 据报Datagram通讯 <br /><br />　　前面在介绍TCP/IP协议的时候，我们已经提到，在TCP/IP协议的传输层除了TCP协议之外还有一个UDP协议，相比而言UDP的应用不如TCP广泛，几个标准的应用层协议 HTTP，FTP，SMTP…使用的都是TCP协议。但是，随着计算机网络的发展，UDP协议正越来越来显示出其威力，尤其是在需要很强的实时交互性的场合，如<a href="http://www.it.com.cn/games/net/"><u><font color="#0000ff">网络</font></u></a><a href="http://www.it.com.cn/games/"><u><font color="#0000ff">游戏</font></u></a>，视频会议等，UDP更是显示出极强的威力，下面我们就介绍一下Java环境下如何实现UDP网络传输。 <br /><br />　　8.3.11 什么是Datagram <br /><br />　　所谓数据报（Datagram）就跟日常生活中的邮件系统一样，是不能保证可靠的寄到的，而面向链接的TCP就好比电话，双方能肯定对方接受到了信息。在本章前面，我们已经对UDP和TCP进行了比较，在这里再稍作小节： <br /><br />　　TCP，可靠，传输大小无限制，但是需要连接建立时间，差错控制开销大。 <br />　　UDP，不可靠，差错控制开销较小，传输大小限制在64K以下，不需要建立连接。 <br /><br />　　总之，这两种协议各有特点，应用的场合也不同，是完全互补的两个协议，在TCP/IP协议中占有同样重要的地位，要学好网络编程，两者缺一不可。 <br /><br />　　8.3.12 Datagram通讯的表示方法：DatagramSocket；DatagramPacket <br /><br />　　包java.net中提供了两个类DatagramSocket和DatagramPacket用来支持数据报通信，DatagramSocket用于在程序之间建立传送数据报的通信连接， DatagramPacket则用来表示一个数据报。先来看一下DatagramSocket的构造方法： <br />　　　DatagramSocket（）； <br />　　　DatagramSocket（int prot）; <br />　　　DatagramSocket(int port, InetAddress laddr) <br />　　 <br />　　其中，port指明socket所使用的端口号，如果未指明端口号，则把socket连接到本地主机上一个可用的端口。laddr指明一个可用的本地地址。给出端口号时要保证不发生端口冲突，否则会生成SocketException类例外。注意：上述的两个构造方法都声明抛弃非运行时例外 SocketException，程序中必须进行处理，或者捕获、或者声明抛弃。 <br /><br />　　用数据报方式编写client/server程序时，无论在客户方还是服务方，首先都要建立一个DatagramSocket对象，用来接收或发送数据报，然后使用DatagramPacket类对象作为传输数据的载体。下面看一下DatagramPacket的构造方法 ： <br />　　　DatagramPacket（byte buf[],int length）； <br />　　　DatagramPacket(byte buf[], int length, InetAddress addr, int port); <br />　　　DatagramPacket(byte[] buf, int offset, int length)； <br />　　　DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)； <br /><br />　　其中，buf中存放数据报数据，length为数据报中数据的长度，addr和port旨明目的地址，offset指明了数据报的位移量。 <br /><br />　　在接收数据前，应该采用上面的第一种方法生成一个DatagramPacket对象，给出接收数据的缓冲区及其长度。然后调用DatagramSocket 的方法receive()等待数据报的到来，receive()将一直等待，直到收到一个数据报为止。 <br />　　DatagramPacket packet=new DatagramPacket(buf, 256); <br />　　Socket.receive (packet); <br /><br />　　发送数据前，也要先生成一个新的DatagramPacket对象，这时要使用上面的第二种构造方法，在给出存放发送数据的缓冲区的同时，还要给出完整的目的地址，包括IP地址和端口号。发送数据是通过DatagramSocket的方法send()实现的，send()根据数据报的目的地址来寻径，以传递数据报。 <br />　　DatagramPacket packet=new DatagramPacket(buf, length, address, port); <br />　　Socket.send(packet)； <br /><br />　　在构造数据报时，要给出InetAddress类参数。类InetAddress在包java.net中定义，用来表示一个Internet地址，我们可以通过它提供的类方法getByName（）从一个表示主机名的字符串获取该主机的IP地址，然后再获取相应的地址信息。 <br /><br />　　8.3.13 基于UDP的简单的Client/Server程序设计 <br /><br />　　有了上面的知识，我们就可以来构件一个基于UDP的C/S 网络传输模型 <br /><br />　　1. 客户方程序 QuoteClient.java <br /><br />　　import java.io.*; <br />　　import java.net.*; <br />　　import java.util.*; <br />　　public class QuoteClient { <br />　　　public static void main(String[] args) throws IOException <br />　　　{ <br />　　　　if(args.length!=1) { <br />　　　　//如果启动的时候没有给出Server的名字，那么出错退出 <br />　　　　　System.out.println("Usage:java QuoteClient "); <br />　　　　　//打印出错信息 <br />　　　　　return; //返回 <br />　　　　} <br /><br />　　　　DatagramSocket socket=new DatagramSocklet(); <br />　　　　//创建数据报套接字 <br /><br />　　　　Byte[] buf=new byte[256]; //创建缓冲区 <br />　　　　InetAddress address=InetAddress.getByName(args [0]); <br />　　//由命令行给出的第一个参数默认为Server的名字，通过它得到Server的IP信息 <br />　　　　DatagramPacket packet=new DatagramPacket (buf, buf.length, address, 4445); <br />　　　　//创建DatagramPacket对象 <br />　　　　socket.send(packet); //发送 <br />　　　　packet=new DatagramPacket(buf,buf.length); <br />　　　　//创建新的DatagramPacket对象，用来接收数据报 <br />　　　　socket.receive(packet); //接收 <br />　　　　String received=new String(packet.getData()); <br />　　　　//根据接收到的字节数组生成相应的字符串 <br />　　　　System.out.println("Quote of the Moment:"+received ); <br />　　　　//打印生成的字符串 <br /><br />　　　　socket.close(); //关闭套接口 <br />　　　} <br />　　} <br /><br />　　2. <a href="http://www.it.com.cn/server/"><u><font color="#0000ff">服务器</font></u></a>方程序:QuoteServer.java <br /><br />　　public class QuoteServer{ <br />　　　public static void main(String args[]) throws java.io.IOException <br />　　　{ <br />　　　　new QuoteServerThread().start(); <br />　　　　//启动一个QuoteServerThread线程 <br />　　　} <br />　　} <br /><br />　　3. 程序QuoteServerThread.java <br /><br />　　import java.io.*; <br />　　import java.net.*; <br />　　import java.util.*; <br />　　//服务器线程 <br />　　public class QuoteServerThread extends Thread <br />　　{ <br />　　protected DatagramSocket socket=null; <br />　　//记录和本对象相关联的DatagramSocket对象 <br />　　protected BufferedReader in=null; <br />　　//用来读文件的一个Reader <br />　　protected boolean moreQuotes=true; <br />　　//标志变量，是否继续操作 <br /><br />　　public QuoteServerThread() throws IOException { <br />　　//无参数的构造函数 <br />　　　　this("QuoteServerThread"); <br />　　　　//以QuoteServerThread为默认值调用带参数的构造函数 <br />　　} <br />　　public QuoteServerThread(String name) throws IOException { <br />　　　　super(name); //调用父类的构造函数 <br />　　　　socket=new DatagramSocket(4445); <br />　　　　//在端口4445创建数据报套接字 <br />　　　　try{ <br />　　　　　　in= new BufferedReader(new FileReader(" one-liners.txt")); <br />　　　　　　//打开一个文件，构造相应的BufferReader对象 <br />　　　　}catch(FileNotFoundException e) { //异常处理 <br />　　　　　　System.err.println("Could not open quote file. Serving time instead."); <br />　　　　　　　//打印出错信息 <br />　　　　} <br />　　} <br />　　public void run() //线程主体 <br />　　{ <br />　　　　while(moreQuotes) { <br />　　　　　try{ <br />　　　　　　　byte[] buf=new byte[256]; //创建缓冲区 <br />　　　　　　　DatagramPacket packet=new DatagramPacket(buf,buf.length); <br />　　　　　　　//由缓冲区构造DatagramPacket对象 <br />　　　　　　　socket.receive(packet); //接收数据报 <br />　　　　　　　String dString=null; <br />　　　　　　　if(in= =null) dString=new Date().toString(); <br />　　　　　　　//如果初始化的时候打开文件失败了， <br />　　　　　　　//则使用日期作为要传送的字符串 <br />　　　　　　　else dString=getNextQuote(); <br />　　　　　　　//否则调用成员函数从文件中读出字符串 <br />　　　　　　　buf=dString.getByte(); <br />　　　　　　　//把String转换成字节数组，以便传送 <br /><br />　　　　　　　InetAddress address=packet.getAddress(); <br />　　　　　　　//从Client端传来的Packet中得到Client地址 <br />　　　　　　　int port=packet.getPort(); //和端口号 <br />　　　　　　　packet=new DatagramPacket(buf,buf.length,address,port); <br />　　　　　　　//根据客户端信息构建DatagramPacket <br />　　　　　　　socket.send(packet); //发送数据报 <br />　　　　　　}catch(IOException e) { //异常处理 <br />　　　　　　　e.printStackTrace(); //打印错误栈 <br />　　　　　　　moreQuotes=false; //标志变量置false，以结束循环 <br />　　　　　　} <br />　　　　} <br />　　　　socket.close(); //关闭数据报套接字 <br />　　} <br /><br />　　protected String getNextQuotes(){ <br />　　//成员函数，从文件中读数据 <br />　　　　String returnValue=null; <br />　　　　try { <br />　　　　　　　if((returnValue=in.readLine())= =null) { <br />　　　　　　　//从文件中读一行，如果读到了文件尾 <br />　　　　　　　in.close( ); //关闭输入流 <br />　　　　　　　moreQuotes=false; <br />　　　　　　　//标志变量置false，以结束循环 <br />　　　　　　　returnValue="No more quotes. Goodbye."; <br />　　　　　　　//置返回值 <br />　　　　　　　} //否则返回字符串即为从文件读出的字符串 <br />　　　　}catch(IOEception e) { //异常处理 <br />　　　　　　　returnValue="IOException occurred in server"; <br />　　　　　　　//置异常返回值 <br />　　　　} <br />　　　　return returnValue; //返回字符串 <br />　　} <br />　　} <br /></div>可以看出使用UDP和使用TCP在程序上还是有很大的区别的。一个比较明显的区别是，UDP的Socket编程是不提供监听功能的，也就是说通信双方更为平等，面对的接口是完全一样的。但是为了用UDP实现C/S结构，在使用UDP时可以使用DatagramSocket.receive()来实现类似于监听的功能。因为receive()是阻塞的函数，当它返回时，缓冲区里已经填满了接受到的一个数据报，并且可以从该数据报得到发送方的各种信息，这一点跟 accept()是很相象的，因而可以根据读入的数据报来决定下一步的动作，这就达到了跟网络监听相似的效果。 <br /><br />　　 <br /><br />　　8.3.14 用数据报进行广播通讯 <br /><br />　　DatagramSocket只允许数据报发送一个目的地址，java.net包中提供了一个类MulticastSocket，允许数据报以广播方式发送到该端口的所有客户。MulticastSocket用在客户端，监听服务器广播来的数据。 <br /><br />　　我们对上面的程序作一些修改，利用MulticastSocket实现广播通信。新程序完成的功能是使同时运行的多个客户程序能够接收到服务器发送来的相同的信息，显示在各自的屏幕上。 <br /><br />　　1. 客户方程序:MulticastClient.java <br /><br />　　import java.io.*; <br />　　import java.net.*; <br />　　import java.util.*; <br />　　public class MulticastClient { <br />　　　　public static void main(String args[]) throws IOException <br />　　　　{ <br />　　　　　MulticastSocket socket=new MulticastSocket(4446); <br />　　　　　//创建4446端口的广播套接字 <br />　　　　　InetAddress address=InetAddress.getByName("230.0.0.1"); <br />　　　　　//得到230.0.0.1的地址信息 <br />　　　　　socket.joinGroup(address); <br />　　　　　//使用joinGroup()将广播套接字绑定到地址上 <br />　　　　　DatagramPacket packet; <br /><br />　　　　　for(int i=0;i&lt;5;i++) { <br />　　　　　　　byte[] buf=new byte[256]; <br />　　　　　　　//创建缓冲区 <br />　　　　　　　packet=new DatagramPacket(buf,buf.length); <br />　　　　　　　//创建接收数据报 <br />　　　　　　　socket.receive(packet); //接收 <br />　　　　　　　String received=new String(packet.getData()); <br />　　　　　　　//由接收到的数据报得到字节数组， <br />　　　　　　　//并由此构造一个String对象 <br />　　　　　　　System.out.println("Quote of theMoment:"+received); <br />　　　　　　　//打印得到的字符串 <br />　　　　　} //循环5次 <br />　　　　　socket.leaveGroup(address); <br />　　　　　//把广播套接字从地址上解除绑定 <br />　　　　　socket.close(); //关闭广播套接字 <br />　　　} <br />　　} <br /><br />　　2. 服务器方程序:MulticastServer.java <br /><br />　　public class MulticastServer{ <br />　　　　public static void main(String args[]) throws java.io.IOException <br />　　　　{ <br />　　　　　　new MulticastServerThread().start(); <br />　　　　　　//启动一个服务器线程 <br />　　　　} <br />　　} <br /><br />　　3. 程序MulticastServerThread.java <br /><br />　　import java.io.*; <br />　　import java.net.*; <br />　　import java.util.*; <br />　　public class MulticastServerThread extends QuoteServerThread <br />　　//从QuoteServerThread继承得到新的服务器线程类MulticastServerThread <br />　　{ <br />　　　　Private long FIVE_SECOND=5000; //定义常量，5秒钟 <br />　　　　public MulticastServerThread(String name) throws IOException <br />　　　　{ <br />　　　　　　super("MulticastServerThread"); <br />　　　　　　//调用父类，也就是QuoteServerThread的构造函数 <br />　　　　} <br /><br />　　　　public void run() //重写父类的线程主体 <br />　　　　{ <br />　　　　　while(moreQuotes) { <br />　　　　　//根据标志变量判断是否继续循环 <br />　　　　　　try{ <br />　　　　　　　　byte[] buf=new byte[256]; <br />　　　　　　　　//创建缓冲区 <br />　　　　　　　　String dString=null; <br />　　　　　　　　if(in==null) dString=new Date().toString(); <br />　　　　　　　　//如果初始化的时候打开文件失败了， <br />　　　　　　　　//则使用日期作为要传送的字符串 <br />　　　　　　　　else dString=getNextQuote(); <br />　　　　　　　　//否则调用成员函数从文件中读出字符串 <br />　　　　　　　　buf=dString.getByte(); <br />　　　　　　　　//把String转换成字节数组，以便传送send it <br />　　　　　　　　InetAddress group=InetAddress.getByName("230.0.0.1"); <br />　　　　　　　　//得到230.0.0.1的地址信息 <br />　　　　　　　　DatagramPacket packet=new DatagramPacket(buf,buf.length,group,4446); <br />　　　　　　　　//根据缓冲区，广播地址，和端口号创建DatagramPacket对象 <br />　　　　　　　　socket.send(packet); //发送该Packet <br />　　　　　　　　try{ <br />　　　　　　　　　　sleep((long)(Math.random()*FIVE_SECONDS)); <br />　　　　　　　　　　//随机等待一段时间，0～5秒之间 <br />　　　　　　　　}catch(InterruptedException e) { } //异常处理 <br />　　　　　　}catch(IOException e){ //异常处理 <br />　　　　　　　　e.printStackTrace( ); //打印错误栈 <br /><br />　　　　　　　　moreQuotes=false; //置结束循环标志 <br />　　　　　　} <br />　　　　} <br />　　　　socket.close( ); //关闭广播套接口 <br />　　　} <br />　　} <br /><br />　　至此，Java网络编程这一章已经讲解完毕。读者通过学习，应该对网络编程有了一个清晰的认识，可能对某些概念还不是十分的清楚，还是需要更多的实践来进一步掌握。编程语言的学习不同于一般的学习，及其强调实践的重要性。读者应该对URL网络编程，Socket中的TCP，UDP编程进行大量的练习才能更好的掌握本章中所提到的一些概念，才能真正学到Java网络编程的精髓！ <br /><br />　　最后几个小节所举的例子，读者务必要亲自试验一下，如果遇到问题，想办法解决之。最好能根据自己的意图加以改进。这样才能更好的理解这几个程序，理解其中所包含的编程思想。 <br /><br />　　<span id="BlogViewId">【</span>本讲小结】 <br /><br />　　本讲主要讲解了Java环境下的网络编程。因为TCP/IP协议是Java网络编程的基础知识，本讲开篇重点介绍了TCP/IP协议中的一些概念， TCP/IP协议本身是一个十分庞大的系统，用几个小节是不可能讲清楚的。所以我们只是联系实际，讲解了一些最基本的概念，帮助学生理解后面的相关内容。重点有一下几个概念：主机名，IP，端口，服务类型，TCP，UDP。 <br /><br />　　后续的内容分为两大块，一块是以URL为主线，讲解如何通过URL类和URLConnection类访问WWW网络资源，由于使用URL十分方便直观，尽管功能不是很强，还是值得推荐的一种网络编程方法，尤其是对于初学者特别容易接受。本质上讲，URL网络编程在传输层使用的还是TCP协议。 <br /><br />　　另一块是以Socket接口和C/S网络编程模型为主线，依次讲解了如何用Java实现基于TCP的C/S结构，主要用到的类有Socket，ServerSocket。以及如何用Java实现基于 UDP的C/S结构，还讨论了一种特殊的传输方式，广播方式，这种方式是UDP所特有的，主要用到的类有DatagramSocket , DatagramPacket, MulticastSocket。这一块在Java网络编程中相对而言是最难的（尽管Java在网络编程这方面已经做的够"傻瓜"了，但是网络编程在其他环境下的却是一件极为头痛的事情，再"傻瓜"还是有一定的难度），也是功能最为强大的一部分,读者应该好好研究，领悟其中的思想。 <br /><br />　　最后要强调的是要学好Java网络编程，Java语言，最重要的还是在于多多练习！ </span>
<img src ="http://www.blogjava.net/yanzhou/aggbug/79186.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanzhou/" target="_blank">周游世界</a> 2006-11-05 12:36 <a href="http://www.blogjava.net/yanzhou/archive/2006/11/05/79186.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA教程 第八讲 Java网络编程(二)</title><link>http://www.blogjava.net/yanzhou/archive/2006/11/05/79180.html</link><dc:creator>周游世界</dc:creator><author>周游世界</author><pubDate>Sun, 05 Nov 2006 03:42:00 GMT</pubDate><guid>http://www.blogjava.net/yanzhou/archive/2006/11/05/79180.html</guid><wfw:comment>http://www.blogjava.net/yanzhou/comments/79180.html</wfw:comment><comments>http://www.blogjava.net/yanzhou/archive/2006/11/05/79180.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanzhou/comments/commentRss/79180.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanzhou/services/trackbacks/79180.html</trackback:ping><description><![CDATA[
		<span id="BlogViewId">8.3 基于Socket（套接字）的低层次Java网络编程 <br /><br />　　8.3.1 Socket通讯 <br /><br />　　网络上的两个程序通过一个双向的通讯连接实现数据的交换，这个双向链路的一端称为一个Socket。Socket通常用来实现客户方和服务方的连接。Socket是TCP/IP协议的一个十分流行的编程界面，一个Socket由一个IP地址和一个端口号唯一确定。 <br /><br />　　在传统的UNIX环境下可以操作TCP/IP协议的接口不止Socket一个，Socket所支持的协议种类也不光TCP/IP一种，因此两者之间是没有必然联系的。在Java环境下，Socket编程主要是指基于TCP/IP协议的网络编程。 <br /><br />　　说Socket编程是低层次网络编程并不等于它功能不强大，恰恰相反，正因为层次低，Socket编程比基于URL的网络编程提供了更强大的功能和更灵活的控制，但是却要更复杂一些。由于Java本身的特殊性，Socket编程在Java中可能已经是层次最低的网络编程接口，在Java中要直接操作协议中更低的层次，需要使用Java的本地方法调用（JNI），在这里就不予讨论了。 <br /><br />　　8.3.2 Socket通讯的一般过 <br /><br />　　前面已经提到Socket通常用来实现C/S结构。 <br /><br />　　使用Socket进行Client/Server程序设计的一般连接过程是这样的：Server端Listen(监听)某个端口是否有连接请求， Client端向Server端发出Connect(连接)请求，Server端向Client端发回Accept（接受）消息。一个连接就建立起来了。 Server端和Client端都可以通过Send，Write等方法与对方通信。 <br /><br />　　对于一个功能齐全的Socket，都要包含以下基本结构，其工作过程包含以下四个基本的步骤： <br />　　（1） 创建Socket； <br />　　（2） 打开连接到Socket的输入/出流； <br />　　（3） 按照一定的协议对Socket进行读/写操作； <br />　　（4） 关闭Socket. <br /><br />　　第三步是程序员用来调用Socket和实现程序功能的关键步骤，其他三步在各种程序中基本相同。 <br /><br />　　以上4个步骤是针对TCP传输而言的，使用UDP进行传输时略有不同，在后面会有具体讲解。 <br /><br />　　8.3.3 创建Socket <br /><br />　　java在包java.net中提供了两个类Socket和ServerSocket，分别用来表示双向连接的客户端和服务端。这是两个封装得非常好的类，使用很方便。其构造方法如下： <br />　　Socket(InetAddress address, int port); <br />　　Socket(InetAddress address, int port, boolean stream); <br />　　Socket(String host, int prot); <br />　　Socket(String host, int prot, boolean stream); <br />　　Socket(SocketImpl impl) <br />　　Socket(String host, int port, InetAddress localAddr, int localPort) <br />　　Socket(InetAddress address, int port, InetAddress localAddr, int localPort) <br />　　ServerSocket(int port); <br />　　ServerSocket(int port, int backlog); <br />　　ServerSocket(int port, int backlog, InetAddress bindAddr) <br /><br />　　其中address、host和port分别是双向连接中另一方的IP地址、主机名和端口号，stream指明socket是流socket还是数据报 socket，localPort表示本地主机的端口号，localAddr和bindAddr是本地机器的地址（ServerSocket的主机地址），impl是socket的父类，既可以用来创建serverSocket又可以用来创建Socket。count则表示服务端所能支持的最大连接数。例如： <br />　　Socket client = new Socket("127.0.01.", 80); <br />　　ServerSocket server = new ServerSocket(80); <br /><br />　　注意，在选择端口时，必须小心。每一个端口提供一种特定的服务，只有给出正确的端口，才能获得相应的服务。0~1023的端口号为系统所保留，例如 http服务的端口号为80,telnet服务的端口号为21,ftp服务的端口号为23, 所以我们在选择端口号时，最好选择一个大于1023的数以防止发生冲突。 <br /><br />　　在创建socket时如果发生错误，将产生IOException，在程序中必须对之作出处理。所以在创建Socket或ServerSocket是必须捕获或抛出例外。 <br /><br />　　8.3.4 客户端的Socket <br /><br />　　下面是一个典型的创建客户端Socket的过程。 <br />　　　try{ <br />　　　　　Socket socket=new Socket("127.0.0.1",4700); <br />　　　　　//127.0.0.1是TCP/IP协议中默认的本机地址 <br />　　　}catch(IOException e){ <br />　　　　　System.out.println("Error:"+e); <br />　　　} <br /><br />　　这是最简单的在客户端创建一个Socket的一个小程序段，也是使用Socket进行网络通讯的第一步，程序相当简单，在这里不作过多解释了。在后面的程序中会用到该小程序段。 <br /><br /><br />　　8.3.5 <a href="http://www.it.com.cn/server/"><u><font color="#0000ff">服务器</font></u></a>端的ServerSocket <br /><br />　　下面是一个典型的创建Server端ServerSocket的过程。 <br />　　ServerSocket server=null; <br />　　try { <br />　　　　　server=new ServerSocket(4700); <br />　　　　　//创建一个ServerSocket在端口4700监听客户请求 <br />　　}catch(IOException e){ <br />　　　　　System.out.println("can not listen to :"+e); <br />　　} <br />　　Socket socket=null; <br />　　try { <br />　　　　socket=server.accept(); <br />　　　　//accept()是一个阻塞的方法，一旦有客户请求，它就会返回一个Socket对象用于同客户进行交互 <br />　　}catch(IOException e){ <br />　　　　System.out.println("Error:"+e); <br />　　} <br /><br />　　以上的程序是Server的典型工作模式，只不过在这里Server只能接收一个请求，接受完后Server就退出了。实际的应用中总是让它不停的循环接收，一旦有客户请求，Server总是会创建一个服务线程来服务新来的客户，而自己继续监听。程序中accept()是一个阻塞函数，所谓阻塞性方法就是说该方法被调用后，将等待客户的请求，直到有一个客户启动并请求连接到相同的端口，然后accept()返回一个对应于客户的socket。这时，客户方和服务方都建立了用于通信的socket，接下来就是由各个socket分别打开各自的输入/输出流。 <br /><br />　　8.3.6 打开输入/出流 <br /><br />　　类Socket提供了方法getInputStream ()和getOutStream()来得到对应的输入/输出流以进行读/写操作，这两个方法分别返回InputStream和OutputSteam类对象。为了便于读/写数据，我们可以在返回的输入/输出流对象上建立过滤流，如DataInputStream、DataOutputStream或 PrintStream类对象，对于文本方式流对象，可以采用InputStreamReader和OutputStreamWriter、 PrintWirter等处理。 <br /><br />　　例如： <br />　　PrintStream os=new PrintStream(new BufferedOutputStreem(socket.getOutputStream())); <br />　　DataInputStream is=new DataInputStream(socket.getInputStream()); <br />　　PrintWriter out=new PrintWriter(socket.getOutStream(),true); <br />　　BufferedReader in=new ButfferedReader(new InputSteramReader(Socket.getInputStream())); <br /><br />　　输入输出流是网络编程的实质性部分，具体如何构造所需要的过滤流，要根据需要而定，能否运用自如主要看读者对Java中输入输出部分掌握如何。 <br /><br />　　8.3.7 关闭Socket <br /><br />　　每一个Socket存在时，都将占用一定的资源，在Socket对象使用完毕时，要其关闭。关闭Socket可以调用Socket的Close（）方法。在关闭Socket之前，应将与Socket相关的所有的输入/输出流全部关闭，以释放所有的资源。而且要注意关闭的顺序，与Socket相关的所有的输入/输出该首先关闭，然后再关闭Socket。 <br />　　os.close(); <br />　　is.close(); <br />　　socket.close(); <br /><br />　　尽管Java有自动回收机制，网络资源最终是会被释放的。但是为了有效的利用资源，建议读者按照合理的顺序主动释放资源。 <br /><br />　　8.3.8 简单的Client/Server程序设计 <br /><br />　　下面我们给出一个用Socket实现的客户和服务器交互的典型的C/S结构的演示程序，读者通过仔细阅读该程序，会对前面所讨论的各个概念有更深刻的认识。程序的意义请参考注释。 <br /><br />　　1. 客户端程序 <br /><br />　　import java.io.*; <br />　　import java.net.*; <br />　　public class TalkClient { <br />　　　　public static void main(String args[]) { <br />　　　　　　try{ <br />　　　　　　　　Socket socket=new Socket("127.0.0.1",4700); <br />　　　　　　　　//向本机的4700端口发出客户请求 <br />　　　　　　　　BufferedReader sin=new BufferedReader(new InputStreamReader(System.in)); <br />　　　　　　　　//由系统标准输入设备构造BufferedReader对象 <br />　　　　　　　　PrintWriter os=new PrintWriter(socket.getOutputStream()); <br />　　　　　　　　//由Socket对象得到输出流，并构造PrintWriter对象 <br />　　　　　　　　BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream())); <br />　　　　　　　　//由Socket对象得到输入流，并构造相应的BufferedReader对象 <br />　　　　　　　　String readline; <br />　　　　　　　　readline=sin.readLine(); //从系统标准输入读入一字符串 <br />　　　　　　　　while(!readline.equals("bye")){ <br />　　　　　　　　//若从标准输入读入的字符串为 "bye"则停止循环 <br />　　　　　　　　　　os.println(readline); <br />　　　　　　　　　　//将从系统标准输入读入的字符串输出到Server <br />　　　　　　　　　　os.flush(); <br />　　　　　　　　　　//刷新输出流，使Server马上收到该字符串 <br />　　　　　　　　　　System.out.println("Client:"+readline); <br />　　　　　　　　　　//在系统标准输出上打印读入的字符串 <br />　　　　　　　　　　System.out.println("Server:"+is.readLine()); <br />　　　　　　　　　　//从Server读入一字符串，并打印到标准输出上 <br />　　　　　　　　　　readline=sin.readLine(); //从系统标准输入读入一字符串 <br />　　　　　　　　} //继续循环 <br />　　　　　　　　os.close(); //关闭Socket输出流 <br />　　　　　　　　is.close(); //关闭Socket输入流 <br />　　　　　　　　socket.close(); //关闭Socket <br />　　　　　　}catch(Exception e) { <br />　　　　　　　　System.out.println("Error"+e); //出错，则打印出错信息 <br />　　　　　　} <br />　　} <br />　　} 2. 服务器端程序 <br /><br />　　import java.io.*; <br />　　import java.net.*; <br />　　import java.applet.Applet; <br />　　public class TalkServer{ <br />　　　　public static void main(String args[]) { <br />　　　　　　try{ <br />　　　　　　　　ServerSocket server=null; <br />　　　　　　　　try{ <br />　　　　　　　　　　server=new ServerSocket(4700); <br />　　　　　　　　//创建一个ServerSocket在端口4700监听客户请求 <br />　　　　　　　　}catch(Exception e) { <br />　　　　　　　　　　System.out.println("can not listen to:"+e); <br />　　　　　　　　//出错，打印出错信息 <br />　　　　　　　　} <br /><br />　　　　　　　　Socket socket=null; <br />　　　　　　　　try{ <br />　　　　　　　　　　socket=server.accept(); <br />　　　　　　　　　　//使用accept()阻塞等待客户请求，有客户 <br />　　　　　　　　　　//请求到来则产生一个Socket对象，并继续执行 <br />　　　　　　　　}catch(Exception e) { <br />　　　　　　　　　　System.out.println("Error."+e); <br />　　　　　　　　　　//出错，打印出错信息 <br />　　　　　　　　} <br />　　　　　　　　String line; <br />　　　　　　　　BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream())); <br />　　　　　　　　　//由Socket对象得到输入流，并构造相应的BufferedReader对象 <br />　　　　　　　　PrintWriter os=newPrintWriter(socket.getOutputStream()); <br />　　　　　　　　　//由Socket对象得到输出流，并构造PrintWriter对象 <br />　　　　　　　　BufferedReader sin=new BufferedReader(new InputStreamReader(System.in)); <br />　　　　　　　　　//由系统标准输入设备构造BufferedReader对象 <br /><br />　　　　　　　　System.out.println("Client:"+is.readLine()); <br />　　　　　　　　//在标准输出上打印从客户端读入的字符串 <br />　　　　　　　　line=sin.readLine(); <br />　　　　　　　　//从标准输入读入一字符串 <br />　　　　　　　　while(!line.equals("bye")){ <br />　　　　　　　　//如果该字符串为 "bye"，则停止循环 <br />　　　　　　　　　　os.println(line); <br />　　　　　　　　　　//向客户端输出该字符串 <br />　　　　　　　　　　os.flush(); <br />　　　　　　　　　　//刷新输出流，使Client马上收到该字符串 <br />　　　　　　　　　　System.out.println("Server:"+line); <br />　　　　　　　　　　//在系统标准输出上打印读入的字符串 <br />　　　　　　　　　　System.out.println("Client:"+is.readLine()); <br />　　　　　　　　　　//从Client读入一字符串，并打印到标准输出上 <br />　　　　　　　　　　line=sin.readLine(); <br />　　　　　　　　　　//从系统标准输入读入一字符串 <br />　　　　　　　　} 　//继续循环 <br />　　　　　　　　os.close(); //关闭Socket输出流 <br />　　　　　　　　is.close(); //关闭Socket输入流 <br />　　　　　　　　socket.close(); //关闭Socket <br />　　　　　　　　server.close(); //关闭ServerSocket <br />　　　　　　}catch(Exception e){ <br />　　　　　　　　System.out.println("Error:"+e); <br />　　　　　　　　//出错，打印出错信息 <br />　　　　　　} <br />　　　　} <br />　　} <br /><br />　　从上面的两个程序中我们可以看到，socket四个步骤的使用过程。读者可以分别将Socket使用的四个步骤的对应程序段选择出来，这样便于读者对socket的使用有进一步的了解。 <br /><br />　　读者可以在单机上试验该程序，最好是能在真正的网络环境下试验该程序，这样更容易分辨输出的内容和客户机，服务器的对应关系。同时也可以修改该程序，提供更为强大的功能，或更加满足读者的意图。 <br /><br />　　 <br /><br />　　8.3.9 支持多客户的client/server程序设计 <br /><br />　　前面提供的Client/Server程序只能实现Server和一个客户的对话。在实际应用中，往往是在服务器上运行一个永久的程序，它可以接收来自其他多个客户端的请求，提供相应的服务。为了实现在服务器方给多个客户提供服务的功能，需要对上面的程序进行改造，利用多线程实现多客户机制。服务器总是在指定的端口上监听是否有客户请求，一旦监听到客户请求，服务器就会启动一个专门的服务线程来响应该客户的请求，而服务器本身在启动完线程之后马上又进入监听状态，等待下一个客户的到来。 <br /><br />　　客户端的程序和上面程序是完全一样的，读者如果仔细阅读过上面的程序，可以跳过不读，把主要精力集中在Server端的程序上。 <br /><br />　　2. 服务器端程序: MultiTalkServer.java <br /><br />　　import java.io.*; <br />　　import java.net.*; <br />　　import ServerThread; <br />　　public class MultiTalkServer{ <br />　　　static int clientnum=0; //静态成员变量，记录当前客户的个数 <br />　　　public static void main(String args[]) throws IOException { <br />　　　　ServerSocket serverSocket=null; <br />　　　　boolean listening=true; <br />　　　　try{ <br />　　　　　　serverSocket=new ServerSocket(4700); <br />　　　　　　//创建一个ServerSocket在端口4700监听客户请求 <br />　　　　}catch(IOException e) { <br />　　　　　　System.out.println("Could not listen on port:4700."); <br />　　　　　　//出错，打印出错信息 <br />　　　　　　System.exit(-1); //退出 <br />　　　　} <br />　　　　while(listening){ //永远循环监听 <br />　　　　　　new ServerThread(serverSocket.accept(),clientnum).start(); <br />　　　　　　//监听到客户请求，根据得到的Socket对象和 <br />　　　　　　　客户计数创建服务线程，并启动之 <br />　　　　　　clientnum++; //增加客户计数 <br />　　　　} <br />　　　　serverSocket.close(); //关闭ServerSocket <br />　　} <br />　　} <br /><br />　　3. 程序ServerThread.java <br /><br />　　import java.io.*; <br />　　import java.net.*; <br />　　public class ServerThread extends Thread{ <br />　　　Socket socket=null; //保存与本线程相关的Socket对象 <br />　　　int clientnum; //保存本进程的客户计数 <br />　　　public ServerThread(Socket socket,int num) { //构造函数 <br />　　　　this.socket=socket; //初始化socket变量 <br />　　　　clientnum=num+1; //初始化clientnum变量 <br />　　　} <br />　　　public void run() { //线程主体 <br />　　　　try{ <br />　　　　　　String line; <br />　　　　　　BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream())); <br />　　//由Socket对象得到输入流，并构造相应的BufferedReader对象 <br />　　　　　　PrintWriter os=newPrintWriter(socket.getOutputStream()); <br />　　　　　　//由Socket对象得到输出流，并构造PrintWriter对象 <br />　　　　　　BufferedReader sin=new BufferedReader(new InputStreamReader(System.in)); <br />　　　　　　//由系统标准输入设备构造BufferedReader对象 <br />　　　　　　System.out.println("Client:"+ clientnum +is.readLine()); <br />　　　　　　//在标准输出上打印从客户端读入的字符串 <br />　　　　　　line=sin.readLine(); <br />　　　　　　//从标准输入读入一字符串 <br />　　　　　　while(!line.equals("bye")){ <br />　　　　　　//如果该字符串为 "bye"，则停止循环 <br />　　　　　　　　os.println(line); <br />　　　　　　　　//向客户端输出该字符串 <br />　　　　　　　　os.flush(); <br />　　　　　　　　//刷新输出流，使Client马上收到该字符串 <br />　　　　　　　　System.out.println("Server:"+line); <br />　　　　　　　　//在系统标准输出上打印该字符串 <br />　　　　　　　　System.out.println("Client:"+ clientnum +is.readLine()); <br />　　　　　　　　//从Client读入一字符串，并打印到标准输出上 <br />　　　　　　　　line=sin.readLine(); <br />　　　　　　　　//从系统标准输入读入一字符串 <br />　　　　　　} //继续循环 <br />　　　　　　os.close(); //关闭Socket输出流 <br />　　　　　　is.close(); //关闭Socket输入流 <br />　　　　　　socket.close(); //关闭Socket <br />　　　　　　server.close(); //关闭ServerSocket <br />　　　　　}catch(Exception e){ <br />　　　　　　System.out.println("Error:"+e); <br />　　　　　　//出错，打印出错信息 <br />　　　　　} <br />　　　} <br />　　} <br /><br />　　通过以上的学习，读者应该对Java的面向流的网络编程有了一个比较全面的认识，这些都是基于TCP的应用，后面我们将介绍基于UDP的Socket编程</span>
<img src ="http://www.blogjava.net/yanzhou/aggbug/79180.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanzhou/" target="_blank">周游世界</a> 2006-11-05 11:42 <a href="http://www.blogjava.net/yanzhou/archive/2006/11/05/79180.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>PPP介绍</title><link>http://www.blogjava.net/yanzhou/archive/2006/10/12/PPP?????.html</link><dc:creator>周游世界</dc:creator><author>周游世界</author><pubDate>Thu, 12 Oct 2006 06:16:00 GMT</pubDate><guid>http://www.blogjava.net/yanzhou/archive/2006/10/12/PPP?????.html</guid><wfw:comment>http://www.blogjava.net/yanzhou/comments/74780.html</wfw:comment><comments>http://www.blogjava.net/yanzhou/archive/2006/10/12/PPP?????.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanzhou/comments/commentRss/74780.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanzhou/services/trackbacks/74780.html</trackback:ping><description><![CDATA[
		<b>一、介绍</b>
		<br />
		<br />
 PPP（Point-to-Point
Protocol点到点协议）是为在同等单元之间传输数据包这样的简单链路设计的链路层协议。这种链路提供全双工操作，并按照顺序传递数据包。设计目的主
要是用来通过拨号或专线方式建立点对点连接发送数据，使其成为各种主机、网桥和路由器之间简单连接的一种共通的解决方案。
<br /><br /><b>  二、PPP链路建立过程
</b><br /><br />

PPP协议中提供了一整套方案来解决链路建立、维护、拆除、上层协议协商、认证等问题。PPP协议包含这样几个部分：<br /><br />
    链路控制协议LCP（Link
Control Protocol）<br />
    网络控制协议NCP（Network Control
Protocol）<br />
    认证协议<br />
    <br />LCP负责创建，维护或终止一次物理连接。NCP是一族协议，负责解决物理连接上运行什么网络协议，以及解决上层网络协议发生的问题。
最常用的认证协议包括口令验证协议PAP（Password Authentication
Protocol）和挑战握手验证协议CHAP（Challenge-Handshake Authentication Protocol）。<br /><br />
  下面介绍PPP链路建立的过程：
<br /><br />
  PPP链路状态机如图1所示。 <br /><div align="center"><img src="http://www.net130.com/Fsmanage/RoUpimages/20041091593.gif" /><br /><div align="left">一个典型的链路建立过程分为三个阶段：创建阶段、认证阶段和网络协商阶段。<br /></div></div><br /><b>
  阶段1</b>：创建PPP链路
<br /><br />
 LCP负责创建链路。在这个阶段，将对基本的通讯方式进行选择。链路两端设备通过LCP向对方发送配置信息报文（Configure
Packets）。一旦一个配置成功信息包（Configure-Ack packet）被发送且被接收，就完成了交换，进入了LCP开启状态。
<br /><br />
  应当注意，在链路创建阶段，只是对验证协议进行选择，用户验证将在第2阶段实现。
<br /><br /><b>
  阶段2</b>：用户验证
<br /><br />
  在这个阶段，客户端会将自己的身份发送给远端的接入服务器。该阶段使用一种安全验证方式避免第三方窃取数据或冒充远程客户接管与客户端的连接。在认证完成之前，禁止从认证阶段前进到网络层协议阶段。如果认证失败，认证者应该跃迁到链路终止阶段。
<br /><br />
  在这一阶段里，只有链路控制协议、认证协议，和链路质量监视协议的packets是被允许的。在该阶段里接收到的其他的packets必须被静静的丢弃。
<br /><br />
  最常用的认证协议有口令验证协议（PAP）和挑战握手验证协议（CHAP）。 认证方式介绍在第三部分中介绍。 
<br /><b><br />

  阶段3</b>：调用网络层协议
<br /><br />
  认证阶段完成之后，PPP将调用在链路创建阶段（阶段1）选定的各种网络控制协议（NCP）。选定的NCP解决PPP链路之上的高层协议问题，例如，在该阶段IP控制协议（IPCP）可以向拨入用户分配动态地址。
<br /><br />
  这样，经过三个阶段以后，一条完整的PPP链路就建立起来了。
<br /><br /><b>
  三、 认证方式</b><br /><br />
  1）口令验证协议（PAP） 
<br /><br />
 PAP是一种简单的明文验证方式。NAS（网络接入服务器，Network Access
Server）要求用户提供用户名和口令，PAP以明文方式返回用户信息。很明显，这种验证方式的安全性较差，第三方可以很容易的获取被传送的用户名和口
令，并利用这些信息与NAS建立连接获取NAS提供的所有资源。所以，一旦用户密码被第三方窃取，PAP无法提供避免受到第三方攻击的保障措施。
<br /><br />
  2）挑战-握手验证协议（CHAP） 
<br /><br />

CHAP是一种加密的验证方式，能够避免建立连接时传送用户的真实密码。NAS向远程用户发送一个挑战口令（challenge），其中包括会话ID和一
个任意生成的挑战字串（arbitrary challengestring）。远程客户必须使用MD5单向哈希算法（one-way hashing
algorithm）返回用户名和加密的挑战口令，会话ID以及用户口令，其中用户名以非哈希方式发送。
<br /><br />

CHAP对PAP进行了改进，不再直接通过链路发送明文口令，而是使用挑战口令以哈希算法对口令进行加密。因为服务器端存有客户的明文口令，所以服务器可
以重复客户端进行的操作，并将结果与用户返回的口令进行对照。CHAP为每一次验证任意生成一个挑战字串来防止受到再现攻击（replay
attack）。在整个连接过程中，CHAP将不定时的向客户端重复发送挑战口令，从而避免第3方冒充远程客户（remote client
impersonation）进行攻击。
<br /><br /><b>
  四、PPP协议的应用
</b><br /><br />
  PPP协议是目前广域网上应用最广泛的协议之一，它的优点在于简单、具备用户验证能力、可以解决IP分配等。 
<br /><br />
 家庭拨号上网就是通过PPP在用户端和运营商的接入服务器之间建立通信链路。
目前，宽带接入正在成为取代拨号上网的趋势，在宽带接入技术日新月异的今天，PPP也衍生出新的应用。典型的应用是在ADSL（非对称数据用户环线，
Asymmetrical Digital Subscriber
Loop）接入方式当中，PPP与其他的协议共同派生出了符合宽带接入要求的新的协议，如PPPoE（PPP over
Ethernet），PPPoA（PPP over ATM）。 <br /><br />
  利用以太网（Ethernet）资源，在以太网上运行PPP来进行用户认证接入的方式称为PPPoE。PPPoE即保护了用户方的以太网资源，又完成了ADSL的接入要求，是目前ADSL接入方式中应用最广泛的技术标准。 
<br /><br />
 同样，在ATM（异步传输模式，Asynchronous Transfer
Mode）网络上运行PPP协议来管理用户认证的方式称为PPPoA。它与PPPoE的原理相同，作用相同；不同的是它是在ATM网络上，而PPPoE是
在以太网网络上运行，所以要分别适应ATM标准和以太网标准。 <br /><br />
  PPP协议的简单完整使它得到了广泛的应用，相信在未来的网络技术发展中，它还可以发挥更大的作用。<img src ="http://www.blogjava.net/yanzhou/aggbug/74780.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanzhou/" target="_blank">周游世界</a> 2006-10-12 14:16 <a href="http://www.blogjava.net/yanzhou/archive/2006/10/12/PPP?????.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ] JavaEE5的新特征</title><link>http://www.blogjava.net/yanzhou/archive/2006/10/08/73716.html</link><dc:creator>周游世界</dc:creator><author>周游世界</author><pubDate>Sun, 08 Oct 2006 02:18:00 GMT</pubDate><guid>http://www.blogjava.net/yanzhou/archive/2006/10/08/73716.html</guid><wfw:comment>http://www.blogjava.net/yanzhou/comments/73716.html</wfw:comment><comments>http://www.blogjava.net/yanzhou/archive/2006/10/08/73716.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanzhou/comments/commentRss/73716.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanzhou/services/trackbacks/73716.html</trackback:ping><description><![CDATA[    2006年的Sun科技日正在上海和北京如火如荼地举行，时间分别是上海:2006年9月22 -24日，北京:2006年9月27 -29日。
回忆起一年前JavaChina2005大会的火爆场景，相信这次科技日一定能进一步激发大家学习和使用Java的激情。这次的科技日，我们部门没有参加
到Speaker的行列，希望通过在这篇Blog中和大家分享一下JavaEE5规范的最新进展和Sun开源JavaEE应用服务器--
Glassfish。
<p>　　<strong>JavaEE5规范</strong></p><p>　　2006年4月18日,JavaEE规
范专家组全票通过JavaEE5规范。5月，最新的JavaEE的规范可以从这里下载到。在J2EE1.4发布1年半之后,Java企业开发技术的业界标
准终于又向前跨了一大步。我说的这一大步不仅仅是名字从J2EE到JavaEE的变化，也
不是简单把版本从1.4升级到5。应该说，JavaEE5的发布把Java技术在企业开发的业界标准又推向一个新的高度，对一些重要的规范都本质的改动。</p><p>　
　JavaEE5规范最主要的目的就是简化开发,提高开发的效率.同时,随着JavaEE规范变得逐渐庞大，JavaEE5对已有的子规范进行了系统的分
类,分别包括:企业应用、Web应用、Web服务和管理四个方面，如此分类让开发人员更加清晰，更容易选择相关规范进行学习.</p><p>　　JavaEE5主要的新特征包括:</p><p>　　• EJB规范</p><p>　
　EJB规范在保证向后兼容性的同时,做了较大的改动,EJB规范的版本从2.1升级到3.0,在版本升级的背后包含了如下的显著特征:EJB成为轻量级
的 POJO,可以脱离容器运行和测试;EJB3.0中引入的持久化规范可以脱离JavaEE运行环境,单独运行在标准的Java应用程序中.
这也是对Java持久化规范的一个重大贡献.我的同事Wynne Wang写了一篇关于EJB3.0的Blog，里面详细介绍了其新特性。</p><p>　　• Web层规范</p><p>　
　引入了Java Server Faces(JSF) 1.2和Java Server Pages Standard Tag Library
1.2.在传统的JSP和Servlet的基础之上，这两个规范的引入也将大大简化Web层或者说是展现层的开发，其中包括通用的组件、流程控制等。</p><p>　　• 标注(Annotation)的使用</p><p>　
　标注(Annotation)是JavaSE5在Java语言上的一个重大变化，JavaEE5大量使用了标注。标注的使用可以简化JavaEE应用程
序 的发布过程，使得开发人员可以从代码和发布文件的同步中解脱出来;同时，标注也可以大大简化应用程序中资源(例如:JDBC
Connection，EJB远程接口)的查找过程，使得开发人员不用过多考虑系统环境的问题，专注于业务的开发。</p><p>　　值得一提的是，不可
否认，Java企业开发技术的业界标准也吸收了很多大家耳熟能详的框架的设计思想(包括Hibernate, Spring,
Toplink和JDO等等)，例如，Hibernate的创始人Gavin
King是EJB3.0规范的专家成员之一。我想从这一点也可以看出JavaEE的业界标准是一个开放的标准。</p><p>　　<strong>Glassfish---Sun开源JavaEE应用服务器</strong></p><p>　　Sun公司不仅仅是开发规范制定的领导者，同时也在规范的实现和推广上也做了很大的努力和贡献。从JavaEE参考实现到产品级别的应用服务器(Sun Java System Application Server)，Sun都一套完整的产品线。</p><p>　　2005年6月,JavaOne大会上Sun开始了GlassFish项目。GlassFish项目使用CDDL规范.开发者可以查看、使用、修改和发布Glassfish的代码。</p><p>　
　Sun公司把其商用应用服务器的代码贡献到GlassFish项目中,Oracle也贡献了Toplink的代码实现EJB的持久化功能。在正在开发中
Glassfish V2版本中，还将包括集群等特性。
虽然知道Glassfish的开发人员不是很多，但是我想，就象Sun的另一个开源产品NetBeansJavaIDE工具一样，在不久的将来一定会有越
来越多的人使用Glassfish!</p><p>　　如果您还在犹豫，可以看看这篇Blog:Why to use Glassfish? 相信你看后一定会跃跃欲试。</p><p>　　让我们跟上时代的步伐，不要成为JavaEE5的淘汰者!</p><img src ="http://www.blogjava.net/yanzhou/aggbug/73716.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanzhou/" target="_blank">周游世界</a> 2006-10-08 10:18 <a href="http://www.blogjava.net/yanzhou/archive/2006/10/08/73716.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ] JSF详解(从jsf的运行原理开始介绍了jsf)</title><link>http://www.blogjava.net/yanzhou/archive/2006/09/30/73012.html</link><dc:creator>周游世界</dc:creator><author>周游世界</author><pubDate>Sat, 30 Sep 2006 05:23:00 GMT</pubDate><guid>http://www.blogjava.net/yanzhou/archive/2006/09/30/73012.html</guid><wfw:comment>http://www.blogjava.net/yanzhou/comments/73012.html</wfw:comment><comments>http://www.blogjava.net/yanzhou/archive/2006/09/30/73012.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/yanzhou/comments/commentRss/73012.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanzhou/services/trackbacks/73012.html</trackback:ping><description><![CDATA[
		<strong>
				<span class="tpc_title">JSF详解(从jsf的运行原理开始介绍了jsf)</span>
				<br />
				<br />
		</strong>
		<span class="tpc_content">
				<font size="2">1．   结构： <br />a)     结构图：<br /><b></b><br /><img onclick="if(this.width&gt;=780) window.open('http://www.agilejava.org/java/attachment/2_575_0a1f4f09192ac37.jpg');" src="http://www.agilejava.org/java/attachment/2_575_0a1f4f09192ac37.jpg" onload="if(this.width&gt;'780')this.width='780';if(this.height&gt;'1680')this.height='1680';" border="0" />     <br />b)     说明：JSF以MVC模式为基础，与Struts不同，JSF的目标是希望以一个与Swing相类似的方式来开发网页，因此，从JSF的结构图当中，他的核心概念不是页面，而是控件树，也就是说，当用户提交一个请求时，JSF会先将页面上的组件先转换为与Swing当中类似的，由容器和控件组成的控件树，然后数据和事件被设置到对应的控件上，然后以一种与Swing类似的方式，来处理后续的请求。控件树是整个JSF的核心，所有其他的一切一切都是围绕着这棵控件树展开的。 <br />2．   生命周期： <br />a)     周期图：<br /><b></b><br /><img onclick="if(this.width&gt;=780) window.open('http://www.agilejava.org/java/attachment/2_575_ebec8869e275066.jpg');" src="http://www.agilejava.org/java/attachment/2_575_ebec8869e275066.jpg" onload="if(this.width&gt;'780')this.width='780';if(this.height&gt;'1680')this.height='1680';" border="0" />   <br />  b)     说明： <br />  b)     说明： <br />  b)     说明： <br />              i.         Restore View：JSF的处理核心是控件树，他会先将页面上所声明的控件转换为一棵控件树，后续的操作将在这颗控件树上进行。为了提高性能，系统会为之前生成的控件树提供缓存。Restore View的工作就是在缓存当中查找是否存在之前已经生成好的控件树，如果没有，则根据页面的内容，重新生成。 <br />            ii.         Apply Request Values：把请求当中的数据设置到控件树当中对应的控件当中去。 <br />          iii.         Process Validations：如果某一控件有配置Validator，则这些Validator将对刚设置的数据的正确性和合法性进行验证。 <br />            iv.         Update Model Values：控件树上的控件更新其底层所对应的模型。 <br />              v.         Invoke Application：对产生的事件进行分发。 <br />            vi.         Render Response：构建作为响应的控件树。 <br />3．   UI： <br />a)     结构图：<br /><b></b><br /><img onclick="if(this.width&gt;=780) window.open('http://www.agilejava.org/java/attachment/2_575_3747cecf87276cf.jpg');" src="http://www.agilejava.org/java/attachment/2_575_3747cecf87276cf.jpg" onload="if(this.width&gt;'780')this.width='780';if(this.height&gt;'1680')this.height='1680';" border="0" /><br /><br />b)     控件： <br />              i.         说明：JSF通过标签库，提供了一些主要控件的实现。包括标签，文本框，单选框，列表等。由于JSF使用一种类似于UI的方式来组织组件，所以，除了基本的组件以外，还提供了一些用于布局的容器，例如面板等。在这里有一个要注意的地方就是，一般情况下，页面的内容应该放到JSF提供的view标签里面。 <br />            ii.         代码： <br />&lt;%@page contentType="text/html;charset=gb2312"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/core" target="_blank">
						<font size="2">http://java.sun.com/jsf/core</font>
				</a>
				<font size="2">" prefix="f"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/html" target="_blank">
						<font size="2">http://java.sun.com/jsf/html</font>
				</a>
				<font size="2">" prefix="h"%&gt; <br />&lt;html&gt; <br />&lt;head&gt;&lt;title&gt;Test&lt;/title&gt;&lt;/head&gt; <br />&lt;body&gt; <br />&lt;f:view&gt; <br />&lt;h:form&gt; <br />&lt;h:panelGrid columns="1"&gt; <br />&lt;h:outputLabel&gt; <br />&lt;h:outputText value="User ID"/&gt; <br />&lt;/h:outputLabel&gt; <br />&lt;/h:panelGrid&gt; <br />&lt;/h:form&gt; <br />&lt;/f:view&gt; <br />&lt;/body&gt; <br />&lt;/html&gt; <br />c)     事件处理： <br />              i.         说明：与Struts不同，由于JSF使用以控件树为中心的方式来处理请求，所以，她提供了一种额外的类似Swing的，事件处理的方式来处理用户的输入事件。JSF提供了两种事件类型，ActionEvent，用于处理命令和ValueChangeEvent，用于处理数据更改。 <br />            ii.         代码： <br />模型代码： <br />package nick; <br />public class UserActionListener implements ActionListener { <br />  public void processAction(ActionEvent arg0) <br />throws AbortProcessingException { <br />    FacesContext context = FacesContext.getCurrentInstance(); <br />    ValueBinding binding = Util.getValueBinding("#{user}"); <br />    User user = (User) binding.getValue(context); <br />    String id = user.getId(); <br />  } <br />} <br />页面代码： <br />&lt;%@page contentType="text/html;charset=gb2312" import="java.util.*"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/core" target="_blank">
						<font size="2">http://java.sun.com/jsf/core</font>
				</a>
				<font size="2">" prefix="f"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/html" target="_blank">
						<font size="2">http://java.sun.com/jsf/html</font>
				</a>
				<font size="2">" prefix="h"%&gt; <br />&lt;html&gt; <br />&lt;head&gt;&lt;title&gt;Test&lt;/title&gt;&lt;/head&gt; <br />&lt;body&gt; <br />&lt;f:view&gt; <br />&lt;h:form&gt; <br />&lt;h:panelGrid columns="2"&gt; <br />&lt;h:outputLabel for="id"&gt; <br />&lt;h:outputText value="User ID"/&gt; <br />&lt;/h:outputLabel&gt; <br />&lt;h:commandButton id="regist" value="注册"&gt; <br />&lt;f:actionListener type="nick.UserActionListener"/&gt; <br />&lt;/h:commandButton&gt; <br />&lt;/h:panelGrid&gt; <br />&lt;/h:form&gt; <br />&lt;/f:view&gt; <br />&lt;/body&gt; <br />&lt;/html&gt; <br />注：通过嵌套actionListener标签，我们可以为一个控件注册监视器。 <br /><br />4．   数据绑定： <br />a)     说明：数据绑定要解决的问题就是如何把模型中的值，绑定到页面的控件上。在JSF当中这可以通过JSF所提供的配置文件来完成。 <br />b)     代码： <br />配置文件： <br />&lt;faces-config&gt; <br />&lt;managed-bean&gt; <br />&lt;managed-bean-name&gt; user &lt;/managed-bean-name&gt; <br />&lt;managed-bean-class&gt; nick.User &lt;/managed-bean-class&gt; <br />&lt;managed-bean-scope&gt; session &lt;/managed-bean-scope&gt; <br />&lt;/managed-bean&gt; <br />&lt;/faces-config&gt; <br />模型代码： <br />package nick; <br />public class User { <br />  private String id = "Nick"; <br />public void setId(String id) { <br />    this.id = id; <br />  } <br />  public String getId() { <br />    return this.id; <br />  } <br />} <br />页面代码： <br />&lt;%@page contentType="text/html;charset=gb2312"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/core" target="_blank">
						<font size="2">http://java.sun.com/jsf/core</font>
				</a>
				<font size="2">" prefix="f"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/html" target="_blank">
						<font size="2">http://java.sun.com/jsf/html</font>
				</a>
				<font size="2">" prefix="h"%&gt; <br />&lt;html&gt; <br />  &lt;head&gt;&lt;title&gt;Test&lt;/title&gt;&lt;/head&gt; <br />&lt;body&gt; <br />&lt;f:view&gt; <br />&lt;h:form&gt; <br />&lt;h:panelGrid columns="2"&gt; <br />&lt;h:outputLabel for="id"&gt; <br />&lt;h:outputText value="User ID"/&gt; <br />&lt;/h:outputLabel&gt; <br />&lt;h:inputText id="id" value="#{user.id}"/&gt; <br />          &lt;/h:panelGrid&gt; <br />        &lt;/h:form&gt; <br />    &lt;/f:view&gt; <br />  &lt;/body&gt;   <br />&lt;/html&gt; <br />注：通过配置文件，我们把nick.User类绑定到名称user上，然后页面的代码就可以直接使用#{user.xxx}来引用User这个类中的各个字段。 <br />5．   页面流： <br />a)     页面到控制器： <br />              i.         说明：JSF通过使用方法绑定的方式来定义从页面到控制器的跳转，和数据绑定相同，为了能够正确找到被绑定方法所在的类，我们需要首先在配置文件当中声明managed-bean，然后通过设置控件的action属性，定义页面到控制器的跳转逻辑。 <br />            ii.         代码： <br />配置文件： <br />&lt;faces-config&gt; <br />&lt;managed-bean&gt; <br />&lt;managed-bean-name&gt; user &lt;/managed-bean-name&gt; <br />&lt;managed-bean-class&gt; nick.User &lt;/managed-bean-class&gt; <br />&lt;managed-bean-scope&gt; session &lt;/managed-bean-scope&gt; <br />&lt;/managed-bean&gt; <br />&lt;/faces-config&gt; <br /><br /><br /><br />模型代码： <br />package nick; <br />public class User { <br />  public String regist() { <br />    return "regist"; <br />  } <br />  public String login() { <br />    return "login"; <br />  } <br />} <br />页面代码： <br />&lt;%@page contentType="text/html;charset=gb2312"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/core" target="_blank">
						<font size="2">http://java.sun.com/jsf/core</font>
				</a>
				<font size="2">" prefix="f"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/html" target="_blank">
						<font size="2">http://java.sun.com/jsf/html</font>
				</a>
				<font size="2">" prefix="h"%&gt; <br />&lt;html&gt; <br />&lt;head&gt;&lt;title&gt;Test&lt;/title&gt;&lt;/head&gt; <br />&lt;body&gt; <br />&lt;f:view&gt; <br />&lt;h:form&gt; <br />&lt;h:panelGrid columns="2"&gt; <br />&lt;h:commandButton id="regist" <br />action="#{user.regist}" value="注册"/&gt; <br />&lt;h:commandButton id="login" <br />action="#{user.login}" value="登陆"/&gt; <br />&lt;/h:panelGrid&gt; <br />&lt;/h:form&gt; <br />&lt;/f:view&gt; <br />&lt;/body&gt; <br />&lt;/html&gt; <br />注：上述的页面代码，把注册按钮的动作绑定到User类的regist()方法，把登陆按钮的动作绑定到User类的login()方法。因此，当这两个按钮被点击时，对应的方法将被调用，用于实现页面流的方法，必须声明为public，而且她不接受参数，且返回值必须为String。 <br />b)     控制器到页面： <br />              i.         说明：JSF通过名称绑定的方式，来定义从控制器到页面的跳转。为了实现从控制器到页面的跳转，我们需要在配置文件当中定义一些&lt;navigation-rule&gt;，这些rule主要定义了怎么根据上述action标签所绑定的方法的返回值来查找下一页面。 <br />            ii.         代码： <br />配置文件： <br />&lt;faces-config&gt; <br />&lt;managed-bean&gt; <br />&lt;managed-bean-name&gt; user &lt;/managed-bean-name&gt; <br />&lt;managed-bean-class&gt; nick.User &lt;/managed-bean-class&gt; <br />&lt;managed-bean-scope&gt; session &lt;/managed-bean-scope&gt; <br />&lt;/managed-bean&gt; <br /><br /><br /><br />&lt;navigation-rule&gt; <br />&lt;from-view-id&gt;/index.jsp&lt;/from-view-id&gt; <br />&lt;navigation-case&gt; <br />&lt;from-outcome&gt;regist&lt;/from-outcome&gt; <br />&lt;to-view-id&gt;/regist.jsp&lt;/to-view-id&gt; <br />&lt;/navigation-case&gt; <br />&lt;navigation-case&gt; <br />&lt;from-outcome&gt;login&lt;/from-outcome&gt; <br />&lt;to-view-id&gt;/login.jsp&lt;/to-view-id&gt; <br />&lt;/navigation-case&gt; <br />&lt;/navigation-rule&gt; <br />&lt;/faces-config&gt; <br />模型代码： <br />package nick; <br />public class User { <br />  public String regist() { <br />    return "regist"; <br />  } <br />  public String login() { <br />    return "login"; <br />  } <br />} <br />注：上述的配置文件定义了一个&lt;navigation-rule&gt;，该rule指明了如果“/index.jsp”页面通过她内部的某个控件的action属性发生了跳转，那么当该跳转方法的返回值为字符串“regist”时，则页面将跳转到对应的“/regist.jsp”中，同理，如果返回值为“login”，则页面将跳转到“/login.jsp”。 <br />6．   数据传输： <br />a)     页面到控制器： <br />              i.         说明：在JSF的页面代码当中，通过数据绑定，我们把控件的value值，与某个后台的数据bean关联起来。而在前述的生命周期部分，我们看到，当一个JSF请求到达时，他需要经历Restore View，Apply Request Value等步骤，而Apply Request Value部分的工作，就是把请求当中的值绑定到这个后台的bean之中，因此，我们不需要考虑页面中的Form值如何传入到后台的bean当中。 <br />进一步，如果录入控件的value属性，和命令控件的action属性都是绑定在同一个bean上的话，那么在页面跳转时，我们可以直接访问到bean的属性值。但是为了不污染模型，和实现控制与模型的分离，一般情况下，我们需要把输入控件的value值绑定到数据bean，而把命令控件的action值绑定到控制bean，由于两个bean不是同一个，所以，控制bean需要一种方法来获取数据bean中的属性值。 <br />            ii.         代码： <br />配置文件： <br />&lt;faces-config&gt; <br />&lt;managed-bean&gt; <br />&lt;managed-bean-name&gt; user &lt;/managed-bean-name&gt; <br />&lt;managed-bean-class&gt; nick.User &lt;/managed-bean-class&gt; <br />&lt;managed-bean-scope&gt; session &lt;/managed-bean-scope&gt; <br />&lt;/managed-bean&gt; <br />&lt;managed-bean&gt; <br />&lt;managed-bean-name&gt; action &lt;/managed-bean-name&gt; <br />&lt;managed-bean-class&gt; nick.Action &lt;/managed-bean-class&gt; <br />&lt;managed-bean-scope&gt; session &lt;/managed-bean-scope&gt; <br />&lt;/managed-bean&gt; <br />&lt;/faces-config&gt; <br />模型代码： <br />        package nick; <br />public class User { <br />private String id = "Nick"; <br />public void setId(String id) { <br />this.id = id; <br />} <br />public String getId() { <br />return this.id; <br />} <br />} <br />package nick; <br />public class Action { <br />  public String regist() { <br />    ValueBinding binding = Util.getValueBinding("#{user}"); <br />    User user = (User) <br />binding.getValue(FacesContext.getCurrentInstance()); <br />            … <br />    return "regist"; <br />  } <br />} <br />页面代码： <br />&lt;%@page contentType="text/html;charset=gb2312"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/core" target="_blank">
						<font size="2">http://java.sun.com/jsf/core</font>
				</a>
				<font size="2">" prefix="f"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/html" target="_blank">
						<font size="2">http://java.sun.com/jsf/html</font>
				</a>
				<font size="2">" prefix="h"%&gt; <br />&lt;html&gt; <br />&lt;head&gt;&lt;title&gt;Test&lt;/title&gt;&lt;/head&gt; <br />&lt;body&gt; <br />&lt;f:view&gt; <br />&lt;h:form&gt; <br />&lt;h:panelGrid columns="2"&gt; <br />&lt;h:outputLabel for="id"&gt; <br />&lt;h:outputText value="User ID"/&gt; <br />&lt;/h:outputLabel&gt; <br />&lt;h:inputText id="id" value="#{user.id}"/&gt; <br />&lt;h:commandButton id="regist" <br />action="#{action.regist}" value="注册"/&gt; <br />&lt;/h:panelGrid&gt; <br />&lt;/h:form&gt; <br />&lt;/f:view&gt; <br />&lt;/body&gt; <br />&lt;/html&gt; <br /><br /><br /><br />注：页面代码当中把输入控件的value绑定到了user的id上，把命令控件的action值绑 <br />定到action的regist上。当用户点击登陆按钮时，action的regist()方法将会被调用， <br />而在该方法内部，为了获取之前页面中的信息，我们可以使用ValueBinding类。该类使用 <br />的数据绑定表达式与页面中的类似。 <br />b)     控制器到页面： <br />              i.         说明：通过之前的叙述，我们可以发现，当我们需要把数据从控制器传到页面时，我们同样可以使用数据绑定的方式，因为，当我们通过数据绑定表达式，获取到某个页面当中所使用的模型实例时，便可以在该实例上直接调用对应的set()方法，来设定所希望的值。 <br />7．   插件功能： <br />a)     Converter（转换）： <br />              i.         说明：当模型中的某个字段需要在页面上显示时，她需要先被转换为字符串类型；而用户在页面输入的字符串值，在传到模型中时也需要根据模型对应字段的类型，进行一个转换。另外，由于国际化的要去，模型中的值在不同的地区会有不同的表示方式。为了解决以上这些问题，JSF提供了Converter的实现，她主要做的事情就是根据所在的地区，对页面数据和模型数据进行双向的转换。 <br />            ii.         代码： <br />模型代码： <br />package nick; <br />public class User { <br />  private Date date = new Date(); <br />  public Date getDate() { <br />    return date; <br />  } <br />  public void setDate(Date date) { <br />    this.date = date; <br />  } <br />} <br />页面代码： <br />&lt;%@page contentType="text/html;charset=gb2312"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/core" target="_blank">
						<font size="2">http://java.sun.com/jsf/core</font>
				</a>
				<font size="2">" prefix="f"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/html" target="_blank">
						<font size="2">http://java.sun.com/jsf/html</font>
				</a>
				<font size="2">" prefix="h"%&gt; <br />&lt;html&gt; <br />&lt;head&gt;&lt;title&gt;Test&lt;/title&gt;&lt;/head&gt; <br />&lt;body&gt; <br />&lt;f:view&gt; <br />&lt;h:form&gt; <br />&lt;h:panelGrid columns="3"&gt; <br />&lt;h:outputLabel for="date"&gt; <br />&lt;h:outputText value="date"/&gt; <br />&lt;/h:outputLabel&gt; <br />&lt;h:inputText id="date" value="#{user.date}"&gt; <br />&lt;f:convertDateTime pattern="M/d/yyyy"/&gt; <br />&lt;/h:inputText&gt; <br />&lt;h:message for="date"/&gt; <br />&lt;/h:panelGrid&gt; <br />&lt;/h:form&gt; <br />&lt;/f:view&gt; <br />&lt;/body&gt; <br />&lt;/html&gt; <br />注：通过嵌套的convertXXX标签，我们可以为控件配置用于转换的转换器。我们可以使用两种方式来注册转换器，一是通过控件的convert属性，另外一种就是通过嵌套的convertXXX标签。如果在转换的时候发生错误，那么JSF将跳过转换以后的步骤，而直接跳到Render Response步骤，生成响应，并在FacesContext里添加一个出错的Message，该Message的内容可以通过message标签进行显示。<br />b)     Validate（验证）： <br />              i.         说明：在数据被交付后台处理以前，我们可以通过验证器，来验证输入的数据是否合法，这包括数值的大小，或者是字符串的长度等。使用验证器的好处就是我们可以把验证的代码从控制代码中单独出来，以便管理和重用。 <br />            ii.         代码： <br />页面代码： <br />&lt;%@page contentType="text/html;charset=gb2312"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/core" target="_blank">
						<font size="2">http://java.sun.com/jsf/core</font>
				</a>
				<font size="2">" prefix="f"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/html" target="_blank">
						<font size="2">http://java.sun.com/jsf/html</font>
				</a>
				<font size="2">" prefix="h"%&gt; <br />&lt;html&gt; <br />&lt;head&gt;&lt;title&gt;Test&lt;/title&gt;&lt;/head&gt; <br />&lt;body&gt; <br />&lt;f:view&gt; <br />&lt;h:form&gt; <br />&lt;h:panelGrid columns="3"&gt; <br />&lt;h:outputLabel for="id"&gt; <br />&lt;h:outputText value="User ID"/&gt; <br />&lt;/h:outputLabel&gt; <br />&lt;h:inputText id="id" value="#{user.id}"&gt; <br />&lt;f:validateLength minimum="3" maximum="6"/&gt; <br />&lt;/h:inputText&gt; <br />&lt;h:message for="id"/&gt; <br />&lt;/h:panelGrid&gt; <br />&lt;/h:form&gt; <br />&lt;/f:view&gt; <br />&lt;/body&gt; <br />&lt;/html&gt; <br />注：通过嵌套的validateXXX标签我们可以为一个控件注册一个验证器，与转换器不同，我们可以为一个控件注册多个验证器，以进行复杂的验证。注册验证器的方法也有两个，一是通过控件的validator属性，另外一种就是通过嵌套的validateXXX标签。与转换器类似，当验证失败时，失败的原因也会以Message的形式被放到FacesContext内，在页面内通过message标签可以对错误信息进行显示。 <br />8．   国际化支持 <br />a)     说明：为了提供对国际化的支持，JSF使用了与Struts类似的通过资源文件的形式，从外部获取需要显示的内容。 <br />b)     代码： <br />配置文件： <br />&lt;faces-config&gt; <br />&lt;application&gt; <br />&lt;message-bundle&gt;nick.Messages&lt;/message-bundle&gt; <br />&lt;locale-config&gt; <br />&lt;default-locale&gt;cn&lt;/default-locale&gt; <br />&lt;supported-locale&gt;en&lt;/supported-locale&gt; <br />&lt;/locale-config&gt; <br />  &lt;/application&gt; <br />&lt;/faces-config&gt; <br />页面代码： <br />&lt;%@page contentType="text/html;charset=gb2312"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/core" target="_blank">
						<font size="2">http://java.sun.com/jsf/core</font>
				</a>
				<font size="2">" prefix="f"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/html" target="_blank">
						<font size="2">http://java.sun.com/jsf/html</font>
				</a>
				<font size="2">" prefix="h"%&gt; <br />&lt;html&gt; <br />&lt;head&gt;&lt;title&gt;Test&lt;/title&gt;&lt;/head&gt; <br />&lt;f:loadBundle basename="nick.Messages" var="message"/&gt; <br />&lt;body&gt; <br />&lt;f:view&gt; <br />&lt;h:form&gt; <br />&lt;h:panelGrid columns="3"&gt; <br />&lt;h:outputLabel for="id"&gt; <br />&lt;h:outputText value="#{message.id}"/&gt; <br />&lt;/h:outputLabel&gt; <br />&lt;h:inputText id="id" value="#{user.id}"/&gt; <br />                &lt;/h:panelGrid&gt; <br />&lt;/h:form&gt; <br />&lt;/f:view&gt; <br />&lt;/body&gt; <br />&lt;/html&gt; <br />注：如果我们需要考虑国际化的问题时，那么我们需要为同一个资源提供多个地区的资源文件实现。通过在配置文件中声明message-bundle，我们可以注册资源文件的多个版本，以上面为例，她注册了两个资源文件nick.Messages_cn.properties和nickcen.Messages_en.properties分别对应于中国和英国地区，当页面当中通过loadBundle标签读取资源时，她就会根据所在的区域，去查找对应的文件。当然，所有资源文件都应该使用java所提供的native2ascii工具，进行一下预处理。 <br />9．   JSF定制： <br />a)     说明：该部分主要展示如何对JSF提供的转换器，验证器和控件进行定制，以扩展JSF的功能。 <br />b)     配置文件定制： <br />              i.         说明：JSF提供了比Struts更多的定制特性，包括用于实例化前端控制FacesServelt的faces-context-factory，用于实例化控件绘画RenderKit的render-kit-factory，进行声明周期管理的lifecyle-factory等。 <br />            ii.         代码： <br />配置文件： <br />&lt;faces-config&gt; <br />  &lt;factory&gt; <br />    &lt;application-factory&gt;…&lt;/application-factory&gt; <br />    &lt;faces-context-factory&gt;…&lt;/faces-context-factory&gt; <br />    &lt;lifecycle-factory&gt;…&lt;/lifecycle-factory&gt; <br />    &lt;render-kit-factory&gt;…&lt;/render-kit-factory&gt; <br />  &lt;/factory&gt; <br />&lt;/faces-config&gt; <br />c)     转换器定制： <br />              i.         说明：通过扩展JSF提供的Converter接口，我们可以定义自己的转换器。而为了能在页面上使用自定义的转换器，我们需要在配置文件中，对转换器进行注册。 <br />            ii.         代码： <br />配置文件： <br />&lt;faces-config&gt; <br />&lt;converter&gt; <br />&lt;converter-id&gt;nameConverter&lt;/converter-id&gt; <br />&lt;converter-class&gt;nick.NameConverter&lt;/converter-class&gt; <br />&lt;/converter&gt; <br />&lt;/faces-config&gt; <br />模型文件： <br />package nick; <br />public class NameConverter implements Converter { <br />public Object getAsObject(FacesContext ar0, UIComponent ar1, String ar2) { <br />    return ar2.toLowerCase(); <br />} <br />public String getAsString(FacesContext ar0, UIComponent ar1, Object ar2) { <br />    retrun ((String) arg2).toUpperCase(); <br />} <br />} <br />页面文件： <br />&lt;%@page contentType="text/html;charset=gb2312"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/core" target="_blank">
						<font size="2">http://java.sun.com/jsf/core</font>
				</a>
				<font size="2">" prefix="f"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/html" target="_blank">
						<font size="2">http://java.sun.com/jsf/html</font>
				</a>
				<font size="2">" prefix="h"%&gt; <br />&lt;html&gt; <br />&lt;head&gt;&lt;title&gt;Test&lt;/title&gt;&lt;/head&gt; <br />&lt;f:loadBundle basename="nick.Messages" var="message"/&gt; <br />&lt;body&gt; <br />&lt;f:view&gt; <br />&lt;h:form&gt; <br />&lt;h:panelGrid columns="3"&gt; <br />&lt;h:outputLabel for="id"&gt; <br />&lt;h:outputText value="#{message.id}"/&gt; <br />&lt;/h:outputLabel&gt; <br />&lt;h:inputText id="id" <br />value="#{user.id}" immediate="true"&gt; <br />&lt;f:converter converterId="nameConverter"/&gt; <br />&lt;/h:inputText&gt; <br />&lt;h:message for="id"/&gt; <br />&lt;h:commandButton <br />action="#{action.regist}" value="Regist"/&gt; <br />&lt;/h:panelGrid&gt; <br />&lt;/h:form&gt; <br />&lt;/f:view&gt; <br />&lt;/body&gt; <br />&lt;/html&gt; <br />注：当页面数据转换为后台数据时，Converter的getAsObject()方法会被调用。而当需要把后台数据显示到页面中时，getAsString()方法会被调用。这里，我们注意到，我们的Converter是不能带任何配置属性的，为了让我们的Converter能够在页面中增加属性，我们需要进一步的定义自己的标签。除了可以根据converter-id来注册转换器以外，我们还可以使用converter-for-class标签来为某一特定的Java类型注册转换器。如果在转换过程中发生错误的话，那么我们可以通过抛出ConverterException来停止后续的工作。 <br />d)     验证器定制： <br />              i.         说明：通过扩展Validator接口，我们可以定义自己的验证器。而为了能在页面上使用自定义的验证器，我们需要在配置文件中，对验证器进行注册。 <br />            ii.         代码： <br />配置文件： <br />&lt;faces-config&gt; <br />&lt;validator&gt; <br />&lt;validator-id&gt;nameValidator&lt;/validator-id&gt; <br />&lt;validator-class&gt;nick.NameValidator&lt;/validator-class&gt; <br />&lt;/validator&gt; <br />&lt;/faces-config&gt; <br />模型文件： <br />package nick; <br />public class NameValidator implements Validator { <br />  public void validate(FacesContext arg0, UIComponent arg1, Object arg2) <br />        throws ValidatorException { <br />    String name = (String) arg2; <br />    if (!name.startsWith("nick")) { <br />        throw new ValidatorException(new FacesMessage( <br />            "name must start with nick")); <br />    } <br />  } <br />} <br /><br /><br /><br />页面文件： <br />&lt;%@page contentType="text/html;charset=gb2312"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/core" target="_blank">
						<font size="2">http://java.sun.com/jsf/core</font>
				</a>
				<font size="2">" prefix="f"%&gt; <br />&lt;%@ taglib uri="</font>
				<a href="http://java.sun.com/jsf/html" target="_blank">
						<font size="2">http://java.sun.com/jsf/html</font>
				</a>
				<font size="2">" prefix="h"%&gt; <br />&lt;html&gt; <br />&lt;head&gt;&lt;title&gt;Test&lt;/title&gt;&lt;/head&gt; <br />&lt;f:loadBundle basename="nick.Messages" var="message"/&gt; <br />&lt;body&gt; <br />&lt;f:view&gt; <br />&lt;h:form&gt; <br />&lt;h:panelGrid columns="3"&gt; <br />&lt;h:outputLabel for="id"&gt; <br />&lt;h:outputText value="#{message.id}"/&gt; <br />&lt;/h:outputLabel&gt; <br />&lt;h:inputText id="id" value="#{user.id}"&gt;             &lt;f:validator validatorId="nameValidator"/&gt; <br />&lt;/h:inputText&gt; <br />&lt;h:message for="id"/&gt; <br />&lt;h:commandButton <br />action="#{action.regist}" value="Regist"/&gt; <br />&lt;/h:panelGrid&gt; <br />&lt;/h:form&gt; <br />&lt;/f:view&gt; <br />&lt;/body&gt; <br />&lt;/html&gt; <br />注：定制验证器的操作与定制转换器的操作大致相同，如果我们希望通过页面的方式为验证器提供属性的话，那么我们也需要定义额外的标签。 <br />e)     控件定制： <br />              i.         说明：JSF允许用户基于JSF的框架，进行控件的定制，控件定制需要完成以下几部分的工作，1.需要一个控件实现类，为了能让控件在页面上使用，我们的控件还需要在配置文件中，通过component标签进行注册；2.用于控件渲染的Render类；3.用于在页面上使用控件的标签。 <br />f)     控件树定制： <br />              i.         说明：除了可以通过页面标签来声明控件以外，我们可以通过程序的方式来定制控件树，通过调用FacesContext上的getViewRoot()方法，我们可以获得控件树的根引用，通过该引用，我们可以动态的对控件进行增删，或者动态的为某一控件增删事件监听器，和验证器等。 <br />g)     Web常量： <br />              i.         说明：JSF提供了一组操作application，context，session，cookies，page等常量的方法。通过调用FacesContext上的getExternalContext()方法，我们可以获得一个对ExternalContext的引用，通过该引用我们可以获取所有我们希望使用的session，cookies等常量的引用，除此以外，我们还能获取request当中的头信息和请求信息等。</font>
		</span>
<img src ="http://www.blogjava.net/yanzhou/aggbug/73012.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanzhou/" target="_blank">周游世界</a> 2006-09-30 13:23 <a href="http://www.blogjava.net/yanzhou/archive/2006/09/30/73012.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ] 解决web开发中的中文问题</title><link>http://www.blogjava.net/yanzhou/archive/2006/09/19/70535.html</link><dc:creator>周游世界</dc:creator><author>周游世界</author><pubDate>Tue, 19 Sep 2006 07:09:00 GMT</pubDate><guid>http://www.blogjava.net/yanzhou/archive/2006/09/19/70535.html</guid><wfw:comment>http://www.blogjava.net/yanzhou/comments/70535.html</wfw:comment><comments>http://www.blogjava.net/yanzhou/archive/2006/09/19/70535.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanzhou/comments/commentRss/70535.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanzhou/services/trackbacks/70535.html</trackback:ping><description><![CDATA[　　这段时间经常看到有人问到web开发中怎么中文总是?号。原因其实很简单，因为大家大多用的是tomcat服务器，而tomcat服务器的默认编码为 iso-8859-1(西欧字符)。就是因为iso-8859-1(西欧字符)编码造成了我们经常看到？号。关于iso-8859-1(西欧字符)更多知识请看<a href="http://www.regexlab.com/zh/encoding.htm#un" name="main">《字节，字符和编码</a><a href="http://www.regexlab.com/zh/encoding.htm#un">》</a>这篇文章。<br /><br />方法一：最简单也是用的最多的方法。<br />&lt;%@ page language="java" pageEncoding="GBK" %&gt;<br />或者&lt;%@ page contenttype="text/html;charset=gbk";&gt;这里可以用gb2312或者gbk，只是gbk比gb2312支持跟多的字符。<br /><br />这个方法用于jsp页面中的中文显示。<br /><br />方法二：使用过滤器。<br />过滤器使用主要针对表单提交，插入数据库的数据都是？号。这也是应为tomcat不按request所指定的编码进行编码，还是自作主张的采用默认编码方式iso-8859-1编码。<br />编写一个SetCharacterEncodingFilter类。<br />import java.io.IOException;<br /><br />import javax.servlet.Filter;<br />import javax.servlet.FilterChain;<br />import javax.servlet.FilterConfig;<br />import javax.servlet.ServletException;<br />import javax.servlet.ServletRequest;<br />import javax.servlet.ServletResponse;<br /><br />public class SetCharacterEncodingFilter implements Filter {<br />protected String encoding = null;<br />protected FilterConfig filterConfig = null;<br />protected boolean ignore = true;<br /><br />public void init(FilterConfig filterConfig) throws ServletException {<br />this.filterConfig=filterConfig;<br />this.encoding=filterConfig.getInitParameter("encoding");<br />String value=filterConfig.getInitParameter("ignore");<br />if(value==null)<br />this.ignore=true;<br />else if(value.equalsIgnoreCase("true"))<br />this.ignore=true;<br />else<br />this.ignore=false;<br />}<br /><br />public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {<br />// TODO 自动生成方法存根<br />if (ignore || (request.getCharacterEncoding() == null)) {<br />String encoding = selectEncoding(request);<br />if (encoding != null)<br />request.setCharacterEncoding(encoding);<br />}<br />chain.doFilter(request, response);<br />}<br /><br />public void destroy() {<br />// TODO 自动生成方法存根<br />this.encoding = null;<br />this.filterConfig = null;<br />}<br /><br />protected String selectEncoding(ServletRequest request) {<br />return (this.encoding);<br />}<br />}<br /><br />然后再web.xml加上<br />&lt;!-- Set Character Encoding--&gt;<br />&lt;filter&gt;<br />&lt;filter-name&gt;Set Character Encoding&lt;/filter-name&gt;<br />&lt;filter-class&gt;com.struts.common.SetCharacterEncodingFilter&lt;/filter-class&gt;<br />&lt;init-param&gt;<br />&lt;param-name&gt;encoding&lt;/param-name&gt;<br />&lt;param-value&gt;UTF-8&lt;/param-value&gt;<br />&lt;/init-param&gt;<br />&lt;/filter&gt;<br /><br />&lt;filter-mapping&gt;<br />&lt;filter-name&gt;Set Character Encoding&lt;/filter-name&gt;<br />&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br />&lt;/filter-mapping&gt;<br />&lt;!-- Set Character Encoding--&gt;<br /><br />使用过滤器的好处很多，特别是项目之中。<br />而且在使用国际化时就更有用了，只要在页面指定&lt;%@ page language="java" pageEncoding="UTF-8" %&gt;，服务器就会根据本地Locale来显示正确的字符集。<br /><br />所以我特别推荐使用过滤器。<br /><br />方法三：修改tomcat的server.xml文件中URIEncoding。<br />&lt;Connector<br />debug="0"<br />acceptCount="100"<br />connectionTimeout="20000"<br />disableUploadTimeout="true"<br />port="80"<br />redirectPort="8443"<br />enableLookups="false"<br />minSpareThreads="25"<br />maxSpareThreads="75"<br />maxThreads="150"<br />maxPostSize="0"<br /><font color="#0000ff"><b>URIEncoding="GBK"</b></font><br />&gt;<br />&lt;/Connector&gt;<br />这个方法主要针对从url中获取字符串的问题。<br />在tomcat5.0及以上版本，post和get方法在处理编码时有所不同。如果你在url中获取中文就会出现？号。但在tomcat4.1版本没有问题，因为tomcat4.1的post和get方法在处理编码时是一样的。 <img src ="http://www.blogjava.net/yanzhou/aggbug/70535.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanzhou/" target="_blank">周游世界</a> 2006-09-19 15:09 <a href="http://www.blogjava.net/yanzhou/archive/2006/09/19/70535.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>