第 1 章 简介 ....................................................................................................................................3
1.1 计算机网络,分组报文和协议........................................................................................3
1.2 关于地址............................................................................................................................6
1.3 关于名字............................................................................................................................8
1.4 客户端和服务器................................................................................................................8
1.5 什么是套接字....................................................................................................................9
1.6 练习..................................................................................................................................10
第 2 章 基本套接字 ......................................................................................................................10
2.1 套接字地址......................................................................................................................10
2.2 TCP套接字.......................................................................................................................17
2.2.1 TCP客户端............................................................................................................17
2.2.2 TCP服务器端........................................................................................................22
2.2.3 输入输出流...........................................................................................................26
2.3 UDP套接字 ......................................................................................................................28
2.3.1 DatagramPacket类...............................................................................................28
2.3.2 UDP客户端 ...........................................................................................................30
2.3.3 UDP服务器端 .......................................................................................................36
2.3.4 使用UDP套接字发送和接收信息 ......................................................................38
2.4 练习..................................................................................................................................40
第 3 章 发送和接收数据...............................................................................................................41
3.1 信息编码..........................................................................................................................42
3.1.1 基本整型...............................................................................................................42
3.1.2 字符串和文本.......................................................................................................48
3.1.3 位操作:布尔值编码...........................................................................................50
3.2 组合输入输出流..............................................................................................................51
3.3 成帧与解析......................................................................................................................52
3.4 Java特定编码...................................................................................................................58
3.5 构建和解析协议消息......................................................................................................59
3.5.1 基于文本的表示方法...........................................................................................62
3.5.2 二进制表示方法...................................................................................................65
3.5.3 发送和接收...........................................................................................................67
3.6 结束..................................................................................................................................76
3.7 练习..................................................................................................................................76
第 4 章 进阶 ..................................................................................................................................77
4.1 多任务处理......................................................................................................................77
4.1.1 Java 多线程..........................................................................................................78
4.1.2 服务器协议...........................................................................................................80
4.1.3 一客户一线程.......................................................................................................84
4.1.4 线程池...................................................................................................................86
4.1.5 系统管理调度:Executor接口 ...........................................................................89
4.2 阻塞和超时......................................................................................................................91
4.2.1 accept(),read()和receive() ....................................................................................91
4.2.2 连接和写数据.......................................................................................................92
下载自:http://www.javaxxz.com 最方便的Java学习社区
4.2.3 限制每个客户端的时间.......................................................................................92
4.3 多接收者..........................................................................................................................94
4.3.1 广播.......................................................................................................................94
4.3.2 多播.......................................................................................................................95
4.4 控制默认行为................................................................................................................100
4.4.1 Keep-Alive...........................................................................................................100
4.4.2 发送和接收缓存区的大小.................................................................................101
4.4.3 超时.....................................................................................................................101
4.4.4 地址重用.............................................................................................................102
4.4.5 消除缓冲延迟.....................................................................................................102
4.4.6 紧急数据.............................................................................................................103
4.4.7 关闭后停留.........................................................................................................103
4.4.8 广播许可.............................................................................................................103
4.4.9 通信等级.............................................................................................................104
4.4.10 基于性能的协议选择.......................................................................................104
4.5 关闭连接........................................................................................................................104
4.6 Applets ........................................................................................................................... 111
4.7 结束................................................................................................................................112
4.8 练习................................................................................................................................112
第 5 章 NIO.................................................................................................................................112
5.1 为什么需要NIO? ........................................................................................................113
5.2 与Buffer一起使用Channel ..........................................................................................115
5.3 Selector...........................................................................................................................118
5.4 Buffer详解 .....................................................................................................................125
5.4.1 Buffer索引...........................................................................................................125
5.4.2 创建Buffer..........................................................................................................126
5.4.3 存储和接收数据.................................................................................................128
5.4.4 准备Buffer:clear(),flip(),和rewind()........................................................130
5.4.5 压缩Buffer中的数据..........................................................................................132
5.4.6 Buffer透视:duplicate(),slice()等 ..................................................................134
5.4.7 字符编码.............................................................................................................136
5.5 流(TCP)信道详解....................................................................................................136
5.6 Selector详解...................................................................................................................139
5.6.1 在信道中注册.....................................................................................................139
5.6.2 选取和识别准备就绪的信道.............................................................................141
5.6.3 信道附件.............................................................................................................143
5.6.4 Selector小结........................................................................................................144
5.7 数据报(UDP)信道 ...................................................................................................144
5.8 练习................................................................................................................................149
1. 使用定长的写缓冲区改写TCPEchoClientNonblocking.java。 ....................................149
2.使用Buffer和DatagramChannel编写一个回显客户端。 ................................................149
第 6 章 深入剖析 ........................................................................................................................149
6.1 缓冲和TCP....................................................................................................................152
6.2 死锁风险........................................................................................................................155
下载自:http://www.javaxxz.com 最方便的Java学习社区
6.3 性能相关........................................................................................................................158
6.4 TCP套接字的生存周期.................................................................................................158
6.4.1 连接.....................................................................................................................158
6.4.2 关闭TCP连接.....................................................................................................164
6.5 解调多路复用揭秘........................................................................................................167
6.6 练习................................................................................................................................169
第 1 章 简介
如今,人们可以通过电脑来打电话,看电视,给朋友发送即时信息,与其他人玩游戏,
甚至可以通过电脑买到你能想到的任何东西,包括从歌曲到 SUV[ ]。计算机程序能够通过
互联网相互通信使这一切成为了可能。很难统计现在有多少个人电脑接入互联网,但可以肯
定,这个数量增长得非常迅速,相信不久就能达到 10 亿。除此之外,新的应用程序每天在
互联网上层出不穷。随着日益增加的互联网访问带宽,我们可以预见,互联网将会对人们将
来的生活产生长远的影响。
那么程序是如何通过网络进行相互通信的呢?本书的目的就是通过在 Java 编程语言环
境下,带领你进入对这个问题的解答之路。Java 语言从一开始就是为了让人们使用互联网而
设计的,它为实现程序的相互通信提供了许多有用的抽象应用程序接口(API, Application
Programming Interface),这类应用程序接口被称为套接字(sockets)。
在我们开始探究套接字的细节之前,有必要向读者简单介绍计算机网络和通信协议的整
体框架,以使读者能清楚我们的代码将应用的地方。本章的目的不是向读者介绍计算机网络
和 TCP/IP 协议是如何工作的(已经有很多相关内容的教程[ ][ ][ ][ ][ ]),而是介绍一些基
本的概念和术语。
1.1 计算机网络,分组报文和协议
计算机网络由一组通过通信信道相互连接的机器组成。我们把这些机器称为主机
(hosts)和路由器(routers)。主机是指运行应用程序的计算机,这些应用程序包括网络浏
览器(Web browser),即时通讯代理(IM agent),或者是文件共享程序。运行在主机上的
应用程序才是计算机网络的真正"用户"。路由器的作用是将信息从一个通信信道传递或转发
(forward)到另一个通信信道。路由器上可能会运行一些程序,但大多数情况下它们是不
运行应用程序的。基于本书的目的对通信信道(communication channel)进行解释:它是将
字节序列从一个主机传输到另一个主机的一种手段,可能是有线电缆,如以太网(Ethernet),
也可能是无线的,如 WiFi[ ],或是其他方式的连接。
下载自:http://www.javaxxz.com 最方便的Java学习社区
路由器非常重要,因为要想直接将所有不同主机连接起来是不可行的。相反,一些主机
先得连接到路由器,路由器再连接到其他路由器,这样就形成了网络。这种布局使每个主机
只需要用到数量相对较少的通信信道,大部分主机仅需要一条信道。在网络上相互传递信息
的程序并不直接与路由器进行交互,它们基本上感觉不到路由器的存在。
这里的信息(information)是指由程序创建和解释的字节序列。在计算机网络环境中,
这些字节序列被称为分组报文(packets)。一组报文包括了网络用来完成工作的控制信息,
有时还包括一些用户数据。用于定位分组报文目的地址的信息就是一个例子。路由器正是利
用了这些控制信息来实现对每个报文的转发。
协议(protocol)相当于是相互通信的程序间达成的一种约定,它规定了分组报文的交
换方式和它们包含的意义。一组协议规定了分组报文的结构(例如报文中的哪一部分表明了
其目的地址)以及怎样对报文中所包含的信息进行解析。设计一组协议,通常是为了在一定
约束条件下解决某一特定的问题。比如,超文本传输协议(HTTP,HyperText Transfer
Protocol)是为了解决在服务器间传递超文本对象的问题,这些超文本对象在服务器中创建
和存储,并由 Web 浏览器进行可视化,以使其对用户有用。即时消息协议是为了使两个或
更多用户间能够交换简短的文本信息。
要实现一个有用的网络,必须解决大量各种各样的问题。为了使这些问题可管理和模块
化,人们设计了不同的协议来解决不同类型的问题。TCP/IP 协议就是这样一组的解决方案,
有时也被称为协议族(protocol suite)。它刚好是互联网所使用的协议,不过也能用在独立
的专用网络中。本书以后所提到的网络(network),都是指任何使用了 TCP/IP 协议族的网
络。TCP/IP 协议族主要协议有 IP 协议(互联网协议,Internet Protocol[ ]),TCP 协议(传
输控制协议,Transmission Control Protocol[ ])和 UDP 协议(用户数据报协议,User Datagram
Protocol[ ])。
事实证明将各种协议分层组织是一种非常有用的措施,TCP/IP 协议族,实际上其他所
有协议族都是按这种方式组织的。图 1.1 展示了通信协议、应用程序和主机和路由器中的套
接字 API(应用程序接口,Application Programming Interface)之间的关系,同时也展示了
数据流从一个应用程序到另一个应用程序的过程(使用 TCP 协议)。标记为 TCP,UDP 和
IP 的方框分别代表了这些协议的实现,它们通常驻留在主机的操作系统中。应用程序通过
套接字 API 对 UDP 协议和 TCP 协议所提供的服务进行访问。箭头描述了数据流从一个应用
程序,经过 TCP 协议层和 IP 协议层,通过网络,再反向经过 IP 协议层和 TCP 协议层传输
到另一端的应用程序。
下载自:http://www.javaxxz.com 最方便的Java学习社区
(点击查看大图)图 1.1: 一个 TCP/IP 网络
Application:应用程序;Socket:套接字;Host:主机;Channel:通信信道;Ethernet:
以太网;Router:路由器;Network Layer:网络层;Transport Layer:传输层。
在 TCP/IP 协议族中,底层由基础的通信信道构成,如以太网或调制解调器拨号连接。
这些信道由网络层(network layer)使用,而网络层则完成将分组报文传输到它们的目的地
址的工作(也就是路由器的功能)。TCP/IP 协议族中属于网络层的唯一协议是 IP 协议,它
使两个主机间的一系列通信信道和路由器看起来像是一条单一的主机到主机的信道。
IP 协议提供了一种数据报服务:每组分组报文都由网络独立处理和分发,就像信件或
包裹通过邮政系统发送一样。为了实现这个功能,每个 IP 报文必须包含一个保存其目的地
址(address)的字段,就像你所投递的每份包裹都写明了收件人地址。(我们随即会对地址
进行更详细的说明。)尽管绝大部分递送公司会保证将包裹送达,但 IP 协议只是一个"尽力
而为"(best-effort)的协议:它试图分发每一个分组报文,但在网络传输过程中,偶尔也会
发生丢失报文,使报文顺序被打乱,或重复发送报文的情况。
IP 协议层之上称为传输层(transport layer)。它提供了两种可选择的协议:TCP 协议和
UDP 协议。这两种协议都建立在 IP 层所提供的服务基础上,但根据应用程序协议(application
protocols)的不同需求,它们使用了不同的方法来实现不同方式的传输。TCP 协议和 UDP
协议有一个共同的功能,即寻址。回顾一下,IP 协议只是将分组报文分发到了不同的主机,
很明显,还需要更细粒度的寻址将报文发送到主机中指定的应用程序,因为同一主机上可能
有多个应用程序在使用网络。TCP 协议和 UDP 协议使用的地址叫做端口号(port numbers),
都是用来区分同一主机中的不同应用程序。TCP 协议和 UDP 协议也称为端到端传输协议
(end-to-end transport protocols),因为它们将数据从一个应用程序传输到另一个应用程序,
而 IP 协议只是将数据从一个主机传输到另一主机。
下载自:http://www.javaxxz.com 最方便的Java学习社区
TCP 协议能够检测和恢复 IP 层提供的主机到主机的信道中可能发生的报文丢失、重复
及其他错误。TCP 协议提供了一个可信赖的字节流(reliable byte-stream)信道,这样应用程
序就不需要再处理上述的问题。TCP 协议是一种面向连接(connection-oriented)的协议:
在使用它进行通信之前,两个应用程序之间首先要建立一个 TCP 连接,这涉及到相互通信
的两台电脑的 TCP 部件间完成的握手消息(handshake messages)的交换。使用 TCP 协议在
很多方面都与文件的输入输出(I/O, Input/Output)相似。实际上,由一个程序写入的文件再
由另一个程序读取就是一个 TCP 连接的适当模型。另一方面,UDP 协议并不尝试对 IP 层产
生的错误进行修复,它仅仅简单地扩展了 IP 协议"尽力而为"的数据报服务,使它能够在应
用程序之间工作,而不是在主机之间工作。因此,使用了 UDP 协议的应用程序必须为处理
报文丢失、顺序混乱等问题做好准备。
1.2 关于地址
寄信的时候,要在表格中填上邮政服务能够理解的收信人的地址。在给别人打电话之前,
必须给电话系统提供你所联系的人的电话号码。同样,一个程序要与另一个程序通信,就要
给网络提供足够的信息,使其能够找到另一个程序。在 TCP/IP 协议中,有两部分信息用来
定位一个指定的程序:互联网地址(Internet address)和端口号(port number)。其中互联
网地址由 IP 协议使用,而附加的端口地址信息由传输协议(TCP 或 IP 协议)对其进行解析。
互联网地址由二进制的数字组成,有两种型式,分别对应了两个版本的标准互联网协议。
现在最常用的版本是版本 4,即 IPv4[ ],另一个版本是刚开始开发的版本 6,即 IPv6[ ]。IPv4
的地址长 32 位,只能区分大约 40 亿个独立地址,对于如今的互联网来说,这是不够大的。
(也许看起来很多,但由于地址的分配方式的原因,有很多都被浪费了)出于这个原因引入
了 IPv6,它的地址有 128 位长。
为了便于人们使用互联网地址(相对于程序内部的表示),两个版本的 IP 协议有不同
的表示方法。IPv4 地址被表示为一组 4 个十进制数,每两个数字之间由圆点隔开(如:
10.1.2.3),这种表示方法叫做点分形式(dotted-quad)。点分形式字符串中的 4 个数字代表
了互联网地址的 4 个字节,也就是说,每个数字的范围是 0 到 255。
另一方面,IPv6 地址的 16 个字节由几组 16 进制的数字表示,这些 16 进制数之间由分
号隔开(如:2000:fdb8:0000:0000:0001:00ab:853c:39a1)。每组数字分别代表了地址中的两
个字节,并且每组开头的 0 可以省略,因此前面的例子中,第 5 组和第 6 组数字可以缩写
为:1:ab:。甚至,只包含 0 的连续组可以全部省略(但在一个地址中只能这样做一次)。因
此,前面的例子的完整地址可以表示为 2000:fdb8::1:00ab:853c:39a1。
从技术角度来讲,每个互联网地址代表了一台主机与底层的通信信道的连接,换句话说,
也就是一个网络接口(network interface)。主机可以有多个接口,这并不少见,例如一台主
下载自:http://www.javaxxz.com 最方便的Java学习社区
机同时连接了有线以太网(Ethernet)和无线网(WiFi)。由于每个这样的连接都属于唯一
的一台主机,所以只要它连接到网络,一个互联网地址就能定位这条主机。但是反过来,一
台主机并不对应一个互联网地址。因为每台主机可以有多个接口,每个接口又可以有多个地
址。(实际上一个接口可以同时拥有 IPv4 地址和 IPv6 地址)
TCP 或 UDP 协议中的端口号总与一个互联网地址相关联。回到前面我们作类比的例子,
一个端口号就相当于指定街道上一栋大楼的某个房间号。邮政服务通过街道地址把信分发到
一个邮箱,再由清空邮箱的人把这封信递送到这栋楼的正确房间中。或者考虑一个公司的内
部电话系统:要与这个公司中的某个人通话,首先要拨打该公司的总机电话号码连接到其内
部电话系统,然后再拨打你要找的那个人的分机号码。在上面的例子中,互联网地址就相对
于街道地址或公司的总机电话号码,端口号就相当于房间号或分机号码。端口号是一组 16
位的无符号二进制数,每个端口号的范围是 1 到 65535。(0 被保留)
每个版本的 IP 协议都定义了一些特殊用途的地址。其中值得注意的一个是回环地址
(loopback address),该地址总是被分配个一个特殊的回环接口(loopback interface)。回
环接口是一种虚拟设备,它的功能只是简单地将发送给它的报文直接回发给发送者。回环接
口在测试中非常有用,因为发送给这个地址的报文能够立即返回到目标地址。而且每台主机
上都有回环接口,即使当这台计算机没有其他接口(也就是说没有连接到网络),回环接口
也能使用。IPv4 的回环地址是 127.0.0.1[ ],IPv6 的回环地址是 0:0:0:0:0:0:0:1。
IPv4 地址中的另一种特殊用途的保留地址包括那些"私有用途"的地址。它们包括 IPv4
中所有以 10 或 192.168 开头的地址,以及第一个数是 172,第二个数在 16 到 31 的地址。(在
IPv6 中没有相应的这类地址)这类地址最初是为了在私有网络中使用而设计的,不属于公
共互联网的一部分。现在这类地址通常被用在家庭或小型办公室中,这些地方通过 NAT
(Network Address Translation,网络地址转换)设备连接到互联网。NAT 设备的功能就像
一个路由器,转发分组报文时将转换(重写)报文中的地址和端口。更准确地说,它将一个
接口中报文的私有地址端口对(private address, port pairs)映射成另一个接口中的公有地址
端口对(public address, port pairs)。这就使一小组主机(如家庭网络)能够有效地共享同
一个 IP 地址。重要的是这些内部地址不能从公共互联网访问。如果你在拥有私有类型地址
的计算机上试验本书的例子,并试图与另一台没有这类地址的主机进行通信,通常只有这台
拥有私有类型地址的主机发起的通信才能成功。
相关的类型的地址包括本地链接(link-local),或称为"自动配置"地址。IPv4 中,这类
地址由 169.254 开头,在 IPv6 中,前 16 位由 FE8 开头的地址是本地链接地址。这类地址只
能用来在连接到同一网络的主机之间进行通信,路由器不会转发这类地址的信息。
最后,另一类地址由多播(multicast)地址组成。普通的 IP 地址(有时也称为"单播"
地址)只与唯一一个目的地址相关联,而多播地址可能与任意数量的目的地址关联。我们将
下载自:http://www.javaxxz.com 最方便的Java学习社区
在第 4 章中简要地对多播技术作进一步介绍。IPv4 中的多播地址在点分格式中,第一个数
字在 224 到 239 之间。IPv6 中,多播地址由 FF 开始。
1.3 关于名字
也许你更习惯于通过名字来指代一个主机,例如:host.example.com。然而,互联网协
议只能处理二进制的网络地址,而不是主机名。首先应该明确的是,使用主机名而不使用地
址是出于方便性的考虑,这与TCP/IP提供的基本服务是相互独立的。你也可以不使用名字来
编写和使用TCP/IP应用程序。当使用名字来定位一个通信终端时,系统将做一些额外的工作
把名字解析成地址。有两个原因证明这额外的步骤是值得的:第一,相对于点分形式(或IPv6
中的十六进制数字串),人们更容易记住名字;第二,名字提供了一个间接层,使IP地址的
变化对用户不可见。在本书第一版的写作期间,网络服务器www.mkp.com的地址就改变过。
由于我们通常都使用网络服务器的名字,而且地址的改变很快就被反应到映射主机名和网络
地址的服务上(我们马上会对其进行更多的介绍),如www.mkp.com从之前的地址
208.164.121.48 对应到了现在的地址,这种变化对通过名字访问该网络服务器的程序是透明
的。
名字解析服务可以从各种各样的信息源获取信息。两个主要的信息源是域名系统(DNS,
Domain Name System)和本地配置数据库。DNS[ ]是一种分布式数据库,它将像
www.mkp.com这样的域名映射到真实的互联网地址和其他信息上。DNS协议[ ]允许连接到
互联网的主机通过TCP或UDP协议从DNS数据库中获取信息。本地配置数据库通常是一种与
具体操作系统相关的机制,用来实现本地名称与互联网地址的映射。
1.4 客户端和服务器
在前面的邮政和电话系统例子中,每次通信都是由发信方或打电话者发起,而另一方则
通过发回反馈信或接听电话来对通信的发起者作出响应。互联网通信也与这个过程类似。客
户端(client)和服务器(server)这两个术语代表了两种角色:客户端是通信的发起者,而
服务器程序则被动等待客户端发起通信,并对其作出响应。客户端与服务器组成了应用程序
(application)。客户端和服务器这两个术语对典型的情况作出了描述,服务器具有一定的
特殊能力,如提供数据库服务,并使任何客户端能够与之通信。
一个程序是作为客户端还是服务器,决定了它在与其对等端(peer)建立通信时使用的
套接字 API 的形式(客户端的对等端是服务器,反之亦然)。更进一步来说,客户端与服
务器端的区别非常重要,因为客户端首先需要知道服务器的地址和端口号,反之则不需要。
如果有必要,服务器可以使用套接字 API,从收到的第一个客户端通信消息中获取其地址信
息。这与打电话非常相似:被呼叫者不需要知道拨电话者的电话号码。就像打电话一样,只
要通信连接建立成功,服务器和客户端之间就没有区别了。
下载自:http://www.javaxxz.com 最方便的Java学习社区