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

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

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




冲区内。可按第 10章介绍的方法对类进行格式化,就象对待其他任何流对象那样。  

对于Java 库的命名机制,ServerSocket (服务器套接字)的使用无疑是容易产生混淆的又一个例证。大家可 

能认为ServerSocket 最好叫作“ServerConnector”(服务器连接器),或者其他什么名字,只是不要在其 

中安插一个“Socket”。也可能以为ServerSocket 和 Socket 都应从一些通用的基础类继承。事实上,这两 

种类确实包含了几个通用的方法,但还不够资格把它们赋给一个通用的基础类。相反,ServerSocket 的主要 

任务是在那里耐心地等候其他机器同它连接,再返回一个实际的Socket。这正是“ServerSocket”这个命名 

不恰当的地方,因为它的目标不是真的成为一个Socket,而是在其他人同它连接的时候产生一个Socket 对 

象。  

然而,ServerSocket 确实会在主机上创建一个物理性的“服务器”或者侦听用的套接字。这个套接字会侦听 

进入的连接,然后利用 accept()方法返回一个“已建立”套接字(本地和远程端点均已定义)。容易混淆的 

地方是这两个套接字(侦听和已建立)都与相同的服务器套接字关联在一起。侦听套接字只能接收新的连接 

请求,不能接收实际的数据包。所以尽管 ServerSocket 对于编程并无太大的意义,但它确实是“物理性” 

的。  

创建一个ServerSocket 时,只需为其赋予一个端口编号。不必把一个 IP 地址分配它,因为它已经在自己代 

表的那台机器上了。但在创建一个 Socket 时,却必须同时赋予IP 地址以及要连接的端口编号(另一方面, 

从ServerSocket。accept()返回的 Socket 已经包含了所有这些信息)。  



15。2。1 一个简单的服务器和客户机程序  



这个例子将以最简单的方式运用套接字对服务器和客户机进行操作。服务器的全部工作就是等候建立一个连 

接,然后用那个连接产生的Socket 创建一个 InputStream 以及一个OutputStream。在这之后,它从 

InputStream读入的所有东西都会反馈给OutputStream,直到接收到行中止(END)为止,最后关闭连接。  

客户机连接与服务器的连接,然后创建一个OutputStream。文本行通过OutputStream 发送。客户机也会创 

建一个 InputStream,用它收听服务器说些什么(本例只不过是反馈回来的同样的字句)。  

服务器与客户机(程序)都使用同样的端口号,而且客户机利用本地主机地址连接位于同一台机器中的服务 



                                                               539 


…………………………………………………………Page 541……………………………………………………………

器(程序),所以不必在一个物理性的网络里完成测试(在某些配置环境中,可能需要同真正的网络建立连 

接,否则程序不能工作——尽管实际并不通过那个网络通信)。  

下面是服务器程序:  

  

//: JabberServer。java  

// Very simple server that just  

// echoes whatever the client sends。  

import java。io。*;  

import java。*;  

  

public class JabberServer {    

  // Choose a port outside of the range 1…1024:  

  public static final int PORT = 8080;  

  public static void main(String'' args)   

      throws IOException {  

    ServerSocket s = new ServerSocket(PORT);  

    System。out。println(〃Started: 〃 + s);  

    try {  

      // Blocks until a connection occurs:  

      Socket socket = s。accept();  

      try {  

        System。out。println(  

          〃Connection accepted: 〃+ 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);  

        while (true) {    

          String str = in。readLine();  

          if (str。equals(〃END〃)) break;  

          System。out。println(〃Echoing: 〃 + str);  

          out。println(str);  

        }  

      // Always close the two sockets。。。  

      } finally {  

        System。out。println(〃closing。。。〃);  

        socket。close();  

      }  

    } finally {  

      s。close();  

    }  

  }   

} ///:~  

  

可以看到,ServerSocket 需要的只是一个端口编号,不需要 IP地址(因为它就在这台机器上运行)。调用 



                                                                                          540 


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

因为客户和服务器在采取下一步操作之前都要等待一行文本内容的到达。若刷新没有发生,那么信
返回目录 上一页 下一页 回到顶部 0 0
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!