You are on page 1of 71

Java 网络编程基础

1. TCP Sockets 基础
2. UDP Sockets 基础
3. 网页显示控件
4. URL 类
5. URLEncoder 类
6. URLDecoder 类
7. HttpURLConnection 类
8. 其他 Socket 应用

Object-Oriented Programme 1
Java 平台网络编程的支持
 Java 运行环境使得编写的应用程序可以与远
程系统通信和共享信息
 Java.net 包中提供了相应的类和接口
 API 参考
 互联网上的通信协议
 TCP( 传输控制协议 ) 是面向连接的可靠传输协议
,服务器端监听一个特定端口,等待客户端建立
连接进行通信;
 UDP( 用户数据报协议 ) 是无连接面向消息的协议

Object-Oriented Programme 2
1 TCP Sockets 基础
 Sockets 是一个编程抽象概念,它是网络上
与另一个应用程序通信连接的句柄。
 原意:插座,向电源插座一样连接获得源源不
断的资源与服务
 Sockets 编程将用户代码与 TCP/IP 协议堆
栈的底层实现隔离开,允许用户灵活地实现自
己的编程。

Object-Oriented Programme 3
基于 TCP 协议的 TCP Sockets 需要四个方面的
数据
 本地系统的 IP 地址
 本地应用程序正在使用的 TCP 端口号
 通信的远程系统的 IP 地址
 通信的远程系统响应的 TCP 端口号

Object-Oriented Programme 4
TCP Sockets 的应用模型
 通常是客户 / 服务器模型
 一个服务器在一个特定端口等待远程计算机请求
资源,给予响应。
 客户程序绑定到这个端口,建立一个可靠连接来
用于通信。

Object-Oriented Programme 5
面向连接的操作
 使用 TCP 协议
 在这个模式下的 Socket 必须在发送数据之
前与目的地的 Socket 取得一个连接
 连接建立后, Socket 就可以使用一个流接口
:打开 / 读写 / 关闭。
 所有发送的信息都会在另一端以同样的顺序被
接收。
 面向连接的操作比无连接的操作效率低,但是
数据的安全性更高。
Object-Oriented Programme 6
客户端请求 Socket 的步骤

 注意: Socket 使用是双方的


 客户端和服务器端的使用稍有不同。

 客户端请求 Socket 的步骤


① 建立客户端 Socket 连接;
② 得到 Socket 的读和写的流;
③ 利用流;
④ 关闭流;
⑤ 关闭 Socket 。

Object-Oriented Programme 7
服务器端 Socket 完成的基本步骤
 使用一个服务器端的 Socket 请求比使用一个客户
端 Socket 请求要麻烦一些
 服务器并不主动地建立连接
 而是服务器被动地监听客户端连接请示,然后提供服务
 服务器端 Socket 要完成的基本步骤
① 建立一个服务器 Socket 并开始监听;
② 使用 accept() 方法取得新的连接;
③ 建立输入和输出流;
④ 在已有的协议上产生会话;
⑤ 关闭客户端流和 Socket ;
⑥ 回到②或者转到⑦;
⑦ 关闭服务器 Socket 。

Object-Oriented Programme 8
面向连接的 Socket 协作流程图

Object-Oriented Programme 9
举例说明
用 Socket 连接获取网页
 HTTP 请求通过服务器的 80 端口实现,服务器
端类似于 Socket 监听,端口号 为 80 。
 如果客户端希望通过应用程序获得网页,只需采
用面向连接的 Socket ,请求 80 端口,获取网
页数据。
 首先打开一个连接,请求主机为服务器网址,请
求端口号为 80 ,然后发送命令请求“ GET /
HTTP/1.0\r\n\r\n” ,得到返回的网页。

Object-Oriented Programme 10
InetAddress 类
 InetAddress 对象表示一个 Internet 主机地

 主机地址可以通过名字和 IP 地址来标识。
 名字即主机名,例如本机名字为 CLOUD ,为字符串类型;
 IP 地址为 192.1.1.127 ,为四字节的数字,书写形式为 a.b.c.d 。
 常用方法
 public static InetAddress getByName(String host) throws
