按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
因为客户和服务器在采取下一步操作之前都要等待一行文本内容的到达。若刷新没有发生,那么信息不会进
入网络,除非缓冲区满(溢出),这会为本例带来许多问题。
编写网络应用程序时,需要特别注意自动刷新机制的使用。每次刷新缓冲区时,必须创建和发出一个数据包
(数据封)。就目前的情况来说,这正是我们所希望的,因为假如包内包含了还没有发出的文本行,服务器
和客户机之间的相互“握手”就会停止。换句话说,一行的末尾就是一条消息的末尾。但在其他许多情况
下,消息并不是用行分隔的,所以不如不用自动刷新机制,而用内建的缓冲区判决机制来决定何时发送一个
数据包。这样一来,我们可以发出较大的数据包,而且处理进程也能加快。
注意和我们打开的几乎所有数据流一样,它们都要进行缓冲处理。本章末尾有一个练习,清楚展现了假如我
们不对数据流进行缓冲,那么会得到什么样的后果(速度会变慢)。
无限while 循环从BufferedReader in 内读取文本行,并将信息写入System。out,然后写入
PrintWriter。out。注意这可以是任何数据流,它们只是在表面上同网络连接。
客户程序发出包含了〃END〃的行后,程序会中止循环,并关闭 Socket。
下面是客户程序的源码:
//: JabberClient。java
// Very simple client that just sends
// lines to the server and reads lines
// that the server sends。
import java。*;
import java。io。*;
public class JabberClient {
public static void main(String'' args)
throws IOException {
// Passing null to getByName() produces the
// special 〃Local Loopback〃 IP address; for
541
…………………………………………………………Page 543……………………………………………………………
// testing on one machine w/o a network:
InetAddress addr =
InetAddress。getByName(null);
// Alternatively; you can use
// the address or name:
// InetAddress addr =
// InetAddress。getByName(〃127。0。0。1〃);
// InetAddress addr =
// InetAddress。getByName(〃localhost〃);
System。out。println(〃addr = 〃 + addr);
Socket socket =
new Socket(addr; JabberServer。PORT);
// Guard everything in a try…finally to make
// sure that the socket is closed:
try {
System。out。println(〃socket = 〃 + socket);
BufferedReader in =
new BufferedReader(
new InputStreamReader(
socket。getInputStream()));
// Output is automatically flushed
// by PrintWriter:
PrintWriter out =
new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
socket。getOutputStream()));true);
for(int i = 0; i 《 10; i ++) {
out。println(〃howdy 〃 + i);
String str = in。readLine();
System。out。println(str);
}
out。println(〃END〃);
} finally {
System。out。println(〃closing。。。〃);
socket。close();
}
}
} ///:~
在main()中,大家可看到获得本地主机 IP地址的 InetAddress 的三种途径:使用 null,使用 localhost,
或者直接使用保留地址 127。0。0。1。当然,如果想通过网络同一台远程主机连接,也可以换用那台机器的IP
地址。打印出 InetAddress addr 后(通过对 toString()方法的自动调用),结果如下:
localhost/127。0。0。1
通过向 getByName()传递一个null,它会默认寻找 localhost,并生成特殊的保留地址127。0。0。1。注意在名
为 socket 的套接字创建时,同时使用了 InetAddress 以及端口号。打印这样的某个Socket 对象时,为了真
正理解它的含义,请记住一次独一无二的因特网连接是用下述四种数据标识的:clientHost (客户主机)、
clientPortNumber (客户端口号)、serverHost (服务主机)以及serverPortNumber (服务端口号)。服务
程序启动后,会在本地主机(127。0。0。1)上建立为它分配的端口(8080 )。一旦客户程序发出请求,机器上
下一个可用的端口就会分配给它(这种情况下是 1077),这一行动也在与服务程序相同的机器
(127。0。0。1)上进行。现在,为了使数据能在客户及服务程序之间来回传送,每一端都需要知道把数据发到
哪里。所以在同一个“已知”服务程序连接的时候,客户会发出一个“返回地址”,使服务器程序知道将自
542
…………………………………………………………Page 544……………………………………………………………
己的数据发到哪儿。我们在服务器端的示范输出中可以体会到这一情况:
Socket'addr=127。0。0。1;port=1077;localport=8080'
这意味着服务器刚才已接受了来自 127。0。0。1 这台机器的端口 1077 的连接,同时监听自己的本地端口
(8080 )。而在客户端:
Socket'addr=localhost/127。0。0。1;PORT=8080;localport=1077'
这意味着客户已用自己的本地端口 1077 与 127。0。0。1 机器上的端口 8080 建立了 连接。
大家会注意到每次重新启动客户程序的时候,本地端口的编号都会增加。这个编号从 1025 (刚好在系统保留
的1…1024 之外)开始,并会一直增加下去,除非我们重启机器。若重新启动机器,端口号仍然会从 1025 开
始增值(在 Unix 机器中,一旦超过保留的套按字范围,数字就会再次从最小的可用数字开始)。
创建好 Socket 对象后,将其转换成BufferedReader 和 PrintWriter 的过程便与在服务器中相同(同样地,
两种情况下都要从一个 Socket 开始)。在这里,客户通过发出字串〃howdy〃,并在后面跟随一个数字,从而
初始化通信。注意缓冲区必须再次刷新(这是自动发生的,通过传递给PrintWriter 构建器的第二个参
数)。若缓冲区没有刷新,那么整个会话(通信)都会被挂起,因为用于初始化的“howdy”永远不会发送出
去(缓冲区不够满,不足以造成发送动作的自动进行)。从服务器返回的每一行都会写入System。out,以验
证一切都在正常运转。为中止会话,需要发出一个〃END〃。若客户程序简单地挂起,那么服务器会“掷”出一
个违例。
大家在这里可以看到我们采用了同样的措施来确保由Socket 代表的网络资源得到正确的清除,这是用一个
try…finally块实现的。
套接字建立了一个“专用”连接,它会一直持续到明确断开连接为止(专用连接也可能间接性地断开,前提
是某一端或者中间的某条链路出现故障而崩溃)。这意味着参与连接的双方都被锁定在通信中,而且无论是
否有数据传递,连接都会连续处于开放状态。从表面看,这似乎是一种合理的连网方式。然而,它也为网络
带来了额外的开销。本章后面会介绍进行连网的另一种方式。采用那种方式,连接的建立只是暂时的。
15。3 服务多个客户
JabberServer 可以正常工作,但每次只能为一个客户程序提供服务。在典型的服务器中,我们希望同时能处
理多个客户的请求。解决这个