友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
一世书城 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

Java编程思想第4版[中文版](PDF格式)-第174章

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!





…………………………………………………………Page 542……………………………………………………………

accept()时,方法会暂时陷入停顿状态(堵塞),直到某个客户尝试同它建立连接。换言之,尽管它在那里 

等候连接,但其他进程仍能正常运行(参考第 14章)。建好一个连接以后,accept()就会返回一个 Socket 

对象,它是那个连接的代表。  

清除套接字的责任在这里得到了很艺术的处理。假如ServerSocket 构建器失败,则程序简单地退出(注意必 

须保证 ServerSocket 的构建器在失败之后不会留下任何打开的网络套接字)。针对这种情况,main()会 

 “掷”出一个IOException 违例,所以不必使用一个try 块。若 ServerSocket 构建器成功执行,则其他所有 

方法调用都必须到一个 try…finally代码块里寻求保护,以确保无论块以什么方式留下,ServerSocket 都能 

正确地关闭。  

同样的道理也适用于由 accept()返回的 Socket。若accept() 失败,那么我们必须保证Socket 不再存在或者 

含有任何资源,以便不必清除它们。但假若执行成功,则后续的语句必须进入一个try…finally 块内,以保 

障在它们失败的情况下,Socket 仍能得到正确的清除。由于套接字使用了重要的非内存资源,所以在这里必 

须特别谨慎,必须自己动手将它们清除(Java 中没有提供“破坏器”来帮助我们做这件事情)。  

无论ServerSocket 还是由 accept()产生的 Socket 都打印到 System。out 里。这意味着它们的 toString方法 

会得到自动调用。这样便产生了:  

  

ServerSocket'addr=0。0。0。0;PORT=0;localport=8080'  

Socket'addr=127。0。0。1;PORT=1077;localport=8080'  

  

大家不久就会看到它们如何与客户程序做的事情配合。  

程序的下一部分看来似乎仅仅是打开文件,以便读取和写入,只是 InputStream和 OutputStream 是从 

Socket 对象创建的。利用两个“转换器”类 InputStreamReader 和OutputStreamWriter ,InputStream和 

OutputStream 对象已经分别转换成为 Java 1。1 的Reader 和 Writer 对象。也可以直接使用 Java1。0 的 

InputStream和 OutputStream 类,但对输出来说,使用Writer 方式具有明显的优势。这一优势是通过 

PrintWriter 表现出来的,它有一个过载的构建器,能获取第二个参数——一个布尔值标志,指向是否在每 

一次println()结束的时候自动刷新输出(但不适用于print()语句)。每次写入了输出内容后(写进 

out),它的缓冲区必须刷新,使信息能正式通过网络传递出去。对目前这个例子来说,刷新显得尤为重要, 

因为客户和服务器在采取下一步操作之前都要等待一行文本内容的到达。若刷新没有发生,那么信息不会进 

入网络,除非缓冲区满(溢出),这会为本例带来许多问题。  

编写网络应用程序时,需要特别注意自动刷新机制的使用。每次刷新缓冲区时,必须创建和发出一个数据包 

 (数据封)。就目前的情况来说,这正是我们所希望的,因为假如包内包含了还没有发出的文本行,服务器 

和客户机之间的相互“握手”就会停止。换句话说,一行的末尾就是一条消息的末尾。但在其他许多情况 

下,消息并不是用行分隔的,所以不如不用自动刷新机制,而用内建的缓冲区判决机制来决定何时发送一个 

数据包。这样一来,我们可以发出较大的数据包,而且处理进程也能加快。  

注意和我们打开的几乎所有数据流一样,它们都要进行缓冲处理。本章末尾有一个练习,清楚展现了假如我 

们不对数据流进行缓冲,那么会得到什么样的后果(速度会变慢)。  

无限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 可以正常工作,但每次只能为一个客户程序提供服务。在典型的服务器中,我们希望
返回目录 上一页 下一页 回到顶部 0 0
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!