UnknownHostException
 通过名字可以得到主机的 IP 地址。
 public String getHostAddress()
 返回 IP 地址的字符串格式。
 public String getHostName()
 返回 IP 地址的主机名。
 public static InetAddress getLocalHost() throws
UnknownHostException
 以 InetAddress 类封装的格式返回本机地址。
Object-Oriented Programme 11
 public String toString()
Sample code
 FinfHost.java
 InetAddress 对象应用的测试
 获取本机地址并转换成字符串输出,
 输出本地主机名和 IP 地址
 通过局域网内计算机的名字得到它的 IP 地址。

Object-Oriented Programme 12
Socket 类
 Socket
类用来实现客户端的 Socket 。
 常用的构造方法
 public Socket()
创建一个无连接的 Socket
 public Socket(InetAddress address, int port)
创建流式 Socket ,将它连接到 InetdAdress 类指明
的主机和 port 端口上
 public Socket(String host, int port)
创建流式 Socket 并将它连接到特定 host 的特定
port 端口上

Object-Oriented Programme 13
Notes on the sample code
 Sample code (ReadClient.java)
 建立客户端程序,访问网址
http://www.whu.edu.cn ,返回网址的首页写
入文件 xjtu.html 。

Object-Oriented Programme 14
程序说明
 (1) 建立到 http://www.xjtu.edu.cn 端口 80 的 Socket 连接。
Socket clientSocket = new
Socket("www.xjtu.edu.cn", 80);
 (2) 初始化字节流。连接建立后的数据传输通过数据输入输出流实现,写文件又
通过文件输入输出流来实现。各种流对象的初始化如下:
DataOutputStream outbound = new
DataOutputStream(clientSocket.getOutputStrea
m());
DataInputStream inbound = new
DataInputStream(clientSocket.getInputStream());

InputStreamReader inS = new


InputStreamReader(inbound);
File f = new File("xjtu.html");
FileOutputStreamObject-Oriented
fOut = Programme
new FileOutputStream(f);
15
ServerSocket 类
 实现服务器 Socket ,
 服务器 Socket 等待从网络到达的请求
 基于这些请求完成操作
 然后返回相应的结果给请求者。
 ServerSocket 类的重要方法:
 public Socket accept() 用户监听并接收一个连
接。

Object-Oriented Programme 16
Sample code
 SimpleWebServer.java
 ReadClient2.java
 TCP 通信 , 服务器端发送字符串到客户端。

Object-Oriented Programme 17
2 UDP Sockets 基 础
 数据报 Socket
 是包发送服务的接收和发送点,
 接收和发送的每个包都是单独访问和路由的。从一个机器
发送到另一个机器的包可能有不同的路由,
 且可能以任意顺序到达。
 构造函数
 public DatagramSocket() 构建数据报 Socket ,绑定到
本地主机上的任意端口。
 public DatagramSocket(int port) 构建一个数据报
Socket ,将它绑定到指定的 port 端口。
 public DatagramSocket(int port, InetAddress laddr) 构
建一个数据报 Socket ,绑定到指定的 laddr 本地地址
和 port 端口。
Object-Oriented Programme 18
数据报 Socket 方法
 常用方法
 public void connect(InetAddress address, int
port)
将建立的数据报 Socket 连接到远程地址 address 的
port 端口。
 public void receive(DatagramPacket p)
接收数据报包 p。
 public void send(DatagramPacket p)
发送数据报包 p。
 数据报可以通过 connect 方法建立连接,也
可以直接使用 send 方法发送数据,使用
receive 方法接收数据
Object-Oriented Programme 19
DatagramPacket 类
 比较
 TCP Socket 发送的数据是流式数据,
 UDP Socket 发送的数据是分块的数据包,这就用到
DatagramPacket 类
 数据报 Socket 传输数据前首先构造数据报包,然
后传输这个数据报包
 数据报包的构造函数如下:
 DatagramPacket(byte[] buf, int length)
 构建数据报包,接收 length 长度的包并放入 buf 。
 DatagramPacket(byte[] buf, int length, InetAddress
address, int port)
 构建数据报包,发送 length 长度的包到 address 指定的主机的
port 端口。

Object-Oriented Programme 20
Sample code
 客户端循环发送数据“Client:Hello” 到服务器
