套接字(
![](/icons/28506socket.gif)
)为两台计算机的间
![](/icons/28506de.gif)
通信提供了
![](/icons/28506yi.gif)
种机制
![](/icons/28506dou.gif)
在James Gosling注意到Java 语言的前
![](/icons/28506dou.gif)
套接字就早已赫赫有名
![](/icons/28506dou2.gif)
该语言只是让您不必了解底层操作系统
![](/icons/28506de.gif)
细节就能有效地使用套接字
1 客户机/服务器模型
在饭店里
![](/icons/28506dou.gif)
菜单上各种具有异国情调
![](/icons/28506de.gif)
食品映入你
![](/icons/28506de.gif)
眼帘
![](/icons/28506dou.gif)
于是你要了
![](/icons/28506yi.gif)
份pizza
![](/icons/28506dou2.gif)
几分钟后
![](/icons/28506dou.gif)
你用力咀嚼浇着融化
![](/icons/28506de.gif)
乳酪和其他你喜欢
![](/icons/28506de.gif)
配料
![](/icons/28506de.gif)
热pizza
![](/icons/28506dou2.gif)
你不知道
![](/icons/28506dou.gif)
也不想知道:侍者从那里弄来了pizza
![](/icons/28506dou.gif)
在制作过程中加进了什么
![](/icons/28506dou.gif)
以及配料是如何获得
![](/icons/28506de.gif)
上例中包含
![](/icons/28506de.gif)
实体有:美味
![](/icons/28506de.gif)
pizza、接受你定餐
![](/icons/28506de.gif)
侍者、制作pizza
![](/icons/28506de.gif)
厨房
![](/icons/28506dou.gif)
当然还有你
![](/icons/28506dou2.gif)
你是定pizza
![](/icons/28506de.gif)
顾客或客户
![](/icons/28506dou2.gif)
制作pizza
![](/icons/28506de.gif)
过程对于你而言是被封装
![](/icons/28506de.gif)
![](/icons/28506dou2.gif)
你
![](/icons/28506de.gif)
请求在厨房中被处理
![](/icons/28506dou.gif)
pizza制作完成后
![](/icons/28506dou.gif)
由侍者端给你
你所看到
![](/icons/28506de.gif)
就是
![](/icons/28506yi.gif)
个客户机/服务器模型
![](/icons/28506dou2.gif)
客户机向服务器发送
![](/icons/28506yi.gif)
个请求或命令
![](/icons/28506dou2.gif)
服务器处理客户机
![](/icons/28506de.gif)
请求
![](/icons/28506dou2.gif)
客户机和服务器的间
![](/icons/28506de.gif)
通讯是客户机/服务器模型中
![](/icons/28506de.gif)
![](/icons/28506yi.gif)
个重要组成部分
![](/icons/28506dou.gif)
通常通过网络进行
客户机/服务器模型是
![](/icons/28506yi.gif)
个应用
![](/icons/28506chengxu.gif)
开发框架
![](/icons/28506dou.gif)
该框架是为了将数据
![](/icons/28506de.gif)
表示和其内部
![](/icons/28506de.gif)
处理和存储分离开来而设计
![](/icons/28506de.gif)
![](/icons/28506dou2.gif)
客户机请求服务
![](/icons/28506dou.gif)
服务器为这些请求服务
![](/icons/28506dou2.gif)
请求通过网络从客户机传递到服务器
![](/icons/28506dou2.gif)
服务器所进行
![](/icons/28506de.gif)
处理对客户机而言是隐藏
![](/icons/28506de.gif)
![](/icons/28506dou2.gif)
![](/icons/28506yi.gif)
个服务器可以为多台客户机服务
多台客户机访问服务器
服务器和客户机不
![](/icons/28506yi.gif)
定是硬件组件
![](/icons/28506dou2.gif)
它们可以是工作啊同
![](/icons/28506yi.gif)
机器或区别机器上
![](/icons/28506de.gif)
![](/icons/28506chengxu.gif)
![](/icons/28506dou2.gif)
、
考虑
![](/icons/28506yi.gif)
个航空定票系统中
![](/icons/28506de.gif)
数据输入
![](/icons/28506chengxu.gif)
:数据----乘客名、航班号、飞行日期、目
![](/icons/28506de.gif)
地等可以被输入到前端----客户机
![](/icons/28506de.gif)
应用
![](/icons/28506chengxu.gif)
中
![](/icons/28506dou2.gif)
![](/icons/28506yi.gif)
旦数据输入的后
![](/icons/28506dou.gif)
客户机将数据发送到后端----服务器端
![](/icons/28506dou2.gif)
服务器处理数据并在数据库中保存数据
![](/icons/28506dou2.gif)
客户机/服务器模型
![](/icons/28506de.gif)
重要性在于所有
![](/icons/28506de.gif)
数据都存放在同
![](/icons/28506yi.gif)
地点
![](/icons/28506dou2.gif)
客户机从区别
![](/icons/28506de.gif)
地方访问同
![](/icons/28506yi.gif)
数据源
![](/icons/28506dou.gif)
服务器对所有
![](/icons/28506de.gif)
输入数据应用同样
![](/icons/28506de.gif)
检验规则
万维网为‘为什么要将数据
![](/icons/28506de.gif)
表示和其存储、处理分离开来’提供了
![](/icons/28506yi.gif)
个很好
![](/icons/28506de.gif)
例子
![](/icons/28506dou2.gif)
在Web上
![](/icons/28506dou.gif)
你无需控制最终用户用来访问你数据
![](/icons/28506de.gif)
平台和软件Software
![](/icons/28506dou2.gif)
你可以考虑编写出适用和每
![](/icons/28506yi.gif)
种潜在
![](/icons/28506de.gif)
目标平台
![](/icons/28506de.gif)
应用
![](/icons/28506chengxu.gif)
‘客户机/服务器应用
![](/icons/28506chengxu.gif)
![](/icons/28506de.gif)
服务器部分’管理通过多个客户机访问服务器
![](/icons/28506de.gif)
、多个用户共享
![](/icons/28506de.gif)
资源
![](/icons/28506dou2.gif)
表明‘客户机/服务器
![](/icons/28506chengxu.gif)
![](/icons/28506de.gif)
服务器部分’强大功能
![](/icons/28506de.gif)
最好例子应该是Web服务器
![](/icons/28506dou.gif)
它通过Internet将HTML页传递给区别
![](/icons/28506de.gif)
Web用户
Java 编程语言中最基本
![](/icons/28506de.gif)
特点是在Java中创建
![](/icons/28506de.gif)
![](/icons/28506chengxu.gif)
![](/icons/28506de.gif)
代码
![](/icons/28506de.gif)
可移植性
![](/icons/28506dou2.gif)
![](/icons/28506yinwei.gif)
具有其他语言所不具备
![](/icons/28506de.gif)
代码可移植性
![](/icons/28506dou.gif)
Java允许用户只要编写
![](/icons/28506yi.gif)
次应用
![](/icons/28506chengxu.gif)
![](/icons/28506dou.gif)
就可以在任何客户机系统上发布它
![](/icons/28506dou.gif)
并可以让客户机系统解释该
![](/icons/28506chengxu.gif)
![](/icons/28506dou2.gif)
这意味着:你只要写
![](/icons/28506yi.gif)
次代码
![](/icons/28506dou.gif)
就能使其在任何平台上运行
2 协议
当你同朋友交谈时
![](/icons/28506dou.gif)
你们遵循
![](/icons/28506yi.gif)
些暗含
![](/icons/28506de.gif)
规则(或协议)
![](/icons/28506dou2.gif)
例如:你们俩不能同时开始说话
![](/icons/28506dou.gif)
或连续不间断地说话
![](/icons/28506dou2.gif)
如果你们这样作
![](/icons/28506de.gif)
话
![](/icons/28506dou.gif)
谁也不能理解对方所说
![](/icons/28506de.gif)
东西
![](/icons/28506dou2.gif)
当你说话时
![](/icons/28506dou.gif)
你
![](/icons/28506de.gif)
朋友倾听
![](/icons/28506dou.gif)
反的亦然
![](/icons/28506dou2.gif)
你们以双方都能理解
![](/icons/28506de.gif)
语言和速度进行对话
当计算机的间进行通讯
![](/icons/28506de.gif)
时候
![](/icons/28506dou.gif)
也需要遵循
![](/icons/28506yi.gif)
定
![](/icons/28506de.gif)
规则
![](/icons/28506dou2.gif)
数据以包
![](/icons/28506de.gif)
形式从
![](/icons/28506yi.gif)
台机器发送到另
![](/icons/28506yi.gif)
台
![](/icons/28506dou2.gif)
这些规则管理数据打包、数据传输速度和重新 数据将其恢复成原始形式
![](/icons/28506dou2.gif)
这些规则被称为网络协议
![](/icons/28506dou2.gif)
网络协议是通过网络进行通讯
![](/icons/28506de.gif)
系统所遵循
![](/icons/28506de.gif)
![](/icons/28506yi.gif)
系列规则和惯例
![](/icons/28506dou2.gif)
连网软件Software通常实现有高低层次的分
![](/icons/28506de.gif)
多层协议
![](/icons/28506dou2.gif)
网络协议
![](/icons/28506de.gif)
例子有:TCP/IP、UDP、Apple Talk和NetBEUI
Java提供了
![](/icons/28506yi.gif)
个丰富
![](/icons/28506de.gif)
、支持网络
![](/icons/28506de.gif)
类库
![](/icons/28506dou.gif)
这些类使得应用
![](/icons/28506chengxu.gif)
能方便地访问网络资源
![](/icons/28506dou2.gif)
Java提供了两种通讯工具
![](/icons/28506dou2.gif)
它们是:使用用户报文协议(UDP)
![](/icons/28506de.gif)
报文和使用传输控制协议/因特网协议(TCP/IP)
![](/icons/28506de.gif)
Sockets(套接字)
数据报包是
![](/icons/28506yi.gif)
个字节
![](/icons/28506shuzu.gif)
从
![](/icons/28506yi.gif)
个
![](/icons/28506chengxu.gif)
(发送
![](/icons/28506chengxu.gif)
)传送到另
![](/icons/28506yi.gif)
个(接受
![](/icons/28506chengxu.gif)
)
![](/icons/28506dou2.gif)
由于数据报遵守UDP
![](/icons/28506dou.gif)
不保证发出
![](/icons/28506de.gif)
数据包必须到达目
![](/icons/28506de.gif)
地
![](/icons/28506dou2.gif)
数据报并不是可信赖
![](/icons/28506de.gif)
![](/icons/28506dou2.gif)
因此
![](/icons/28506dou.gif)
仅当传送少量数据时才使用
![](/icons/28506dou.gif)
而且发送者和接受者的间
![](/icons/28506de.gif)
距离间隔不大
![](/icons/28506dou.gif)
假如是网络交通高峰
![](/icons/28506dou.gif)
或接受
![](/icons/28506chengxu.gif)
正处理来自其他
![](/icons/28506chengxu.gif)
![](/icons/28506de.gif)
多个请求
![](/icons/28506dou.gif)
就有机会出现数据报包
![](/icons/28506de.gif)
丢失
Sockets套接字用TCP来进行通讯
![](/icons/28506dou2.gif)
套接字模型同其他模型相比
![](/icons/28506dou.gif)
优越性在于其不受客户请求来自何处
![](/icons/28506de.gif)
影响
![](/icons/28506dou2.gif)
只要客户机遵循TCP/IP协议
![](/icons/28506dou.gif)
服务器就会对它
![](/icons/28506de.gif)
请求提供服务
![](/icons/28506dou2.gif)
这意味着客户机可以是任何类型
![](/icons/28506de.gif)
计算机
![](/icons/28506dou2.gif)
客户机不再局限为UNIX、Windows、DOS或 Mac
![](/icons/28506int.gif)
osh平台
![](/icons/28506dou.gif)
因此
![](/icons/28506dou.gif)
网上所有遵循TCP/IP协议
![](/icons/28506de.gif)
计算机可以通过套接字互相通讯
3 Sockets套接字
3.1 Sockets概况
在客户机/服务器应用
![](/icons/28506chengxu.gif)
中
![](/icons/28506dou.gif)
服务器提供象处理数据库查询或修改数据库中
![](/icons/28506de.gif)
数据的类
![](/icons/28506de.gif)
服务
![](/icons/28506dou2.gif)
发生在客户机和服务器的间
![](/icons/28506de.gif)
通讯必须是可靠
![](/icons/28506de.gif)
![](/icons/28506dou.gif)
同时数据在客户机上
![](/icons/28506de.gif)
次序应该和服务器发送出来
![](/icons/28506de.gif)
次序相同
什么是套接字?
既然我们已经知道套接字扮演
![](/icons/28506de.gif)
角色
![](/icons/28506dou.gif)
那么剩下
![](/icons/28506de.gif)
问题是:什么是套接字?Bruce Eckel 在他
![](/icons/28506de.gif)
![](/icons/28506smhl.gif)
Java 编程思想
![](/icons/28506smhr.gif)
![](/icons/28506yi.gif)
书中这样描述套接字:套接字是
![](/icons/28506yi.gif)
种软件Software抽象
![](/icons/28506dou.gif)
用于表达两台机器的间
![](/icons/28506de.gif)
连接“终端”
![](/icons/28506dou2.gif)
对于
![](/icons/28506yi.gif)
个给定
![](/icons/28506de.gif)
连接
![](/icons/28506dou.gif)
每台机器上都有
![](/icons/28506yi.gif)
个套接字
![](/icons/28506dou.gif)
您也可以想象它们的间有
![](/icons/28506yi.gif)
条虚拟
![](/icons/28506de.gif)
“电缆”
![](/icons/28506dou.gif)
“电缆”
![](/icons/28506de.gif)
每
![](/icons/28506yi.gif)
端都插入到套接字中
![](/icons/28506dou2.gif)
当然
![](/icons/28506dou.gif)
机器的间
![](/icons/28506de.gif)
物理硬件和电缆连接都是完全未知
![](/icons/28506de.gif)
![](/icons/28506dou2.gif)
抽象
![](/icons/28506de.gif)
全部目
![](/icons/28506de.gif)
是使我们无须知道不必知道
![](/icons/28506de.gif)
细节
简言的
![](/icons/28506dou.gif)
![](/icons/28506yi.gif)
台机器上
![](/icons/28506de.gif)
套接字和另
![](/icons/28506yi.gif)
台机器上
![](/icons/28506de.gif)
套接字交谈就创建
![](/icons/28506yi.gif)
条通信通道
![](/icons/28506dou2.gif)
![](/icons/28506chengxu.gif)
员可以用该通道来在两台机器的间发送数据
![](/icons/28506dou2.gif)
当您发送数据时
![](/icons/28506dou.gif)
TCP/IP 协议栈
![](/icons/28506de.gif)
每
![](/icons/28506yi.gif)
层都会添加适当
![](/icons/28506de.gif)
报头信息来包装数据
![](/icons/28506dou2.gif)
这些报头帮助协议栈把您
![](/icons/28506de.gif)
数据送到目
![](/icons/28506de.gif)
地
![](/icons/28506dou2.gif)
好消息是 Java 语言通过"流"为您
![](/icons/28506de.gif)
代码提供数据
![](/icons/28506dou.gif)
从而隐藏了所有这些细节
![](/icons/28506dou.gif)
这也是为什么它们有时候被叫做流套接字(streaming
![](/icons/28506socket.gif)
)
![](/icons/28506de.gif)
原因
把套接字想成两端电话上
![](/icons/28506de.gif)
听筒
![](/icons/28506dou.gif)
我和您通过专用通道在我们
![](/icons/28506de.gif)
电话听筒上讲话和聆听
![](/icons/28506dou2.gif)
直到我们决定挂断电话
![](/icons/28506dou.gif)
对话才会结束(除非我们在使用蜂窝电话)
![](/icons/28506dou2.gif)
而且我们各自
![](/icons/28506de.gif)
电话线路都占线
![](/icons/28506dou.gif)
直到我们挂断电话
如果想在没有更高级机制如 ORB(以及 CORBA、RMI、IIOP 等等)开销
![](/icons/28506de.gif)
情况下进行两台计算机的间
![](/icons/28506de.gif)
通信
![](/icons/28506dou.gif)
那么套接字就适合您
![](/icons/28506dou2.gif)
套接字
![](/icons/28506de.gif)
低级细节相当棘手
![](/icons/28506dou2.gif)
幸运
![](/icons/28506de.gif)
是
![](/icons/28506dou.gif)
Java 平台给了您
![](/icons/28506yi.gif)
些虽然简单但却强大
![](/icons/28506de.gif)
更高级抽象
![](/icons/28506dou.gif)
使您可以容易地创建和使用套接字
传输控制协议(TCP)提供了
![](/icons/28506yi.gif)
条可靠
![](/icons/28506de.gif)
、点对点
![](/icons/28506de.gif)
通讯通道
![](/icons/28506dou.gif)
客户机/服务器应用
![](/icons/28506chengxu.gif)
可以用该通道互相通讯
![](/icons/28506dou2.gif)
要通过TCP进行通讯
![](/icons/28506dou.gif)
客户机和服务器
![](/icons/28506chengxu.gif)
建立连接并绑定套接字
![](/icons/28506dou2.gif)
套接字用于处理通过网络连接
![](/icons/28506de.gif)
应用
![](/icons/28506chengxu.gif)
的间
![](/icons/28506de.gif)
通讯
![](/icons/28506dou2.gif)
客户机和服务器的间更深入
![](/icons/28506de.gif)
通讯通过套接字完成
Java被设计成
![](/icons/28506yi.gif)
种连网语言
![](/icons/28506dou2.gif)
它通过将连接功能封装到套接字类里而使得网络编程更加容易
![](/icons/28506dou2.gif)
套接字类即Socket类(它创建
![](/icons/28506yi.gif)
个客户套接字)和ServerSocket类(它创建
![](/icons/28506yi.gif)
个服务器套接字)
![](/icons/28506dou2.gif)
套接字类大致介绍如下:
l Socket是基类
![](/icons/28506dou.gif)
它支持TCP协议
![](/icons/28506dou2.gif)
TCP是
![](/icons/28506yi.gif)
个可靠
![](/icons/28506de.gif)
流网络连接协议
![](/icons/28506dou2.gif)
Socket类提供了流输入/输出
![](/icons/28506de.gif)
思路方法
![](/icons/28506dou.gif)
使得从套接字中读出数据和往套接字中写数据都很容易
![](/icons/28506dou2.gif)
该类对于编写因特网上
![](/icons/28506de.gif)
通讯
![](/icons/28506chengxu.gif)
而言是必不可少
![](/icons/28506de.gif)
l ServerSocket是
![](/icons/28506yi.gif)
个因特网服务
![](/icons/28506chengxu.gif)
用来监听客户请求
![](/icons/28506de.gif)
类
![](/icons/28506dou2.gif)
ServerSocket实际上并不执行服务;而是创建了
![](/icons/28506yi.gif)
个Socket对象来代表客户机
![](/icons/28506dou2.gif)
通讯由创建
![](/icons/28506de.gif)
对象来完成
3.2 IP地址和端口
因特网服务器可以被认为是
![](/icons/28506yi.gif)
组套接字类
![](/icons/28506dou.gif)
它们提供了
![](/icons/28506yi.gif)
般称为服务
![](/icons/28506de.gif)
附加功能
![](/icons/28506dou2.gif)
服务
![](/icons/28506de.gif)
例子有:电子邮件、远程登录
![](/icons/28506de.gif)
Telnet、和通过网络传输文件
![](/icons/28506de.gif)
文件传输协议(FTP)
![](/icons/28506dou2.gif)
每种服务都和
![](/icons/28506yi.gif)
个端口相联系
![](/icons/28506dou2.gif)
端口是
![](/icons/28506yi.gif)
个数值地址
![](/icons/28506dou.gif)
通过它来处理服务请求(就象请求Web页
![](/icons/28506yi.gif)
样)
TCP协议需要两个数据项:IP地址和端口号
![](/icons/28506dou2.gif)
因此
![](/icons/28506dou.gif)
当键入http://www.jinnuo.com时
![](/icons/28506dou.gif)
你是如何进入金诺
![](/icons/28506de.gif)
主页呢?
因特网协议(IP)提供每
![](/icons/28506yi.gif)
项网络设备
![](/icons/28506dou2.gif)
这些设备都带有
![](/icons/28506yi.gif)
个称为IP地址
![](/icons/28506de.gif)
逻辑地址
![](/icons/28506dou2.gif)
由因特网协议提供
![](/icons/28506de.gif)
IP地址具有特定
![](/icons/28506de.gif)
形式
![](/icons/28506dou2.gif)
每个IP地址都是32位
![](/icons/28506de.gif)
数值
![](/icons/28506dou.gif)
表示4个范围在0到255的间
![](/icons/28506de.gif)
8位数值金诺已经注册了它
![](/icons/28506de.gif)
名字
![](/icons/28506dou.gif)
分配给http://www.jinnuo.com
![](/icons/28506de.gif)
IP地址为192.168.0.110
注意:域名服务或DNS服务是将http://www.jinnuo.com翻译成192.168.0.110
![](/icons/28506de.gif)
服务
![](/icons/28506dou2.gif)
这使你可以键入http://www.jinnuo.com而不必记住IP地址
![](/icons/28506dou2.gif)
想象
![](/icons/28506yi.gif)
下
![](/icons/28506dou.gif)
如何可能记住所有需要访问
![](/icons/28506de.gif)
站点
![](/icons/28506de.gif)
IP地址!有趣
![](/icons/28506de.gif)
是
![](/icons/28506yi.gif)
个网络名可以映射到许多IP地址
![](/icons/28506dou2.gif)
对于经常访问
![](/icons/28506de.gif)
站点可能需要这
![](/icons/28506yi.gif)
功能
![](/icons/28506dou.gif)
![](/icons/28506yinwei.gif)
这些站点容纳大量
![](/icons/28506de.gif)
信息
![](/icons/28506dou.gif)
并需要多个IP地址来提供业务服务
![](/icons/28506dou2.gif)
例如:192.168.0.110
![](/icons/28506de.gif)
实际
![](/icons/28506de.gif)
内部名称为http://www.jinnuo.com
![](/icons/28506dou2.gif)
DNS可以将分配给jinnuo Ltd.
![](/icons/28506de.gif)
![](/icons/28506yi.gif)
系列IP地址翻译成http://www.jinnuo.com
如果没有指明端口号
![](/icons/28506dou.gif)
则使用服务文件中服务器
![](/icons/28506de.gif)
端口
![](/icons/28506dou2.gif)
每种协议有
![](/icons/28506yi.gif)
个缺省
![](/icons/28506de.gif)
端口号
![](/icons/28506dou.gif)
在端口号未指明时使用该缺省端口号
端口号 应用
21 FTP.传输文件
23 Telnet.提供远程登录
25 SMTP.传递邮件信息
67 BOOTP.在启动时提供配置情况
80 HTTP.传输Web页
109 POP.使用户能访问远程系统中
![](/icons/28506de.gif)
邮箱
让我们再来看
![](/icons/28506yi.gif)
下URL:http://www.jinnuo.com
URL
![](/icons/28506de.gif)
第
![](/icons/28506yi.gif)
部分(http)意味着你正在使用超文本传输协议(HTTP)
![](/icons/28506dou.gif)
该协议处理Web文档
![](/icons/28506dou2.gif)
如果没有指明文件
![](/icons/28506dou.gif)
大多数
![](/icons/28506de.gif)
Web服务器会取
![](/icons/28506yi.gif)
个叫index.html文件
![](/icons/28506dou2.gif)
因此
![](/icons/28506dou.gif)
IP地址和端口既可以通过明确指出URL各部分来决定
![](/icons/28506dou.gif)
也可以由缺省值决定
4 创建Socket客户
我们将在本部分讨论
![](/icons/28506de.gif)
举例将阐明在 Java 代码中如何使用 Socket 和 ServerSocket
![](/icons/28506dou2.gif)
客户机用 Socket 连接到服务器
![](/icons/28506dou2.gif)
服务器用 ServerSocket 在端口 1001 侦听
![](/icons/28506dou2.gif)
客户机请求服务器 C: 驱动器上
![](/icons/28506de.gif)
文件内容
创建 RemoteFileClient 类
import java.io.*;
import java.net.*;
public
![](/icons/28506class.gif)
RemoteFileClient {
protected BufferedReader
![](/icons/28506socket.gif)
Reader;
protected Pr
![](/icons/28506int.gif)
Writer
![](/icons/28506socket.gif)
Writer;
protected String hostIp;
protected
![](/icons/28506int.gif)
hostPort;
//构造思路方法
public RemoteFileClient(String hostIp,
![](/icons/28506int.gif)
hostPort) {
this.hostIp = hostIp;
this.hostPort=hostPort;
}
//向服务器请求文件
![](/icons/28506de.gif)
内容
public String getFile(String fileNameToGet) {
StringBuffer fileLines =
![](/icons/28506new.gif)
StringBuffer
![](/icons/28506kh.gif)
;
try {
![](/icons/28506socket.gif)
Writer.pr
![](/icons/28506int.gif)
ln(fileNameToGet);
![](/icons/28506socket.gif)
Writer.flush
![](/icons/28506kh.gif)
;
String line = null;
while((line=
![](/icons/28506socket.gif)
Reader.readLine
![](/icons/28506kh.gif)
)!=null)
fileLines.append(line+"\n");
}
catch(IOException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Error reading from file: "+fileNameToGet);
}
![](/icons/28506return.gif)
fileLines.toString
![](/icons/28506kh.gif)
;
}
//连接到远程服务器
public void
![](/icons/28506set.gif)
UpConnection
![](/icons/28506kh.gif)
{
try {
Socket client =
![](/icons/28506new.gif)
Socket(hostIp,hostPort);
![](/icons/28506socket.gif)
Reader =
![](/icons/28506new.gif)
BufferedReader(
![](/icons/28506new.gif)
InputStreamReader(client.getInputStream
![](/icons/28506kh.gif)
));
![](/icons/28506socket.gif)
Writer =
![](/icons/28506new.gif)
Pr
![](/icons/28506int.gif)
Writer(client.getOutputStream
![](/icons/28506kh.gif)
);
}
catch(UnknownHostException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Error1
![](/icons/28506set.gif)
ting up
![](/icons/28506socket.gif)
connection: unknown host at "+hostIp+":"+hostPort);
}
catch(IOException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Error2
![](/icons/28506set.gif)
ting up
![](/icons/28506socket.gif)
connection: "+e);
}
}
//断开远程服务器
public void tearDownConnection
![](/icons/28506kh.gif)
{
try {
![](/icons/28506socket.gif)
Writer.close
![](/icons/28506kh.gif)
;
![](/icons/28506socket.gif)
Reader.close
![](/icons/28506kh.gif)
;
}catch(IOException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Error tearing down
![](/icons/28506socket.gif)
connection: "+e);
}
}
public
![](/icons/28506static.gif)
void
![](/icons/28506main.gif)
(String args
![](/icons/28506zhk2.gif)
) {
RemoteFileClient remoteFileClient =
![](/icons/28506new.gif)
RemoteFileClient("127.0.0.1",1001);
remoteFileClient.
![](/icons/28506set.gif)
UpConnection
![](/icons/28506kh.gif)
;
StringBuffer fileContents =
![](/icons/28506new.gif)
StringBuffer
![](/icons/28506kh.gif)
;
fileContents.append(remoteFileClient.getFile("RemoteFileServer.java"));
//remoteFileClient.tearDownConnection
![](/icons/28506kh.gif)
;
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln(fileContents);
}
}
首先我们导入 java.net 和 java.io
![](/icons/28506dou2.gif)
java.net 包为您提供您需要
![](/icons/28506de.gif)
套接字工具
![](/icons/28506dou2.gif)
java.io 包为您提供对流进行读写
![](/icons/28506de.gif)
工具
![](/icons/28506dou.gif)
这是您和 TCP 套接字通信
![](/icons/28506de.gif)
唯
![](/icons/28506yi.gif)
途径
我们给我们
![](/icons/28506de.gif)
类例子变量以支持对套接字流
![](/icons/28506de.gif)
读写和存储我们将连接到
![](/icons/28506de.gif)
远程主机
![](/icons/28506de.gif)
详细信息
我们类
![](/icons/28506de.gif)
构造器有两个参数:远程主机
![](/icons/28506de.gif)
IP地址和端口号各
![](/icons/28506yi.gif)
个
![](/icons/28506dou.gif)
而且构造器将它们赋给例子变量
我们
![](/icons/28506de.gif)
类有
![](/icons/28506yi.gif)
个
![](/icons/28506main.gif)
![](/icons/28506kh.gif)
思路方法和 3个其它思路方法
![](/icons/28506dou2.gif)
稍后我们将探究这些思路方法
![](/icons/28506de.gif)
细节
![](/icons/28506dou2.gif)
现在您只需知道
![](/icons/28506set.gif)
UpConnection
![](/icons/28506kh.gif)
将连接到远程服务器
![](/icons/28506dou.gif)
getFile
![](/icons/28506kh.gif)
将向远程服务器请求 fileNameToGet
![](/icons/28506de.gif)
内容以及 tearDownConnection
![](/icons/28506kh.gif)
将从远程服务器上断开
实现
![](/icons/28506main.gif)
这里我们实现
![](/icons/28506main.gif)
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou.gif)
它将创建 RemoteFileClient 并用它来获取远程文件
![](/icons/28506de.gif)
内容
![](/icons/28506dou.gif)
然后打印结果
![](/icons/28506dou2.gif)
![](/icons/28506main.gif)
![](/icons/28506kh.gif)
思路方法用主机
![](/icons/28506de.gif)
IP 地址和端口号例子化
![](/icons/28506yi.gif)
个新 RemoteFileClient(客户机)
![](/icons/28506dou2.gif)
然后
![](/icons/28506dou.gif)
我们告诉客户机建立
![](/icons/28506yi.gif)
个到主机
![](/icons/28506de.gif)
连接
![](/icons/28506dou2.gif)
接着
![](/icons/28506dou.gif)
我们告诉客户机获取主机上
![](/icons/28506yi.gif)
个指定文件
![](/icons/28506de.gif)
内容
![](/icons/28506dou2.gif)
最后
![](/icons/28506dou.gif)
我们告诉客户机断开它到主机
![](/icons/28506de.gif)
连接
![](/icons/28506dou2.gif)
我们把文件内容打印到控制台
![](/icons/28506dou.gif)
只是为了证明
![](/icons/28506yi.gif)
切都是按计划进行
![](/icons/28506de.gif)
建立连接
这里我们实现
![](/icons/28506set.gif)
UpConnection
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou.gif)
它将创建我们
![](/icons/28506de.gif)
Socket 并让我们访问该套接字
![](/icons/28506de.gif)
流:
public void
![](/icons/28506set.gif)
UpConnection
![](/icons/28506kh.gif)
{
try {
Socket client =
![](/icons/28506new.gif)
Socket(hostIp,hostPort);
![](/icons/28506socket.gif)
Reader =
![](/icons/28506new.gif)
BufferedReader(
![](/icons/28506new.gif)
InputStreamReader(client.getInputStream
![](/icons/28506kh.gif)
));
![](/icons/28506socket.gif)
Writer =
![](/icons/28506new.gif)
Pr
![](/icons/28506int.gif)
Writer(client.getOutputStream
![](/icons/28506kh.gif)
);
}
catch(UnknownHostException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Error1
![](/icons/28506set.gif)
ting up
![](/icons/28506socket.gif)
connection: unknown host at "+hostIp+":"+hostPort);
}
catch(IOException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Error2
![](/icons/28506set.gif)
ting up
![](/icons/28506socket.gif)
connection: "+e);
}
}
![](/icons/28506set.gif)
UpConnection
![](/icons/28506kh.gif)
思路方法用主机
![](/icons/28506de.gif)
IP 地址和端口号创建
![](/icons/28506yi.gif)
个 Socket:
Socket client =
![](/icons/28506new.gif)
Socket(hostIp, hostPort);
我们把 Socket
![](/icons/28506de.gif)
InputStream 包装进 BufferedReader 以使我们能够读取流
![](/icons/28506de.gif)
行
![](/icons/28506dou2.gif)
然后
![](/icons/28506dou.gif)
我们把 Socket
![](/icons/28506de.gif)
OutputStream 包装进 Pr
![](/icons/28506int.gif)
Writer 以使我们能够发送文件请求到服务器:
![](/icons/28506socket.gif)
Reader =
![](/icons/28506new.gif)
BufferedReader(
![](/icons/28506new.gif)
InputStreamReader(client.getInputStream
![](/icons/28506kh.gif)
));
![](/icons/28506socket.gif)
Writer =
![](/icons/28506new.gif)
Pr
![](/icons/28506int.gif)
Writer(client.getOutputStream
![](/icons/28506kh.gif)
);
请记住我们
![](/icons/28506de.gif)
客户机和服务器只是来回传送字节
![](/icons/28506dou2.gif)
客户机和服务器都必须知道另
![](/icons/28506yi.gif)
方即将发送
![](/icons/28506de.gif)
是什么以使它们能够作出适当
![](/icons/28506de.gif)
响应
![](/icons/28506dou2.gif)
在这个案例中
![](/icons/28506dou.gif)
服务器知道我们将发送
![](/icons/28506yi.gif)
条有效
![](/icons/28506de.gif)
文件路径
当您例子化
![](/icons/28506yi.gif)
个 Socket 时
![](/icons/28506dou.gif)
将抛出 UnknownHostException
![](/icons/28506dou2.gif)
这里我们不特别处理它
![](/icons/28506dou.gif)
但我们打印
![](/icons/28506yi.gif)
些信息到控制台以告诉我们发生了什么
![](/icons/28506cuowu.gif)
![](/icons/28506dou2.gif)
同样地
![](/icons/28506dou.gif)
当我们试图获取 Socket
![](/icons/28506de.gif)
InputStream 或 OutputStream 时
![](/icons/28506dou.gif)
如果抛出了
![](/icons/28506yi.gif)
个
![](/icons/28506yi.gif)
般 IOException
![](/icons/28506dou.gif)
我们也打印
![](/icons/28506yi.gif)
些信息到控制台
和主机交谈
这里我们实现 getFile
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou.gif)
它将告诉服务器我们想要什么文件并在服务器传回其内容时接收该内容
public String getFile(String fileNameToGet) {
StringBuffer fileLines =
![](/icons/28506new.gif)
StringBuffer
![](/icons/28506kh.gif)
;
try {
![](/icons/28506socket.gif)
Writer.pr
![](/icons/28506int.gif)
ln(fileNameToGet);
![](/icons/28506socket.gif)
Writer.flush
![](/icons/28506kh.gif)
;
String line = null;
while((line=
![](/icons/28506socket.gif)
Reader.readLine
![](/icons/28506kh.gif)
)!=null)
fileLines.append(line+"\n");
}
catch(IOException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Error reading from file: "+fileNameToGet);
}
![](/icons/28506return.gif)
fileLines.toString
![](/icons/28506kh.gif)
;
}
对getFile
![](/icons/28506kh.gif)
思路方法
![](/icons/28506de.gif)
![](/icons/28506diaoyong.gif)
要求
![](/icons/28506yi.gif)
个有效
![](/icons/28506de.gif)
文件路径String
![](/icons/28506dou2.gif)
它首先创建名为fileLines
![](/icons/28506de.gif)
StringBuffer
![](/icons/28506dou.gif)
fileLines 用于存储我们读自服务器上
![](/icons/28506de.gif)
文件
![](/icons/28506de.gif)
每
![](/icons/28506yi.gif)
行
StringBuffer fileLines =
![](/icons/28506new.gif)
StringBuffer
![](/icons/28506kh.gif)
;
在 try{}catch{} 块中
![](/icons/28506dou.gif)
我们用 Pr
![](/icons/28506int.gif)
Writer 把请求发送到主机
![](/icons/28506dou.gif)
Pr
![](/icons/28506int.gif)
Writer 是我们在创建连接期间建立
![](/icons/28506de.gif)
![](/icons/28506socket.gif)
Writer.pr
![](/icons/28506int.gif)
ln(fileNameToGet);
![](/icons/28506socket.gif)
Writer.flush
![](/icons/28506kh.gif)
;
请注意这里我们是 flush
![](/icons/28506kh.gif)
该 Pr
![](/icons/28506int.gif)
Writer
![](/icons/28506dou.gif)
而不是关闭它
![](/icons/28506dou2.gif)
这迫使数据被发送到服务器而不关闭 Socket
![](/icons/28506yi.gif)
旦我们已经写到 Socket
![](/icons/28506dou.gif)
我们就希望有
![](/icons/28506yi.gif)
些响应
![](/icons/28506dou2.gif)
我们不得不在 Socket
![](/icons/28506de.gif)
InputStream 上等待它
![](/icons/28506dou.gif)
我们通过在 while 循环中
![](/icons/28506diaoyong.gif)
BufferedReader 上
![](/icons/28506de.gif)
readLine
![](/icons/28506kh.gif)
来达到这个目
![](/icons/28506de.gif)
![](/icons/28506dou2.gif)
我们把每
![](/icons/28506yi.gif)
个返回行附加到 fileLines StringBuffer(带有
![](/icons/28506yi.gif)
个换行符以保护行):
String line = null; while((line=
![](/icons/28506socket.gif)
Reader.readLine
![](/icons/28506kh.gif)
)!=null) fileLines.append(line+"\n");
断开连接
这里我们实现 tearDownConnection
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou.gif)
它将在我们使用完毕连接后负责“清除”
![](/icons/28506dou2.gif)
tearDownConnection
![](/icons/28506kh.gif)
思路方法只是分别关闭我们在Socket
![](/icons/28506de.gif)
InputStream和OutputStream上创建
![](/icons/28506de.gif)
BufferedReader和Pr
![](/icons/28506int.gif)
Writer
![](/icons/28506dou2.gif)
这样做会关闭我们从Socket获取
![](/icons/28506de.gif)
底层流
![](/icons/28506dou.gif)
所以我们必须捕捉可能
![](/icons/28506de.gif)
IOException
整理总结
![](/icons/28506yi.gif)
下客户机
我们
![](/icons/28506de.gif)
类研究完了
![](/icons/28506dou2.gif)
在我们继续往前讨论服务器端
![](/icons/28506de.gif)
情况的前
![](/icons/28506dou.gif)
让我们回顾
![](/icons/28506yi.gif)
下创建和使用 Socket
![](/icons/28506de.gif)
步骤:
1. 用您想连接
![](/icons/28506de.gif)
机器
![](/icons/28506de.gif)
IP 地址和端口例子化 Socket(如有问题则抛出 Exception)
2. 获取 Socket 上
![](/icons/28506de.gif)
流以进行读写
3. 把流包装进 BufferedReader/Pr
![](/icons/28506int.gif)
Writer
![](/icons/28506de.gif)
例子
![](/icons/28506dou.gif)
如果这样做能使事情更简单
![](/icons/28506de.gif)
话
4. 对 Socket 进行读写
5. 关闭打开
![](/icons/28506de.gif)
流
5 创建服务器Socket
创建 RemoteFileServer 类
import java.io.*;
import java.net.*;
public
![](/icons/28506class.gif)
RemoteFileServer {
![](/icons/28506int.gif)
listenPort;
public RemoteFileServer(
![](/icons/28506int.gif)
listenPort) {
this.listenPort=listenPort;
}
//允许客户机连接到服务器,等待客户机请求
public void acceptConnections
![](/icons/28506kh.gif)
{
try {
ServerSocket server =
![](/icons/28506new.gif)
ServerSocket(listenPort);
Socket incomingConnection = null;
while(true) {
incomingConnection = server.accept
![](/icons/28506kh.gif)
;
handleConnection(incomingConnection);
}
}
catch(BindException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Unable to bind to port "+listenPort);
}
catch(IOException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Unable to instantiate a ServerSocket _disibledevent=>
}
}
//和客户机Socket交互以将客户机所请求
![](/icons/28506de.gif)
文件
![](/icons/28506de.gif)
内容发送到客户机
public void handleConnection(Socket incomingConnection) {
try {
OutputStream outputToSocket = incomingConnection.getOutputStream
![](/icons/28506kh.gif)
;
InputStream inputFromSocket = incomingConnection.getInputStream
![](/icons/28506kh.gif)
;
BufferedReader streamReader =
![](/icons/28506new.gif)
BufferedReader(
![](/icons/28506new.gif)
InputStreamReader(inputFromSocket));
FileReader fileReader =
![](/icons/28506new.gif)
FileReader(
![](/icons/28506new.gif)
File(streamReader.readLine
![](/icons/28506kh.gif)
));
BufferedReader bufferedFileReader =
![](/icons/28506new.gif)
BufferedReader(fileReader);
Pr
![](/icons/28506int.gif)
Writer streamWriter =
![](/icons/28506new.gif)
Pr
![](/icons/28506int.gif)
Writer(incomingConnection.getOutputStream
![](/icons/28506kh.gif)
);
String line = null;
while((line=bufferedFileReader.readLine
![](/icons/28506kh.gif)
)!=null){
streamWriter.pr
![](/icons/28506int.gif)
ln(line);
}
fileReader.close
![](/icons/28506kh.gif)
;
streamWriter.close
![](/icons/28506kh.gif)
;
streamReader.close
![](/icons/28506kh.gif)
;
}
catch(Exception e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Error handling a client: "+e);
e.pr
![](/icons/28506int.gif)
StackTrace
![](/icons/28506kh.gif)
;
}
}
public
![](/icons/28506static.gif)
void
![](/icons/28506main.gif)
(String args
![](/icons/28506zhk2.gif)
) {
RemoteFileServer server =
![](/icons/28506new.gif)
RemoteFileServer(1001);
server.acceptConnections
![](/icons/28506kh.gif)
;
}
}
跟客户机中
![](/icons/28506yi.gif)
样
![](/icons/28506dou.gif)
我们首先导入java.net
![](/icons/28506de.gif)
java.io
![](/icons/28506dou2.gif)
接着
![](/icons/28506dou.gif)
我们给我们
![](/icons/28506de.gif)
类
![](/icons/28506yi.gif)
个例子变量以保存端口
![](/icons/28506dou.gif)
我们从该端口侦听进入
![](/icons/28506de.gif)
连接
![](/icons/28506dou2.gif)
缺省情况下
![](/icons/28506dou.gif)
端口是1001
我们
![](/icons/28506de.gif)
类有
![](/icons/28506yi.gif)
个
![](/icons/28506main.gif)
![](/icons/28506kh.gif)
思路方法和两个其它思路方法
![](/icons/28506dou2.gif)
稍后我们将探究这些思路方法
![](/icons/28506de.gif)
细节
![](/icons/28506dou2.gif)
现在您只需知道acceptConnections
![](/icons/28506kh.gif)
将允许客户机连接到服务器以及handleConnection
![](/icons/28506kh.gif)
和客户机Socket交互以将您所请求
![](/icons/28506de.gif)
文件
![](/icons/28506de.gif)
内容发送到客户机
实现
![](/icons/28506main.gif)
这里我们实现
![](/icons/28506main.gif)
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou.gif)
它将创建RemoteFileServer并告诉它接受连接:服务器端
![](/icons/28506de.gif)
![](/icons/28506main.gif)
![](/icons/28506kh.gif)
思路方法中
![](/icons/28506dou.gif)
我们例子化
![](/icons/28506yi.gif)
个新 RemoteFileServer
![](/icons/28506dou.gif)
它将在侦听端口(1001)上侦听进入
![](/icons/28506de.gif)
连接请求
![](/icons/28506dou2.gif)
然后我们
![](/icons/28506diaoyong.gif)
acceptConnections
![](/icons/28506kh.gif)
来告诉该 server进行侦听
接受连接
这里我们实现 acceptConnections
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou.gif)
它将创建
![](/icons/28506yi.gif)
个 ServerSocket 并等待连接请求:
public void acceptConnections
![](/icons/28506kh.gif)
{
try {
ServerSocket server =
![](/icons/28506new.gif)
ServerSocket(listenPort);
Socket incomingConnection = null;
while(true) {
incomingConnection = server.accept
![](/icons/28506kh.gif)
;
handleConnection(incomingConnection);
}
}
catch(BindException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Unable to bind to port "+listenPort);
}
catch(IOException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Unable to instantiate a ServerSocket _disibledevent=>InputStream inputFromSocket = incomingConnection.getInputStream
![](/icons/28506kh.gif)
;
BufferedReader streamReader =
![](/icons/28506new.gif)
BufferedReader(
![](/icons/28506new.gif)
InputStreamReader(inputFromSocket));
FileReader fileReader =
![](/icons/28506new.gif)
FileReader(
![](/icons/28506new.gif)
File(streamReader.readLine
![](/icons/28506kh.gif)
));
BufferedReader bufferedFileReader =
![](/icons/28506new.gif)
BufferedReader(fileReader);
Pr
![](/icons/28506int.gif)
Writer streamWriter =
![](/icons/28506new.gif)
Pr
![](/icons/28506int.gif)
Writer(incomingConnection.getOutputStream
![](/icons/28506kh.gif)
);
String line = null;
while((line=bufferedFileReader.readLine
![](/icons/28506kh.gif)
)!=null){
streamWriter.pr
![](/icons/28506int.gif)
ln(line);
}
fileReader.close
![](/icons/28506kh.gif)
;
streamWriter.close
![](/icons/28506kh.gif)
;
streamReader.close
![](/icons/28506kh.gif)
;
}
catch(Exception e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Error handling a client: "+e);
e.pr
![](/icons/28506int.gif)
StackTrace
![](/icons/28506kh.gif)
;
}
}
跟在客户机中
![](/icons/28506yi.gif)
样
![](/icons/28506dou.gif)
我们用getOutputStream
![](/icons/28506kh.gif)
和getInputStream
![](/icons/28506kh.gif)
来获取和我们刚创建
![](/icons/28506de.gif)
Socket相关联
![](/icons/28506de.gif)
流
![](/icons/28506dou2.gif)
跟在客户机端
![](/icons/28506yi.gif)
样
![](/icons/28506dou.gif)
我们把InputStream包装进BufferedReader
![](/icons/28506dou.gif)
把OutputStream包装进Pr
![](/icons/28506int.gif)
Writer
![](/icons/28506dou2.gif)
在服务器端上
![](/icons/28506dou.gif)
我们需要添加
![](/icons/28506yi.gif)
些代码
![](/icons/28506dou.gif)
用来读取目标文件和把内容逐行发送到客户机
![](/icons/28506dou2.gif)
这里是重要
![](/icons/28506de.gif)
代码:
FileReader fileReader =
![](/icons/28506new.gif)
FileReader(
![](/icons/28506new.gif)
File(streamReader.readLine
![](/icons/28506kh.gif)
)); BufferedReader bufferedFileReader =
![](/icons/28506new.gif)
BufferedReader(fileReader); String line = null; while((line=bufferedFileReader.readLine
![](/icons/28506kh.gif)
)!=null) { streamWriter.pr
![](/icons/28506int.gif)
ln(line); }
这些代码值得详细解释
![](/icons/28506dou2.gif)
让我们
![](/icons/28506yi.gif)
点
![](/icons/28506yi.gif)
点来看:
FileReader fileReader =
![](/icons/28506new.gif)
FileReader(
![](/icons/28506new.gif)
File(streamReader.readLine
![](/icons/28506kh.gif)
));
首先
![](/icons/28506dou.gif)
我们使用Socket
![](/icons/28506de.gif)
InputStream
![](/icons/28506de.gif)
BufferedReader
![](/icons/28506dou2.gif)
我们应该获取
![](/icons/28506yi.gif)
条有效
![](/icons/28506de.gif)
文件路径
![](/icons/28506dou.gif)
所以我们用该路径名构造
![](/icons/28506yi.gif)
个新File
![](/icons/28506dou2.gif)
我们创建
![](/icons/28506yi.gif)
个新FileReader来处理读文件
![](/icons/28506de.gif)
操作
BufferedReader bufferedFileReader =
![](/icons/28506new.gif)
BufferedReader(fileReader);
这里我们把FileReader包装进BufferedReader以使我们能够逐行地读该文件
接着
![](/icons/28506dou.gif)
我们
![](/icons/28506diaoyong.gif)
BufferedReader
![](/icons/28506de.gif)
readLine
![](/icons/28506kh.gif)
![](/icons/28506dou2.gif)
这个
![](/icons/28506diaoyong.gif)
将造成阻塞直到有字节到来
![](/icons/28506dou2.gif)
我们获取
![](/icons/28506yi.gif)
些字节的后就把它们放到本地
![](/icons/28506de.gif)
line变量中
![](/icons/28506dou.gif)
然后再写出到客户机上
![](/icons/28506dou2.gif)
完成读写操作的后
![](/icons/28506dou.gif)
我们就关闭打开
![](/icons/28506de.gif)
流
请注意我们在完成从Socket
![](/icons/28506de.gif)
读操作的后关闭streamWriter和streamReader
![](/icons/28506dou2.gif)
您或许会问我们为什么不在读取文件名的后立刻关闭 streamReader
![](/icons/28506dou2.gif)
原因是当您这样做时
![](/icons/28506dou.gif)
您
![](/icons/28506de.gif)
客户机将不会获取任何数据
![](/icons/28506dou2.gif)
如果您在关闭streamWriter的前关闭 streamReader
![](/icons/28506dou.gif)
则您可以往Socket写任何东西
![](/icons/28506dou.gif)
但却没有任何数据能通过通道(通道被关闭了)
整理总结
![](/icons/28506yi.gif)
下服务器
在我们接着讨论另
![](/icons/28506yi.gif)
个更实际
![](/icons/28506de.gif)
举例的前
![](/icons/28506dou.gif)
让我们回顾
![](/icons/28506yi.gif)
下创建和使用ServerSocket
![](/icons/28506de.gif)
步骤:
1. 用
![](/icons/28506yi.gif)
个您想让它侦听传入客户机连接
![](/icons/28506de.gif)
端口来例子化
![](/icons/28506yi.gif)
个ServerSocket(如有问题则抛出 Exception)
2.
![](/icons/28506diaoyong.gif)
ServerSocket
![](/icons/28506de.gif)
accept
![](/icons/28506kh.gif)
以在等待连接期间造成阻塞
3. 获取位于该底层Socket
![](/icons/28506de.gif)
流以进行读写操作
4. 按使事情简单化
![](/icons/28506de.gif)
原则包装流
5. 对Socket进行读写
6. 关闭打开
![](/icons/28506de.gif)
流(并请记住
![](/icons/28506dou.gif)
永远不要在关闭Writer的前关闭Reader)
6 创建多线程Socket服务器
前面
![](/icons/28506de.gif)
举例教给您基础知识
![](/icons/28506dou.gif)
但并不能令您更深入
![](/icons/28506dou2.gif)
如果您到此就停止了
![](/icons/28506dou.gif)
那么您
![](/icons/28506yi.gif)
次只能处理
![](/icons/28506yi.gif)
台客户机
![](/icons/28506dou2.gif)
原因是handleConnection
![](/icons/28506kh.gif)
是
![](/icons/28506yi.gif)
个阻塞思路方法
![](/icons/28506dou2.gif)
只有当它完成了对当前连接
![](/icons/28506de.gif)
处理时
![](/icons/28506dou.gif)
服务器才能接受另
![](/icons/28506yi.gif)
个客户机
![](/icons/28506dou2.gif)
在多数时候
![](/icons/28506dou.gif)
您将需要(也有必要)
![](/icons/28506yi.gif)
个多线程服务器
创建 MultithreadedRemoteFileServer 类
import java.io.*;
import java.net.*;
public
![](/icons/28506class.gif)
MultithreadedRemoteFileServer {
![](/icons/28506int.gif)
listenPort;
public MultithreadedRemoteFileServer(
![](/icons/28506int.gif)
listenPort) {
this.listenPort=listenPort;
}
//允许客户机连接到服务器,等待客户机请求
public void acceptConnections
![](/icons/28506kh.gif)
{
try {
ServerSocket server =
![](/icons/28506new.gif)
ServerSocket(listenPort, 5);
Socket incomingConnection = null;
while(true) {
incomingConnection = server.accept
![](/icons/28506kh.gif)
;
handleConnection(incomingConnection);
}
}
catch(BindException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Unable to bind to port "+listenPort);
}
catch(IOException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Unable to instantiate a ServerSocket _disibledevent=>server.acceptConnections
![](/icons/28506kh.gif)
;
}
}
这里我们实现改动过acceptConnections
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou.gif)
它将创建
![](/icons/28506yi.gif)
个能够处理待发请求
![](/icons/28506de.gif)
ServerSocket
![](/icons/28506dou.gif)
并告诉ServerSocket接受连接
新
![](/icons/28506de.gif)
server 仍然需要acceptConnections
![](/icons/28506kh.gif)
![](/icons/28506dou.gif)
所以这些代码实际上是
![](/icons/28506yi.gif)
样
![](/icons/28506de.gif)
![](/icons/28506dou2.gif)
突出显示
![](/icons/28506de.gif)
行表示
![](/icons/28506yi.gif)
个重大
![](/icons/28506de.gif)
区别
![](/icons/28506dou2.gif)
对这个多线程版
![](/icons/28506dou.gif)
我们现在可以指定客户机请求
![](/icons/28506de.gif)
最大数目
![](/icons/28506dou.gif)
这些请求都能在例子化ServerSocket期间处于待发状态
![](/icons/28506dou2.gif)
如果我们没有指定客户机请求
![](/icons/28506de.gif)
最大数目
![](/icons/28506dou.gif)
则我们假设使用缺省值50
这里是它
![](/icons/28506de.gif)
工作机制
![](/icons/28506dou2.gif)
假设我们指定待发数(backlog 值)是5并且有 5台客户机请求连接到我们
![](/icons/28506de.gif)
服务器
![](/icons/28506dou2.gif)
我们
![](/icons/28506de.gif)
服务器将着手处理第
![](/icons/28506yi.gif)
个连接
![](/icons/28506dou.gif)
但处理该连接需要很长时间
![](/icons/28506dou2.gif)
由于我们
![](/icons/28506de.gif)
待发值是5
![](/icons/28506dou.gif)
所以我们
![](/icons/28506yi.gif)
次可以放 5个请求到队列中
![](/icons/28506dou2.gif)
我们正在处理
![](/icons/28506yi.gif)
个
![](/icons/28506dou.gif)
所以这意味着还有其它 5个正在等待
![](/icons/28506dou2.gif)
等待
![](/icons/28506de.gif)
和正在处理
![](/icons/28506de.gif)
![](/icons/28506yi.gif)
共有 6个
![](/icons/28506dou2.gif)
当我们
![](/icons/28506de.gif)
服务器仍忙于接受
![](/icons/28506yi.gif)
号连接(记住队列中还有 2?6 号)时
![](/icons/28506dou.gif)
如果有第 7个客户机提出连接申请
![](/icons/28506dou.gif)
那么
![](/icons/28506dou.gif)
该第 7个客户机将遭到拒绝
![](/icons/28506dou2.gif)
我们将在带有连接池服务器举例中介绍说明如何限定能同时连接
![](/icons/28506de.gif)
客户机数目
处理连接:
public void handleConnection(Socket connectionToHandle) {
![](/icons/28506new.gif)
Thread(
![](/icons/28506new.gif)
ConnectionHandler(connectionToHandle)).start
![](/icons/28506kh.gif)
;
}
我们对RemoteFileServer所做
![](/icons/28506de.gif)
大改动就体现在这个思路方法上
![](/icons/28506dou2.gif)
我们仍然在服务器接受
![](/icons/28506yi.gif)
个连接的后
![](/icons/28506diaoyong.gif)
handleConnection
![](/icons/28506kh.gif)
![](/icons/28506dou.gif)
但现在我们把该Socket传递给ConnectionHandler
![](/icons/28506de.gif)
![](/icons/28506yi.gif)
个例子
![](/icons/28506dou.gif)
它是 Runnable
![](/icons/28506de.gif)
![](/icons/28506dou2.gif)
我们用ConnectionHandler创建
![](/icons/28506yi.gif)
个新 Thread 并启动它
![](/icons/28506dou2.gif)
ConnectionHandler
![](/icons/28506de.gif)
run
![](/icons/28506kh.gif)
思路方法包Socket读/写和读File
![](/icons/28506de.gif)
代码
![](/icons/28506dou.gif)
这些代码原来在 RemoteFileServer
![](/icons/28506de.gif)
handleConnection
![](/icons/28506kh.gif)
中
创建 ConnectionHandler 类
import java.io.*;
import java.net.*;
public
![](/icons/28506class.gif)
ConnectionHandler implements Runnable {
protected Socket
![](/icons/28506socket.gif)
ToHandle;
public ConnectionHandler(Socket
![](/icons/28506socket.gif)
ToHandle) {
this.
![](/icons/28506socket.gif)
ToHandle=
![](/icons/28506socket.gif)
ToHandle;
}
public void run
![](/icons/28506kh.gif)
{
try {
Pr
![](/icons/28506int.gif)
Writer streamWriter =
![](/icons/28506new.gif)
Pr
![](/icons/28506int.gif)
Writer(
![](/icons/28506socket.gif)
ToHandle.getOutputStream
![](/icons/28506kh.gif)
);
BufferedReader streamReader =
![](/icons/28506new.gif)
BufferedReader(
![](/icons/28506new.gif)
InputStreamReader(
![](/icons/28506socket.gif)
ToHandle.getInputStream
![](/icons/28506kh.gif)
));
String fileToRead = streamReader.readLine
![](/icons/28506kh.gif)
;
BufferedReader fileReader =
![](/icons/28506new.gif)
BufferedReader(
![](/icons/28506new.gif)
FileReader(fileToRead));
String line =null;
while((line=fileReader.readLine
![](/icons/28506kh.gif)
)!=null) {
streamWriter.pr
![](/icons/28506int.gif)
ln(line);
}
fileReader.close
![](/icons/28506kh.gif)
;
streamWriter.close
![](/icons/28506kh.gif)
;
streamReader.close
![](/icons/28506kh.gif)
;
}
catch(Exception e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("Error handling a client: "+e);
e.pr
![](/icons/28506int.gif)
StackTrace
![](/icons/28506kh.gif)
;
}
}
}
这个助手类相当简单
![](/icons/28506dou2.gif)
跟我们到目前为止
![](/icons/28506de.gif)
其它类
![](/icons/28506yi.gif)
样
![](/icons/28506dou.gif)
我们导入java.net和java.io
![](/icons/28506dou2.gif)
该类只有
![](/icons/28506yi.gif)
个例子变量
![](/icons/28506socket.gif)
ToHandle
![](/icons/28506dou.gif)
它保存由该例子处理
![](/icons/28506de.gif)
Socket
类
![](/icons/28506de.gif)
构造器用
![](/icons/28506yi.gif)
个Socket例子作参数并将它赋给
![](/icons/28506socket.gif)
ToHandle
请注意该类实现了Runnable接口
![](/icons/28506dou2.gif)
实现这个接口
![](/icons/28506de.gif)
类都必须实现run
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou2.gif)
这里我们实现run
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou.gif)
它将攫取我们
![](/icons/28506de.gif)
连接
![](/icons/28506de.gif)
流
![](/icons/28506dou.gif)
用它来读写该连接
![](/icons/28506dou.gif)
并在任务完成的后关闭它
![](/icons/28506dou2.gif)
ConnectionHandler
![](/icons/28506de.gif)
run
![](/icons/28506kh.gif)
思路方法所做
![](/icons/28506de.gif)
事情就是RemoteFileServer上
![](/icons/28506de.gif)
handleConnection
![](/icons/28506kh.gif)
所做
![](/icons/28506de.gif)
事情
![](/icons/28506dou2.gif)
首先
![](/icons/28506dou.gif)
我们把InputStream和OutputStream分别包装(用Socket
![](/icons/28506de.gif)
getOutputStream
![](/icons/28506kh.gif)
和 getInputStream
![](/icons/28506kh.gif)
)进BufferedReader和Pr
![](/icons/28506int.gif)
Writer
![](/icons/28506dou2.gif)
然后我们用这些代码逐行地读目标文件:
Pr
![](/icons/28506int.gif)
Writer streamWriter =
![](/icons/28506new.gif)
Pr
![](/icons/28506int.gif)
Writer(
![](/icons/28506socket.gif)
ToHandle.getOutputStream
![](/icons/28506kh.gif)
);
BufferedReader streamReader =
![](/icons/28506new.gif)
BufferedReader(
![](/icons/28506new.gif)
InputStreamReader(
![](/icons/28506socket.gif)
ToHandle.getInputStream
![](/icons/28506kh.gif)
));
String fileToRead = streamReader.readLine
![](/icons/28506kh.gif)
;
BufferedReader fileReader =
![](/icons/28506new.gif)
BufferedReader(
![](/icons/28506new.gif)
FileReader(fileToRead));
String line =null;
while((line=fileReader.readLine
![](/icons/28506kh.gif)
)!=null) {
streamWriter.pr
![](/icons/28506int.gif)
ln(line);
}
请记住我们应该从客户机获取
![](/icons/28506yi.gif)
条有效
![](/icons/28506de.gif)
文件路径
![](/icons/28506dou.gif)
这样用该路径名构造
![](/icons/28506yi.gif)
个新File
![](/icons/28506dou.gif)
把它包装进FileReader以处理读文件
![](/icons/28506de.gif)
操作
![](/icons/28506dou.gif)
然后把它包装进 BufferedReader以让我们逐行地读该文件
![](/icons/28506dou2.gif)
我们while循环中
![](/icons/28506diaoyong.gif)
BufferedReader上
![](/icons/28506de.gif)
readLine
![](/icons/28506kh.gif)
直到不再有要读
![](/icons/28506de.gif)
行
![](/icons/28506dou2.gif)
请记注
![](/icons/28506dou.gif)
对readLine
![](/icons/28506kh.gif)
![](/icons/28506de.gif)
![](/icons/28506diaoyong.gif)
将造成阻塞
![](/icons/28506dou.gif)
直到有字节来到为止
![](/icons/28506dou2.gif)
我们获取
![](/icons/28506yi.gif)
些字节的后就把它们放到本地
![](/icons/28506de.gif)
line变量中
![](/icons/28506dou.gif)
然后写出到客户机上
![](/icons/28506dou2.gif)
完成读写操作的后
![](/icons/28506dou.gif)
我们关闭打开
![](/icons/28506de.gif)
流
整理总结
![](/icons/28506yi.gif)
下多线程服务器
让我们回顾
![](/icons/28506yi.gif)
下创建和使用“多线程版”
![](/icons/28506de.gif)
服务器
![](/icons/28506de.gif)
步骤:
1. 修改 acceptConnections
![](/icons/28506kh.gif)
以用缺省为 50(或任何您想要
![](/icons/28506de.gif)
大于 1
![](/icons/28506de.gif)
指定数字)例子化 ServerSocket
2. 修改 ServerSocket
![](/icons/28506de.gif)
handleConnection
![](/icons/28506kh.gif)
以用 ConnectionHandler
![](/icons/28506de.gif)
![](/icons/28506yi.gif)
个例子生成
![](/icons/28506yi.gif)
个新
![](/icons/28506de.gif)
Thread
3. 借用 RemoteFileServer
![](/icons/28506de.gif)
handleConnection
![](/icons/28506kh.gif)
思路方法
![](/icons/28506de.gif)
代码实现 ConnectionHandler 类
7 创建带有连接池
![](/icons/28506de.gif)
Socket服务器
我们现在已经拥有
![](/icons/28506de.gif)
MultithreadedServer 每当有客户机申请
![](/icons/28506yi.gif)
个连接时都在
![](/icons/28506yi.gif)
个新Thread中创建
![](/icons/28506yi.gif)
个新 ConnectionHandler
![](/icons/28506dou2.gif)
这意味着可能有
![](/icons/28506yi.gif)
捆Thread“躺”在我们周围
![](/icons/28506dou2.gif)
而且创建Thread
![](/icons/28506de.gif)
系统开销并不是微不足道
![](/icons/28506de.gif)
![](/icons/28506dou2.gif)
如果性能成为了问题(也请不要事到临头才意识到它)
![](/icons/28506dou.gif)
更高效地处理我们
![](/icons/28506de.gif)
服务器是件好事
![](/icons/28506dou2.gif)
那么
![](/icons/28506dou.gif)
我们如何更高效地管理服务器端呢?我们可以维护
![](/icons/28506yi.gif)
个进入
![](/icons/28506de.gif)
连接池
![](/icons/28506dou.gif)
![](/icons/28506yi.gif)
定数量
![](/icons/28506de.gif)
ConnectionHandler将为它提供服务
![](/icons/28506dou2.gif)
这种设计能带来以下好处:
• 它限定了允许同时连接
![](/icons/28506de.gif)
数目
• 我们只需启动ConnectionHandler Thread
![](/icons/28506yi.gif)
次
幸运
![](/icons/28506de.gif)
是
![](/icons/28506dou.gif)
跟在我们
![](/icons/28506de.gif)
多线程举例中
![](/icons/28506yi.gif)
样
![](/icons/28506dou.gif)
往代码中添加“池”不需要来
![](/icons/28506yi.gif)
个大改动
![](/icons/28506dou2.gif)
事实上
![](/icons/28506dou.gif)
应用
![](/icons/28506chengxu.gif)
![](/icons/28506de.gif)
客户机端根本就不受影响
![](/icons/28506dou2.gif)
在服务器端
![](/icons/28506dou.gif)
我们在服务器启动时创建
![](/icons/28506yi.gif)
定数量
![](/icons/28506de.gif)
ConnectionHandler
![](/icons/28506dou.gif)
我们把进入
![](/icons/28506de.gif)
连接放入“池”中并让ConnectionHandler打理剩下
![](/icons/28506de.gif)
事情
![](/icons/28506dou2.gif)
这种设计中有很多我们不打算讨论
![](/icons/28506de.gif)
可能存在
![](/icons/28506de.gif)
窍门技巧
![](/icons/28506dou2.gif)
例如
![](/icons/28506dou.gif)
我们可以通过限定允许在“池”中建立
![](/icons/28506de.gif)
连接
![](/icons/28506de.gif)
数目来拒绝客户机
请注意:我们将不会再次讨论acceptConnections
![](/icons/28506kh.gif)
![](/icons/28506dou2.gif)
这个思路方法跟前面举例中
![](/icons/28506de.gif)
完全
![](/icons/28506yi.gif)
样
![](/icons/28506dou2.gif)
它无限循环地
![](/icons/28506diaoyong.gif)
ServerSocket上
![](/icons/28506de.gif)
accept
![](/icons/28506kh.gif)
并把连接传递到handleConnection
![](/icons/28506kh.gif)
创建 PooledRemoteFileServer 类
import java.io.*;
import java.net.*;
import java.util.*;
public
![](/icons/28506class.gif)
PooledRemoteFileServer {
protected
![](/icons/28506int.gif)
maxConnections;
protected
![](/icons/28506int.gif)
listenPort;
protected ServerSocket serverSocket;
public PooledRemoteFileServer(
![](/icons/28506int.gif)
aListenPort,
![](/icons/28506int.gif)
maxConnections) {
listenPort= aListenPort;
this.maxConnections = maxConnections;
}
public void acceptConnections
![](/icons/28506kh.gif)
{
try {
ServerSocket server =
![](/icons/28506new.gif)
ServerSocket(listenPort, 5);
Socket incomingConnection = null;
while(true) {
incomingConnection = server.accept
![](/icons/28506kh.gif)
;
handleConnection(incomingConnection);
}
}
catch(BindException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("");
}
catch(IOException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln(""+listenPort);
}
}
protected void handleConnection(Socket connectionToHandle) {
PooledConnectionHandler.processRequest(connectionToHandle);
}
public void
![](/icons/28506set.gif)
UpHandlers
![](/icons/28506kh.gif)
{
for(
![](/icons/28506int.gif)
i=0; i<maxConnections; i
![](/icons/28506jiajia.gif)
) {
PooledConnectionHandler currentHandler =
![](/icons/28506new.gif)
PooledConnectionHandler
![](/icons/28506kh.gif)
;
![](/icons/28506new.gif)
Thread(currentHandler, "Handler " + i).start
![](/icons/28506kh.gif)
;
}
}
public
![](/icons/28506static.gif)
void
![](/icons/28506main.gif)
(String args
![](/icons/28506zhk2.gif)
) {
PooledRemoteFileServer server =
![](/icons/28506new.gif)
PooledRemoteFileServer(1001, 3);
server.
![](/icons/28506set.gif)
UpHandlers
![](/icons/28506kh.gif)
;
server.acceptConnections
![](/icons/28506kh.gif)
;
}
}
请注意
![](/icons/28506yi.gif)
下您现在应该熟悉了
![](/icons/28506de.gif)
import 语句
![](/icons/28506dou2.gif)
我们给类以下例子变量以保存:
• 我们
![](/icons/28506de.gif)
服务器能同时处理
![](/icons/28506de.gif)
活动客户机连接
![](/icons/28506de.gif)
最大数目
• 进入
![](/icons/28506de.gif)
连接
![](/icons/28506de.gif)
侦听端口(我们没有指定缺省值
![](/icons/28506dou.gif)
但如果您想这样做
![](/icons/28506dou.gif)
并不会受到限制)
• 将接受客户机连接请求
![](/icons/28506de.gif)
ServerSocket
类
![](/icons/28506de.gif)
构造器用
![](/icons/28506de.gif)
参数是侦听端口和连接
![](/icons/28506de.gif)
最大数目
我们
![](/icons/28506de.gif)
类有
![](/icons/28506yi.gif)
个
![](/icons/28506main.gif)
![](/icons/28506kh.gif)
思路方法和 3个其它思路方法
![](/icons/28506dou2.gif)
稍后我们将探究这些思路方法
![](/icons/28506de.gif)
细节
![](/icons/28506dou2.gif)
现在只须知道
![](/icons/28506set.gif)
UpHandlers
![](/icons/28506kh.gif)
创建数目为 maxConnections
![](/icons/28506de.gif)
大量PooledConnectionHandler
![](/icons/28506dou.gif)
而其它两个思路方法则和我们前面已经看到
![](/icons/28506de.gif)
相似:acceptConnections
![](/icons/28506kh.gif)
在ServerSocket上侦听传入
![](/icons/28506de.gif)
客户机连接
![](/icons/28506dou.gif)
而handleConnection则在客户机连接
![](/icons/28506yi.gif)
旦被建立后就实际处理它
实现
![](/icons/28506main.gif)
这里我们实现需作改动
![](/icons/28506de.gif)
![](/icons/28506main.gif)
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou.gif)
该思路方法将创建能够处理给定数目
![](/icons/28506de.gif)
客户机连接
![](/icons/28506de.gif)
PooledRemoteFileServer
![](/icons/28506dou.gif)
并告诉它接受连接:
public
![](/icons/28506static.gif)
void
![](/icons/28506main.gif)
(String args
![](/icons/28506zhk2.gif)
) {
PooledRemoteFileServer server =
![](/icons/28506new.gif)
PooledRemoteFileServer(1001, 3);
server.
![](/icons/28506set.gif)
UpHandlers
![](/icons/28506kh.gif)
;
server.acceptConnections
![](/icons/28506kh.gif)
;
}
我们
![](/icons/28506de.gif)
![](/icons/28506main.gif)
![](/icons/28506kh.gif)
思路方法很简单
![](/icons/28506dou2.gif)
我们例子化
![](/icons/28506yi.gif)
个新
![](/icons/28506de.gif)
PooledRemoteFileServer
![](/icons/28506dou.gif)
它将通过
![](/icons/28506diaoyong.gif)
![](/icons/28506set.gif)
UpHandlers
![](/icons/28506kh.gif)
来建立 3个 PooledConnectionHandler
![](/icons/28506dou2.gif)
![](/icons/28506yi.gif)
旦服务器就绪
![](/icons/28506dou.gif)
我们就告诉它acceptConnections
![](/icons/28506kh.gif)
建立连接处理
public void
![](/icons/28506set.gif)
UpHandlers
![](/icons/28506kh.gif)
{
for(
![](/icons/28506int.gif)
i=0; i<maxConnections; i
![](/icons/28506jiajia.gif)
) {
PooledConnectionHandler currentHandler =
![](/icons/28506new.gif)
PooledConnectionHandler
![](/icons/28506kh.gif)
;
![](/icons/28506new.gif)
Thread(currentHandler, "Handler " + i).start
![](/icons/28506kh.gif)
;
}
}
![](/icons/28506set.gif)
UpHandlers
![](/icons/28506kh.gif)
思路方法创建maxConnections(例如 3)个PooledConnectionHandler并在新Thread中激活它们
![](/icons/28506dou2.gif)
用实现了 Runnable
![](/icons/28506de.gif)
对象来创建Thread使我们可以在Thread
![](/icons/28506diaoyong.gif)
start
![](/icons/28506kh.gif)
并且可以期望在Runnable上
![](/icons/28506diaoyong.gif)
了run
![](/icons/28506kh.gif)
![](/icons/28506dou2.gif)
换句话说
![](/icons/28506dou.gif)
我们
![](/icons/28506de.gif)
PooledConnectionHandler将等着处理进入
![](/icons/28506de.gif)
连接
![](/icons/28506dou.gif)
每个都在它自己
![](/icons/28506de.gif)
Thread中进行
![](/icons/28506dou2.gif)
我们在举例中只创建 3个Thread
![](/icons/28506dou.gif)
而且
![](/icons/28506yi.gif)
旦服务器运行
![](/icons/28506dou.gif)
这就不能被改变
处理连接
这里我们实现需作改动
![](/icons/28506de.gif)
handleConnections
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou.gif)
它将委派PooledConnectionHandler处理连接:
protected void handleConnection(Socket connectionToHandle) {
PooledConnectionHandler.processRequest(connectionToHandle);
}
我们现在叫 PooledConnectionHandler 处理所有进入
![](/icons/28506de.gif)
连接(processRequest
![](/icons/28506kh.gif)
是
![](/icons/28506yi.gif)
个静态思路方法)
创建 PooledRemoteFileServer 类
import java.io.*;
import java.net.*;
import java.util.*;
public
![](/icons/28506class.gif)
PooledConnectionHandler implements Runnable {
protected Socket connection;
protected
![](/icons/28506static.gif)
List pool =
![](/icons/28506new.gif)
LinkedList
![](/icons/28506kh.gif)
;
public PooledConnectionHandler
![](/icons/28506kh.gif)
{}
public void handleConnection
![](/icons/28506kh.gif)
{
try {
Pr
![](/icons/28506int.gif)
Writer streamWriter =
![](/icons/28506new.gif)
Pr
![](/icons/28506int.gif)
Writer(connection.getOutputStream
![](/icons/28506kh.gif)
);
BufferedReader streamReader =
![](/icons/28506new.gif)
BufferedReader(
![](/icons/28506new.gif)
InputStreamReader(connection.getInputStream
![](/icons/28506kh.gif)
));
String fileToRead = streamReader.readLine
![](/icons/28506kh.gif)
;
BufferedReader fileReader =
![](/icons/28506new.gif)
BufferedReader(
![](/icons/28506new.gif)
FileReader(fileToRead));
String line = null;
while((line=fileReader.readLine
![](/icons/28506kh.gif)
)!=null)
streamWriter.pr
![](/icons/28506int.gif)
ln(line);
fileReader.close
![](/icons/28506kh.gif)
;
streamWriter.close
![](/icons/28506kh.gif)
;
streamReader.close
![](/icons/28506kh.gif)
;
}
catch(FileNotFoundException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("");
}
catch(IOException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln(""+e);
}
}
public
![](/icons/28506static.gif)
void processRequest(Socket requestToHandle) {
synchronized(pool) {
pool.add(pool.size
![](/icons/28506kh.gif)
, requestToHandle);
pool.not
![](/icons/28506if.gif)
yAll
![](/icons/28506kh.gif)
;
}
}
public void run
![](/icons/28506kh.gif)
{
while(true) {
synchronized(pool) {
while(pool.isEmpty
![](/icons/28506kh.gif)
) {
try {
pool.wait
![](/icons/28506kh.gif)
;
}
catch(InterruptedException e) {
e.pr
![](/icons/28506int.gif)
StackTrace
![](/icons/28506kh.gif)
;
}
}
connection= (Socket)pool.remove(0);
}
handleConnection
![](/icons/28506kh.gif)
;
}
}
}
这个助手类和 ConnectionHandler 非常相似
![](/icons/28506dou.gif)
但它带有处理连接池
![](/icons/28506de.gif)
手段
![](/icons/28506dou2.gif)
该类有两个例子变量:
• connection 是当前正在处理
![](/icons/28506de.gif)
Socket
• 名为 pool
![](/icons/28506de.gif)
静态 LinkedList 保存需被处理
![](/icons/28506de.gif)
连接
填充连接池
这里我们实现PooledConnectionHandler上
![](/icons/28506de.gif)
processRequest
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou.gif)
它将把传入请求添加到池中
![](/icons/28506dou.gif)
并告诉其它正在等待
![](/icons/28506de.gif)
对象该池已经有
![](/icons/28506yi.gif)
些内容:
public
![](/icons/28506static.gif)
void processRequest(Socket requestToHandle) {
synchronized(pool) {
pool.add(pool.size
![](/icons/28506kh.gif)
, requestToHandle);
pool.not
![](/icons/28506if.gif)
yAll
![](/icons/28506kh.gif)
;
}
}
synchronized 块是个稍微有些区别
![](/icons/28506de.gif)
东西
![](/icons/28506dou2.gif)
您可以同步任何对象上
![](/icons/28506de.gif)
![](/icons/28506yi.gif)
个块
![](/icons/28506dou.gif)
而不只是在本身
![](/icons/28506de.gif)
某个思路方法中含有该块
![](/icons/28506de.gif)
对象
![](/icons/28506dou2.gif)
在我们
![](/icons/28506de.gif)
举例中
![](/icons/28506dou.gif)
processRequest
![](/icons/28506kh.gif)
思路方法包含有
![](/icons/28506yi.gif)
个 pool(请记住它是
![](/icons/28506yi.gif)
个 LinkedList
![](/icons/28506dou.gif)
保存等待处理
![](/icons/28506de.gif)
连接池)
![](/icons/28506de.gif)
synchronized块
![](/icons/28506dou2.gif)
我们这样做
![](/icons/28506de.gif)
原因是确保没有别人能跟我们同时修改连接池
既然我们已经保证了我们是唯
![](/icons/28506yi.gif)
“涉水”池中
![](/icons/28506de.gif)
人
![](/icons/28506dou.gif)
我们就可以把传入
![](/icons/28506de.gif)
Socket添加到LinkedList
![](/icons/28506de.gif)
尾端
![](/icons/28506dou2.gif)
![](/icons/28506yi.gif)
旦我们添加了新
![](/icons/28506de.gif)
连接
![](/icons/28506dou.gif)
我们就用以下代码通知其它正在等待该池
![](/icons/28506de.gif)
Thread
![](/icons/28506dou.gif)
池现在已经可用:
pool.not
![](/icons/28506if.gif)
yAll
![](/icons/28506kh.gif)
;
Object
![](/icons/28506de.gif)
所有子类都继承这个not
![](/icons/28506if.gif)
yAll
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou2.gif)
这个思路方法
![](/icons/28506dou.gif)
连同我们下
![](/icons/28506yi.gif)
屏将要讨论
![](/icons/28506de.gif)
wait
![](/icons/28506kh.gif)
思路方法
![](/icons/28506yi.gif)
起
![](/icons/28506dou.gif)
就使
![](/icons/28506yi.gif)
个Thread能够让另
![](/icons/28506yi.gif)
个Thread知道
![](/icons/28506yi.gif)
些条件已经具备
![](/icons/28506dou2.gif)
这意味着该第 2个Thread
![](/icons/28506yi.gif)
定正在等待那些条件
![](/icons/28506de.gif)
满足
从池中获取连接
这里我们实现PooledConnectionHandler上需作改动
![](/icons/28506de.gif)
run
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou.gif)
它将在连接池上等待
![](/icons/28506dou.gif)
并且池中
![](/icons/28506yi.gif)
有连接就处理它:
public void run
![](/icons/28506kh.gif)
{
while(true) {
synchronized(pool) {
while(pool.isEmpty
![](/icons/28506kh.gif)
) {
try {
pool.wait
![](/icons/28506kh.gif)
;
}
catch(InterruptedException e) {
e.pr
![](/icons/28506int.gif)
StackTrace
![](/icons/28506kh.gif)
;
}
}
connection= (Socket)pool.remove(0);
}
handleConnection
![](/icons/28506kh.gif)
;
}
}
回想
![](/icons/28506yi.gif)
下在前面讲过
![](/icons/28506de.gif)
:
![](/icons/28506yi.gif)
个Thread正在等待有人通知它连接池方面
![](/icons/28506de.gif)
条件已经满足了
![](/icons/28506dou2.gif)
在我们
![](/icons/28506de.gif)
举例中
![](/icons/28506dou.gif)
请记住我们有 3个 PooledConnectionHandler在等待使用池中
![](/icons/28506de.gif)
连接
![](/icons/28506dou2.gif)
每个PooledConnectionHandler都在它自已
![](/icons/28506de.gif)
Thread中运行
![](/icons/28506dou.gif)
并通过
![](/icons/28506diaoyong.gif)
pool.wait
![](/icons/28506kh.gif)
产生阻塞
![](/icons/28506dou2.gif)
当我们
![](/icons/28506de.gif)
processRequest
![](/icons/28506kh.gif)
在连接池上
![](/icons/28506diaoyong.gif)
not
![](/icons/28506if.gif)
yAll
![](/icons/28506kh.gif)
时
![](/icons/28506dou.gif)
所有正在等待
![](/icons/28506de.gif)
PooledConnectionHandler都将得到“池已经可用”
![](/icons/28506de.gif)
通知
![](/icons/28506dou2.gif)
然后各自继续前行
![](/icons/28506diaoyong.gif)
pool.wait
![](/icons/28506kh.gif)
![](/icons/28506dou.gif)
并重新检查 while(pool.isEmpty
![](/icons/28506kh.gif)
)循环条件
![](/icons/28506dou2.gif)
除了
![](/icons/28506yi.gif)
个处理
![](/icons/28506chengxu.gif)
![](/icons/28506dou.gif)
其它池对所有处理
![](/icons/28506chengxu.gif)
都将是空
![](/icons/28506de.gif)
![](/icons/28506dou.gif)
因此
![](/icons/28506dou.gif)
在
![](/icons/28506diaoyong.gif)
pool.wait
![](/icons/28506kh.gif)
时
![](/icons/28506dou.gif)
除了
![](/icons/28506yi.gif)
个处理
![](/icons/28506chengxu.gif)
![](/icons/28506dou.gif)
其它所有处理
![](/icons/28506chengxu.gif)
都将再次产生阻塞
![](/icons/28506dou2.gif)
恰巧碰上非空池
![](/icons/28506de.gif)
处理
![](/icons/28506chengxu.gif)
将跳出while(pool.isEmpty
![](/icons/28506kh.gif)
)循环并攫取池中
![](/icons/28506de.gif)
第
![](/icons/28506yi.gif)
个连接:
connection= (Socket)pool.remove(0);
处理
![](/icons/28506chengxu.gif)
![](/icons/28506yi.gif)
旦有
![](/icons/28506yi.gif)
个连接可以使用
![](/icons/28506dou.gif)
就
![](/icons/28506diaoyong.gif)
handleConnection
![](/icons/28506kh.gif)
处理它
在我们
![](/icons/28506de.gif)
举例中
![](/icons/28506dou.gif)
池中可能永远不会有多个连接
![](/icons/28506dou.gif)
只是
![](/icons/28506yinwei.gif)
事情很快就被处理掉了
![](/icons/28506dou2.gif)
如果池中有
![](/icons/28506yi.gif)
个以上连接
![](/icons/28506dou.gif)
那么其它处理
![](/icons/28506chengxu.gif)
将不必等待新
![](/icons/28506de.gif)
连接被添加到池
![](/icons/28506dou2.gif)
当它们检查pool.isEmpty
![](/icons/28506kh.gif)
条件时
![](/icons/28506dou.gif)
将发现其值为假
![](/icons/28506dou.gif)
然后就从池中攫取
![](/icons/28506yi.gif)
个连接并处理它
还有另
![](/icons/28506yi.gif)
件事需注意
![](/icons/28506dou2.gif)
当run
![](/icons/28506kh.gif)
拥有池
![](/icons/28506de.gif)
互斥锁时
![](/icons/28506dou.gif)
processRequest
![](/icons/28506kh.gif)
如何能够把连接放到池中呢?答案是对池上
![](/icons/28506de.gif)
wait
![](/icons/28506kh.gif)
![](/icons/28506de.gif)
![](/icons/28506diaoyong.gif)
释放锁
![](/icons/28506dou.gif)
而wait
![](/icons/28506kh.gif)
接着就在自己返回的前再次攫取该锁
![](/icons/28506dou2.gif)
这就使得池对象
![](/icons/28506de.gif)
其它同步代码可以获取该锁
处理连接:再
![](/icons/28506yi.gif)
次
这里我们实现需做改动
![](/icons/28506de.gif)
handleConnection
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou.gif)
该思路方法将攫取连接
![](/icons/28506de.gif)
流
![](/icons/28506dou.gif)
使用它们
![](/icons/28506dou.gif)
并在任务完成的后清除它们:
public void handleConnection
![](/icons/28506kh.gif)
{
try {
Pr
![](/icons/28506int.gif)
Writer streamWriter =
![](/icons/28506new.gif)
Pr
![](/icons/28506int.gif)
Writer(connection.getOutputStream
![](/icons/28506kh.gif)
);
BufferedReader streamReader =
![](/icons/28506new.gif)
BufferedReader(
![](/icons/28506new.gif)
InputStreamReader(connection.getInputStream
![](/icons/28506kh.gif)
));
String fileToRead = streamReader.readLine
![](/icons/28506kh.gif)
;
BufferedReader fileReader =
![](/icons/28506new.gif)
BufferedReader(
![](/icons/28506new.gif)
FileReader(fileToRead));
String line = null;
while((line=fileReader.readLine
![](/icons/28506kh.gif)
)!=null)
streamWriter.pr
![](/icons/28506int.gif)
ln(line);
fileReader.close
![](/icons/28506kh.gif)
;
streamWriter.close
![](/icons/28506kh.gif)
;
streamReader.close
![](/icons/28506kh.gif)
;
}
catch(FileNotFoundException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln("");
}
catch(IOException e) {
![](/icons/28506System.gif)
.out.pr
![](/icons/28506int.gif)
ln(""+e);
}
}
跟在多线程服务器中区别
![](/icons/28506dou.gif)
我们
![](/icons/28506de.gif)
PooledConnectionHandler有
![](/icons/28506yi.gif)
个handleConnection
![](/icons/28506kh.gif)
思路方法
![](/icons/28506dou2.gif)
这个思路方法
![](/icons/28506de.gif)
代码跟非池式
![](/icons/28506de.gif)
ConnectionHandler上
![](/icons/28506de.gif)
run
![](/icons/28506kh.gif)
思路方法
![](/icons/28506de.gif)
代码完全
![](/icons/28506yi.gif)
样
![](/icons/28506dou2.gif)
首先
![](/icons/28506dou.gif)
我们把OutputStream和InputStream分别包装进(用Socket上
![](/icons/28506de.gif)
getOutputStream
![](/icons/28506kh.gif)
和getInputStream
![](/icons/28506kh.gif)
)BufferedReader和Pr
![](/icons/28506int.gif)
Writer
![](/icons/28506dou2.gif)
然后我们逐行读目标文件
![](/icons/28506dou.gif)
就象我们在多线程举例中做
![](/icons/28506de.gif)
那样
![](/icons/28506dou2.gif)
再
![](/icons/28506yi.gif)
次
![](/icons/28506dou.gif)
我们获取
![](/icons/28506yi.gif)
些字节的后就把它们放到本地
![](/icons/28506de.gif)
line变量中
![](/icons/28506dou.gif)
然后写出到客户机
![](/icons/28506dou2.gif)
完成读写操作的后
![](/icons/28506dou.gif)
我们关闭FileReader和打开
![](/icons/28506de.gif)
流
![](/icons/28506dou2.gif)
整理总结
![](/icons/28506yi.gif)
下带有连接池
![](/icons/28506de.gif)
服务器
让我们回顾
![](/icons/28506yi.gif)
下创建和使用“池版”服务器
![](/icons/28506de.gif)
步骤:
1. 创建
![](/icons/28506yi.gif)
个新种类
![](/icons/28506de.gif)
连接处理
![](/icons/28506chengxu.gif)
(我们称的为 PooledConnectionHandler)来处理池中
![](/icons/28506de.gif)
连接
2. 修改服务器以创建和使用
![](/icons/28506yi.gif)
组 PooledConnectionHandler
Java 语言简化了套接字在应用
![](/icons/28506chengxu.gif)
中
![](/icons/28506de.gif)
使用
![](/icons/28506dou2.gif)
它
![](/icons/28506de.gif)
基础实际上是 java.net 包中
![](/icons/28506de.gif)
Socket 和 ServerSocket 类
![](/icons/28506dou2.gif)
![](/icons/28506yi.gif)
旦您理解了表象背后发生
![](/icons/28506de.gif)
情况
![](/icons/28506dou.gif)
就能容易地使用这些类
![](/icons/28506dou2.gif)
在现实生活中使用套接字只是这样
![](/icons/28506yi.gif)
件事
![](/icons/28506dou.gif)
即通过贯彻优秀
![](/icons/28506de.gif)
OO 设计原则来保护应用
![](/icons/28506chengxu.gif)
中各层间
![](/icons/28506de.gif)
封装
![](/icons/28506dou2.gif)
我们为您展示了
![](/icons/28506yi.gif)
些有帮助
![](/icons/28506de.gif)
类
![](/icons/28506dou2.gif)
这些类
![](/icons/28506de.gif)
结构对我们
![](/icons/28506de.gif)
应用
![](/icons/28506chengxu.gif)
隐藏了 Socket 交互作用
![](/icons/28506de.gif)
低级细节 ? 使应用
![](/icons/28506chengxu.gif)
能只使用可插入
![](/icons/28506de.gif)
ClientSocketFacade 和 ServerSocketFacade
![](/icons/28506dou2.gif)
在有些地方(在 Facade 内)
![](/icons/28506dou.gif)
您仍然必须管理稍显杂乱
![](/icons/28506de.gif)
字节细节
![](/icons/28506dou.gif)
但您只须做
![](/icons/28506yi.gif)
次就可以了
![](/icons/28506dou2.gif)
更好
![](/icons/28506de.gif)
是
![](/icons/28506dou.gif)
您可以在将来
![](/icons/28506de.gif)
项目中重用这些低级别
![](/icons/28506de.gif)
助手类