端,服务器端截取字符串 Hello 输出,并将
源字符串返回给客户端。
 服务器端程序
DatagramSocket mysock = new DatagramSocket(1719); // 建
立数据报包,准备接收。
byte[] buf = new byte[1024];
DatagramPacket p = new
DatagramPacket(buf,buf.length); // 接收或者发送。
mysock.receive(p);
mysock.send(p);

Object-Oriented Programme 21
客户端程序
 建立数据报 Socket 。
DatagramSocket mysock = new DatagramSocket(); //
建立数据报包,准备接收。
DatagramPacket p = new
DatagramPacket(buf,buf.length,InetAddress.
getLocalHost(),1719); // 接收或者发送
mysock.receive(p);
mysock.send(p);

Object-Oriented Programme 22
音频采集、播放
 使用UDP Socket 和多线程技术实现局域网
内部的声音采集与传输。

Object-Oriented Programme 23
Sample code
 TalkSelf.java
 采用对等访问模式,使用时只需输入
 Java UDPTalk “ 对方服务器 IP 地址”
 本程序是 Applet 与应用程序共用
 如果通过 Applet 进行访问,需要获取
SERVER_NAME 参数,或者在程序内写成定

 需为 Applet 进行数字签名,否则无权在客
户端的机器上进行音频采集。
Object-Oriented Programme 24
3 网页显示组件 JEditorPane
 前面介绍的例程中的 TCP Socket 客户端程
序访问网页,将返回的网页内容另存为一个文
件,无法直观地受到 Java 编程的控制
 JeditorPane 组件
在 javax.swing 包中
 可以显示网页
 响应简单的链接事件

Object-Oriented Programme 25
JEditorPane 类
 JEditorPane 类是一个文本组件,通过实现相应的
EditorKit 接口可以编辑一些特殊格式的内容
 它的内容类型有三种:
 text/plain
 使用默认的 DefaultEditorKit 的扩展,处理普通文本。
 txt/html
 使用 javax.swing.text.html.HTMLEditorKit 提供 HTML3.2 的帮
助,用以处理 HTML 文本,针对 HTML 中存在的一些链接,进
行点击时可以激活超链接事件。
 txt/rtf
 使用 javax.swing.text.rtf.RTFEditorKit 实现对 RTF ( ] Rich
Text Format ) 格式文本的处理。

Object-Oriented Programme 26
JEditorPane 类的构造函数和常用方法
 public JEditorPane()
 构建一个空 JeditorPane 。
 public JEditorPane(String url)
 构建一个基于 URL 声明的字符串的 JEditorPane 。
 public JEditorPane(URL initialPage)
 构建一个基于特定 URL 对象的 JeditorPane 。
 publicvoid
addHyperlinkListener(HyperlinkListener
listener)
 添加一个超链接监听者,通知任何变化,例如选中一个链接并进入时。

 public void setPage(String url)


 设置显示的 URL 页面。 字符串 url 表示页面的 URL 地址。
 public void setPage(URL page)
 设置显示的 URL 对象代表的 page
Object-Oriented 页面。
Programme 27
HyperlinkListener
 HyperlinkListener
 超链接事件监听者接口,
 继承于 EventListener 接口,
 只有一个超链接更新事件:点击链接或者进入链
接引发。
 重要方法
 public void hyperlinkUpdate(HyperlinkEvent e)
当更新一个超链接时调用此方法。

Object-Oriented Programme 28
HyperlinkEvent
 HyperlinkEvent
 是超链接事件,用于通知有关超文本链接的变化

 常用方法
 public HyperlinkEvent.EventType getEventType()
返回事件的类型,类型有三种 ( 下页表 )
 public URL getURL()
返回超链接指向的 URL 地址

Object-Oriented Programme 29
超链接事件的三种类型

HyperlinkEvent.EventT 描 述
ype
HyperlinkEvent.ACTIV 激活超链接
ATED
HyperlinkEvent.ENTER 进入超链接的文本
ED

HyperlinkEvent.EXITE 退出超链接的文本
D

Object-Oriented Programme 30
Sample code
 DisplayHtml.java
 使用JEditorPane 显示网页,当单击其中的
链接时能够给予响应。

Object-Oriented Programme 31
较高层次的网络接口
 Java 编程语言中同时提供了
 较低层次网络接口
 较高层次网络接口。
 前面介绍到的 Socket 编程比较接近网络的底层,
程序直接作用于网络协议
 区别:
 较低级的接口应用
 Socket
编程提供较为丰富的编程
 用户可以定制自己的应用协议
 实现服务器和客户端之间的自由通信,但编写繁琐;

 较高级的接口应用,
 只针对网络服务器进行访问,应用较为简单,
 而这些较高级的接口都是在封装 Socket 的基础上实现的。
Object-Oriented Programme 32
4 URL 类
 URL 类
 代表统一资源定位符 (Uniform Resource
Locator) ,指向 WWW 上的资源,
 资源可以是
一个文件
一个目录,
也可以是一个更加复杂的对象,如对数据库的查询或者
搜索引擎的查询

Object-Oriented Programme 33
一个 URL 由几个部分组成
 http://192.100.100.43:8080/web/index.html?search
=Java
 其中:
 (1) http 用来描述使用的协议为超文本链接协议。
 (2) 192.100.100.43 是信息存放的主机 IP 地址 ( 也可以
是主机名,如 www.sohu.com.cn) 。
 (3) 端口号为 8080 ,默认都为 80 端口。
 (4) web/index.html 为 HTTP 服务器上文件的虚拟路径。
(5) ?search=Java 为网页上表单使用 GET 方法进行
查询的附加部分,
? 表示后面跟的是提交的表单的名 - 值对,格式为“名 =
值”。 search 为查询词的名字, Java 为查询词的值,即用户要
查询的值,
 提交搜索引擎时可以有 0 个到多个这样的查询名 - 值对。

Object-Oriented Programme 34
构造方法
 public URL(String spec)
 从指定的字符串 spec 创建一个 URL 对象。
 publicURL(String protocol, String host, int
port, String file)
 从指定的 protocol 协议、 host 主机、 port 端
口号和 file 文件名创建一个 URL 对象。
 publicURL(String protocol, String host,
String file)
 从指定的 protocol 协议、主机名 host 和文件
名 file 创建一个 URL 对象。

Object-Oriented Programme 35
其它常用方法
 public Object getContent() 得到 URL 的内
容。
 public String getContentType() 返回网页内容
类型,普通网页为“ text/html” 。 public
String getFile() 得到 URL 文件名。
 public String getHost() 得到 URL 的主机
名。
 public String getPath() 得到 URL 的路径
部分。
 public int getPort() 得到 URL 的端口号。
 public String getProtocol() 得到 URL 的协
议名。
 public String getQuery() 得到 URL 的查询
Object-Oriented Programme 36
4 URLEncoder 类
 URLEncoder 类是一个工具类,用于 HTML 表单
的编码。
 类只有一个静态方法,这个方法将字符串转换成
application/x-www-form-urlencoded MIME 格式。
 publicstatic String encode(String s, String enc) throws
UnsupportedEncodingException 使用的指定 enc 编码
格式将字符串 s 转换为 application/x-www-form-
urlencoded 格式,得以顺利地在网上传输。
 其中
s 为要转换的字符串;
 enc 是字符编码格式名称,包括 US-ASCII 、 ISO-8859-
1 、 UTF-8 等。

Object-Oriented Programme 37
Sample code
 UseEncode.java
 将查询表单提交的网址和相应的两个查询“名
- 值对”进行“ UTF-8” 编码格式进行编码。

Object-Oriented Programme 38
5 URLDecoder 类
是 与 URLEncoder 类对应的解码类。
 构造和解码方法
 public URLEncoder()
 public static String decode(String s, String enc)
使用编码格式 enc 对 application/x-www-form-
urlencoded 字符串 s 进行解码

Object-Oriented Programme 39
6 URLConnection 类
 URLConnection
 它是抽象类
 代表应用程序和 URL 之间通信连接的类的超类。
 类的实例可以用来读取和写入 URL 代表的资源。
 URLConnection 类方法
 openConnection()
 处理影响到远程资源的连接参数
 connect()
 同资源进行交互,查询头文件和内容
 方法的实现步骤
 调用 openConnection 方法建立连接对象。
 操作建立参数和普通请求参数。
 调用 connect 方法建立到远程对象的实际连接。
 远程对象可用,头文件和远程对象的内容可以访问

Object-Oriented Programme 40
Sample code
 Search.java
 程序访问天网主页 (http://e.pku.edu.cn) 并查询
Java 一词,将查询结果存入 result.html 文件。
 分析
 (1) 建立 URL 对象。 u = new URL(fullURL);
 (2) 打开到 URL 对象的连接。 conn =
u.openConnection();
 (3) 获取输入流。 theData =conn.getInputStream();
 (4) 输出到文件。 p.println(line);

Object-Oriented Programme 41
7 HttpURLConnection 类
 HttpURLConnection 类继承 URLConnection 类,专门支持
HTTP 协议相关的特征
 每个 HttpURLConnection 实例完成一个单一的请求
 构造及其它方法
 protected HttpURLConnection(URL u)
 构建一个到 URL 对象 u 代表的网络资源的 HttpURLConnection 连接

 public int getResponseCode() throws IOException
 返回 HTTP 状态码,如: HTTP/1.0 200 OK HTTP/1.0 401
Unauthorized
 HttpURLConnection 类中有一系列的常量值对应着这些状态
码。
 HTTP_OK 对应 HTTP/1.0 200 OK
 HTTP_UNAUTHORIZED 对应 HTTP/1.0 401 Unauthorized

Object-Oriented Programme 42
Sample code
 UseHttp.java
 返回 www.whu.edu.cn 网址的首页,并与从浏览器
获得的网页进行比较。
 由运行结果可以看出,左边的页面是网址首页,右
边的网页是程序生成的 whu.html 页面。比较可得
,两个页面是一致的。
 随着类的依次封装,可应用领域变窄
从 Socket 到 URL ,再到针对 HTTP 协议的 URL ,
 但相对专门的应用而言,用户使用起来更为简单。

Object-Oriented Programme 43
8 其他 Socket 应用
 在 J2sdk1.4 中提供了一个新 I/O 包 (java.nio) ,通过引入
四个基本抽象类,开发网络 Socket 连接的非阻塞应用。
 对于前面 Java 编程中的多个 Socket 连接,用线程实现时
会遇到一些问题如操作系统限制、死锁、线程安全违反等。
 而新 I/O 提供的特性可以很好地解决这一问题。
 引入的四个基本抽象类如下:
 Buffer :包含读写的数据线性流。
 Charset :将 Unicode 字符与字节流之间进行相互转换。

 Channels :可以是 Socket 、文件或管道,代表双边通


信的管道。
 Selectors :多元异步 I/O 操作,应用于单个线程或多个
线程。

Object-Oriented Programme 44
Buffers
 Buffer 是一个线性、有序的数据集,提供了
一种在内存容器中保存一系列原始数据的机制。
Buffer 进行特定的封装之后只允许读写一种
数据类型,例如 char 、 int 或 double 等。
共有七种读写不同数据类型的 Buffer ,如表
所示
 Buffer 为抽象基类

Object-Oriented Programme 45
读写不同数据类型的 Buffer
Buffer 名字 存放数据类型
ByteBuffer 字节
CharBuffer 字符
DoubleBuffer 双精度数
FloatBuffer 单精度数
IntBuffer 整数
LongBuffer 长整数
ShortBuffer 短整数
Object-Oriented Programme 46
Buffer 中涉及到的四个基本概念
 Capcity
 Buffer 的大小 (>=size) ;
 Position
在 Buffer 中目前读写的位置 (<=limit) ;
 Limit
 第一个不应该被读取元素的位置的 index( 索引号 )
(<=cpacity) ;
 Mark
用 mark 方 法 设 置 的可 设位 置, mark 方 法 可
以使 用 reset 来 重 置 position(<=position , >=0) 。

Object-Oriented Programme 47
Sample code
 代码依次演示 capacity 、 position 和 limit 的值
 ByteBuffer buf = ByteBuffer.allocateDirect(8);
 buf.put( (byte)0xca );
 buf.putShort( (short)0xfeba );
 buf.put( (byte)0xbe );
 buf.flip();
 代码段分析如下: ByteBuffer buf =
ByteBuffer.allocateDirect(8); 分配 8 字节的字节缓冲,
position=0 、 capacity=8 、 limit=8 ,如图 a 所示。

Object-Oriented Programme 48
 buf.put( (byte)0xca );
 buf.putShort( (short)0xfeba );
 buf.put( (byte)0xbe ); 依次放入三个数据,两
个字节数据和一个短整型数据,其中短整型数
据占两个字节, position=4 、 capacity=8 ,
如图 12.4(b) 所示

Object-Oriented Programme 49
 buf.flip();
上面这行语句是填充数据之后、写
入通道之前需要调用的方法,将 position 重
定位到 0 ,将 limit 定位到数据结束位置,
准备好序列写入通道。
position=0 、 capacity=8 、 limit=4 ,如图
12.4(c) 所示

Object-Oriented Programme 50
Charset( 字符集 )
 Charset(
字符集 ) 定义了 Unicode 和字节之
间的映射。字符集根据 IANA 标准定义,
Java 中字符集由 java.nio.charset.Charset
实例代表,使用 Charset.forName() 得到合
适实例。 Charset.availableCharsets() 给出支
持字符集名的映射和它们的 Charset 实例。
JDK1.4 包括 8 个字符集: US-
ASCII 、 ISO-8859-1 、 ISO-8859-
15 、 UTF-8 、 UTF-16 等。 Charset 构造
CharsetEncoder 和 CharsetDecoder 使得
,有序字符和字节之间可以进行转换,输出时
使用 encoder ,对输入请求使用 decoder 。
以下代码段是对字符 buffer 进行编码的情况。
Object-Oriented Programme 51
Channels( 通道 )
 Channels(
通道 ) 将数据读入缓冲并且从缓冲
发送数据,被认为是到一些设备、程序或网络
的连接。常用通道有
ServerSocketChannel 、 SocketChannel 和
FileChannel 几类。

Object-Oriented Programme 52
ServerSocketChannel
 Java.nio.channels.ServerSocketChannel

java.net.ServerSocket 一样,用于创建一个监听线
程来接收到来的连接,只是既不能读也不能写。
ServerSocketChannel.socket() 提供对底层
ServerSocket 的 访 问 , 因 此 可 以 设 置
Socket 选 项 。 这 个 通 道 可 以 使 用
ServerSocketChannel.open() 工厂方法创建。
ServerSocketChannelaccept() 为新建立连接的客户
返回 java.nio.channel.SocketChannel 。如果
ServerSocketChannel 进入阻塞模式, accept() 不
会返回,直到一个有连接请求到达;而在非阻塞模
式,不管 Socket 是否为空,总是立刻返回。

Object-Oriented Programme 53
SocketChannel
 Java.nio.channels.SocketChannel
在应用程
序中封装了 java.net.Socket 并添加非阻塞
模式和状态机。 SocketChannel 可以通过两
种方法创建。 SocketChannel.open() 创建一
个新的、无连接的 SocketChannel ;通过
ServerSocketChannel.accept() 返回的
Socket ,实际上有一个开放和连接的
SocketChannel 附加在它上面。

Object-Oriented Programme 54
FileChannel
 文件通道允许使用操作系统的文件缓冲直接进
行文件传输。文件通道可以将文件区域映射到
内存。

Object-Oriented Programme 55
MappedByteBuffer
 一个专门用于直接缓冲的 ByteBuffer ,这个类用字节缓冲来
映射一个文件。想要映射一个文件到 MappedByteBuffer ,
必须先取得这个文件的通道 (channel) 。通道是某种已建立的
连接,如管道 (Pipe) 、套接口 (Socket) 或文件 (File) 等能够
完成 I/O 操作的对象。如果是文件通道,你可以通过
FileInputStream( 文件输入流 ) 、 FileOutputStream( 文件输
出流 ) 或 RandomAccessFile( 随机存取文件 ) 的
getChannel 方法来获得一个通道。一旦你取得这个通道,就
可以通过它的 map 方法指明映射模式,来把你想映射的那
一部分文件映射到缓冲中去。文件通道可以使用
FileChannel.MapMode 的任一个常数打开:只读
 (READ_ONLY) 、私有 / 写时拷贝 (PRIVATE) 或读写
(READ_WRITE) 。

Object-Oriented Programme 56
Sample code
 UseFchannel.java
 使用文件通道打开一个文件并输出文件内容
 分析
 (1) 通过文件输入流对象得到通道。 FileInputStream input
= new FileInputStream(filename); FileChannel channel =
input.getChannel();
 (2) 映射到字节缓冲。 int fileLength = (int)channel.size();
MappedByteBuffer buffer =
channel.map(FileChannel.MapMode.READ_ONLY, 0,
fileLength);
 (3) 字符集进行解码 . Charset charset =
Charset.forName("ISO-8859-1");// CharsetDecoder decoder
= charset.newDecoder(); CharBuffer charBuffer =
decoder.decode(buffer);
 (4) 出文件内容。 System.out.println(charBuffer);

Object-Oriented Programme 57
electors
 非阻塞 I/O 围绕为多元选择通道准备的 Selectors(
选择器 ) 对象构建。选择器对象保持一系列选择键,
这些键在应用中可由某个事件激活。选择器本身管
理键,编程人员使用键的状态管理回调来完成客户
请求。 构造函数如下: protected Selector() 初始
化一个实例。 选择器可以通过调用自带的 open
方法进行创建: Selector s = Selector.open();
下面的两个方法用来返回选择键值的个数和键值集
合。 public abstract int select() throws
IOException 返回键值的个数。 public abstract Set
selectedKeys() 返回选择器选中的键值集合。

Object-Oriented Programme 58
 对于一个 Socket 通道,只有将它本身发生
的事件 ( 如监听、连接、读、写等 ) 在选择器
上进行注册,才可以被选择器识别,从而进行
处理,这个注册就用到 SelectionKey 类。
SelectionKey 类包括四个注册值,将通道支
持的事件注册到相应的选择器上它还提供四个
判断键值状态的方法,从而进行相应的事件处
理,如表 12.3 所示。

Object-Oriented Programme 59
SelectionKey 类的注册值及其方法
注册值 注册事 键值状态判断 函数描述
件 函数

OP_ACCEPT 接收 public boolean 测试通道是否


isAcceptable() 可接收

OP_CONNECT 连接 public boolean 测试通道连接


isConnectable( 完成或失败
)
OP_READ 读取 public boolean 测试通道是否
isReadable() 可读

OP_WRITE 写入 public boolean 测试通道是否


isWritable() 可写

Object-Oriented Programme 60
example
 接收新连接的通道应该注册为: ServerSocketChannel ch =
ServerSocketChannel.open(); SelectionKey acceptKey =
ch.register(s,SelectionKey.OP_ACCEPT); 一个读取和写入
数据的通道应该注册为: SelectionKey readWriteKey =
ch.register(s,SelectionKey.OP_READ|
SelectionKey.OP_WRITE); 当用户发送一个请求时,选择器
通过返回键进行工作。 // 循环实现 While((keysAdded =
s.select())>0) { // 返回键值集合 set readyKeys =
s.selectedKeys(); // 使用 Iterator 枚举 Iterator I =
readKeys.iterator(); While(i.hasNext()) { SelectionKey sk =
(SelectionKey)i.next(); ? 接收连接处理请求 ; } } 具体的处理
可以根据键值的状态进行,上面的“接收连接处理请求”就
可以为: i.remove(); if ( key.isAcceptable() ) { … } else if
( key.isWritable() ) { … }

Object-Oriented Programme 61
阻塞模式
 阻塞模式
 服务器端和客户端的程序基本相同
 惟一不同的客户端为 Connect 方法,服务器端为
Accept 方法。
 新 I/O 包处理 Socket 的读写操作时使用
java.net 包中的 InetSocketAddress 类指定链连接
地址,并用前面介绍的 SocketChannel 类来完成
实际的读写操作。
 InetSocketAddress 类提供一个用来绑定、连接或者
返回数值的远程对象。
 它常用的构造函数为:
 InetSocketAddress(String
hostname, int port)
 以名字 hostname 和端口号 port 创建 socket 地址。

Object-Oriented Programme 62
客户端使用 SocketChannel 建立连接的步

 (1) 首先获得 InetSocketAddress 的对象。
String host = "www.sohu.com.cn";
InetSocketAddress socketAddress = new
InetSocketAddress(host, 80);
 (2) 打开一个到 InetSocketAddress 代表的
主机的连接。 使用 SocketChannel 取代以
前从 Socket 对象的输入流来读取、向
Socket 的输出流写入的所有操作:
SocketChannel channel =
SocketChannel.open();
channel.connect(socketAddress);
 (3) 发送一个 HTTP 请求,发送请求之前进
行编码。 Charset charset =
Charset.forName("ISO-8859-1"); 63
Object-Oriented Programme
Sample code
 ReadURL.java
 程序通过一个 HTTP 请求来读取站点
www.xjtu.edu.cn 的首页,直接输出到屏幕上
,可以看见是网页的文本文件。

Object-Oriented Programme 64
非阻塞模式
 非阻塞模式是基于事件响应的,根据通道注册
的键值的状态进行处理。在打开通道后调用
configureBlocking 方法,输入 false 值,就
可以进入非阻塞状态。

Object-Oriented Programme 65
非阻塞客户端编程步骤
 (1) 创建可选择通道,配置成非阻塞模式并进
行连接。 String host = "192.100.100.43";
InetSocketAddress socketAddress = new
InetSocketAddress(host, 80); channel =
SocketChannel.open();
channel.configureBlocking(false);
channel.connect(socketAddress); 这时得到
的通道 channel 是一个可选择通道
(SelectableChannel) ,通过一个 Selector
来工作。将通道注册到一个 Selector ,同时
声明一些事件,当事件发生时通道会通过回调
执行。 (2) 创建 Selector 实例。
Selector selector = Selector.open(); (3) 通
道 向 选 择 器Object-Oriented
的 注Programme
册 。 对 于 66
Sample code
 NBReadURL.java
 程序结果是得到本机tomcat 主页并显示在
标准输出上。用户也可以将它输出到文件中,
以方便与原网页进行比较。

Object-Oriented Programme 67
非阻塞服务器
 非阻塞服务器 使用新 I/O 包实现的非阻塞
Web 服务器,不必为每个连接都提供一个线
程就可以实现 web 服务器的功能。

Object-Oriented Programme 68
非阻塞服务器程序编写的步骤
 (1) 创建接收通道,配置非阻塞模式并绑定到
InetSocketAddress 对象。 ServerSocketChannel channel
= ServerSocketChannel.open();
channel.configureBlocking(false); InetSocketAddress isa =
new InetSocketAddress(port); channel.socket().bind(isa); (2)
创建 Selector 实例。 Selector selector =
Selector.open(); (3) 注册事件。服务器端需要注册的是
OP_ACCEPT 键值: channel.register(selector,
SelectionKey.OP_ACCEPT); (4) 处理通道事件。当有事件
在通道上发生时, Selector 进行通知,然后使用一个 while
(selector.select() > 0) 循环进行处理。发出请求得到响应的代
码都放入这一段,当作读写事件进行处理。 (5) 加入异常处
理代码。加入必要的 try{?}catch(Exception e){?} 语句,并
在 finally 代码段中加入关闭通道的代码。 可以看出非阻塞
模式下服务器和客户端编程的步骤都是一致的,只是有些步
骤下编写的代码有所不同。

Object-Oriented Programme 69
Sample code
 Server.java
 给出一个基本的单线程的服务器,对每一个响
应都发回一段文字信息来表示时间。

Object-Oriented Programme 70
note
 为了服务器与客户端能够进行通信 , 对客 户 端
程 序进行修改 , 将 InetSocketAddress 对象
sockAddr 中的端口号改成 4321 ,使得客户端请求
的端口号与此服务器监听的端口号一致,即将
InetSocketAddress sockAddr = new
InetSocketAddress(host, 8080); 改为
InetSocketAddress sockAddr = new
InetSocketAddress(host, 4321); 然后重新编译,生
成类文件。 启动两个命令提示符,其中一个运行命
令“ java Server” ,另一个键入命令“ java
NBReadURL” ,可以看见如图 12.5 的通信结果。
客户端显示服务器发送的一段时间信息。如果希望
在服务器端显示内容,还需修改服务器程序,注册
服务器通道的读、写事件。

Object-Oriented Programme 71