八宝书库 > 文学其他电子书 > Java编程思想第4版[中文版](PDF格式) >

第140部分

Java编程思想第4版[中文版](PDF格式)-第140部分

小说: Java编程思想第4版[中文版](PDF格式) 字数: 每页4000字

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






                                                                                          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 可以正常工作,但每次只能为一个客户程序提供服务。在典型的服务器中,我们希望同时能处 

理多个客户的请求。解决这个问题的关键就是多线程处理机制。而对于那些本身不支持多线程的语言,达到 

这个要求无疑是异常困难的。通过第 14 章的学习,大家已经知道Java 已对多线程的处理进行了尽可能的简 

化。由于Java 的线程处理方式非常直接,所以让服务器控制多名客户并不是件难事。  

最基本的方法是在服务器(程序)里创建单个 ServerSocket,并调用accept()来等候一个新连接。一旦 

accept()返回,我们就取得结果获得的 Socket,并用它新建一个线程,令其只为那个特定的客户服务。然后 

再调用 accept() ,等候下一次新的连接请求。  

对于下面这段服务器代码,大家可发现它与JabberServer。java 例子非常相似,只是为一个特定的客户提供 

服务的所有操作都已移入一个独立的线程类中:  

  

//: MultiJabberServer。java  

// A server that uses multithreading to handle   

// any number of clients。  

import java。io。*;  

import java。*;  

  

class ServeOneJabber extends Thread {  

  private Socket socket;  

  private BufferedReader in;  

  private PrintWriter out;  

  public ServeOneJabber(Socket s)   

      throws IOException {  

    socket = s;  

    in =   

      new BufferedReader(  

        new InputStreamReader(  

          socket。getInputStream()));  

    // Enable auto…flush:  



                                                                               543 


…………………………………………………………Page 545……………………………………………………………

    out =   

      new PrintWriter(  

        new BufferedWriter(  

          new OutputStreamWriter(  

            socket。getOutputStream())); true);  

    // If any of the above calls throw an   

    // exception; the caller is responsible for  

    // closing the socket。 Otherwise the thread  

    // will close it。  

    start(); // Calls run()  

  }  

  public void run() {  

    try {  

      while (true) {    

        String str = in。readLine();  

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

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

        out。println(str);  

      }  

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

    } catch (IOException e) {  

    } finally {  

      try {  

        socket。close();  

      } catch(IOException e) {}  

    }  

  }  

}  

  

public class MultiJabberServer {    

  static final int PORT = 8080;  

  public static void main(String'' args)  

      throws IOException {  

    ServerSocket s = new ServerSocket(PORT);  

    System。out。println(〃Server Started〃);  

    try {  

      while(true) {  

        // Blocks until a connection occurs:  

        Socket socket = s。accept();  

        try {  

          new ServeOneJabber(socket);  

        } catch(IOException e) {  

          // If it fails; close the socket;  

          // otherwise the thread will close it:  

          socket。close();  

        }  

      }  

    } finally {  

      s。close();  

    }  

  }   



                                                                                             544 


…………………………………………………………Page 546……………………………………………………………

} ///:~  

  

每次有新客户请求建立一个连接时,ServeOneJabber 线程都会取得由accept()在 main() 中生成的Socket 对 

象。然后和往常一样,它创建一个 BufferedReader,并用Socket 自动刷新PrintWriter 对象。最后,它调 

用Thread 的特殊方法 start(),令其进行线程的初始化,然后调用run()。这里采取的操作与前例是一样 

的:从套扫字读入某些东西,然后把它原样反馈回去,直到遇到一个特殊的〃END〃结束标志为止。  

同样地,套接字的清除必须进行谨慎的设计。就目前这种情况来说,套接字是在ServeOneJabber 外部创建 

的,所以清除工作可以“共享”。若ServeOneJabber 构建器失败,那么只需向调用者“掷”出一个违例即 

可,然后由调用者负责线程的清除。但假如构建器成功,那么必须由 ServeOneJabber 对象负责线程的清除, 

这是在它的 run()里进行的。  

请注意MultiJabberServer 有多么简单。和以前一样,我们创建一个 ServerSocket,并调用accept()允许一 

个新连接的建立。但这一次,accept() 的返回值(一个套接字)将传递给用于ServeOneJabber 的构建器,由 

它创建一个新线程,并对那个连接进行控制。连接中断后,线程便可简单地消失。  

如果ServerSocket 创建失败,则再一次通过 main()掷出违例。如果成功,则位于外层的 try…finally代码 

块可以担保正确的清除。位于内层的try…catch 块只负责防范 ServeOneJabber 构建器的失败;若构建器成 

功,则 ServeOneJabber 线程会将对应的套接字关掉。  

为了证实服务器代码确实能为多名客户提供服务,下面这个程序将创建许多客户(使用线程),并同相同的 

服务器建立连接。每个线程的“存在时间”都是有限的。一旦到期,就留出空间以便创建一个新线程。允许 

创建的线程的最大数量是由final int maxthreads 决定的。大家会注意到这个值非常关键,因为假如把它设 

得很大,线程便有可能耗尽资源,并产生不可预知的程序错误。  

  

//: MultiJabberClient。java  

// Client that tests the MultiJabberServer  

// by starting up multiple clients。  

import java。*;  

import java。io。*;  

  

class JabberClientThread extends Thread {  

  private Socket socket;  

  private BufferedReader in;  

  private PrintWriter out;  

  private static int counter = 0;  

  private int id = counter++;  

  private static int threadcount = 0;  

  public static int threadCount() {   

    return threadcount;   

  }  

  public JabberClientThread(InetAddress addr) {  

    System。out。println(〃Making client 〃 + id);  

    threadcount++;  

    try {  

      socket =   

        new Socket(addr; MultiJabberServer。PORT);  

    } catch(IOException e) {  

      // If the creation of the socket fails;   

      // nothing needs to be cleaned up。  

    }  

    try {      

      in =   

        new BufferedReader(  

          new InputStreamReader(  

            socket。getInputStream()));  



                                                                                          545 


…………………………………………………………Page 547……………………………………………………………

      // Enable auto…flush:  

      out =   

        new PrintWriter(  

          new BufferedWriter(  

            new OutputStreamWriter(  

              socket。getOutputStream())); true);  

      start();  

    } catch(IOException e) {  

      // The socket should be closed on any   

      // failures other than the socket   

      // constructor:  

      try {  

        socket。close();  

      } catch(IOException e2) {}  

    }  

    // Otherwise the socket will be closed by  

    // the run() method of the thread。  

  }  

  public void run() {  

    try {  

      for(int i = 0; i 《 25; i++) {  

        out。println(〃Client 〃 + id + 〃: 〃 + i);  

        String str = in。readLine();  

        System。out。println(str);  

      }  

      out。println(〃END〃);  

    } catch(IOException e) {  

    } finally {  

      // Always close it:  

      try {  

        socket。close();  

      } catch(IOExcept ion e) {}  

      threadcount…; // Ending this thread  

    }  

  }  

}  

  

public class MultiJabberClient {  

  static final int MAX_THREADS = 40;  

  public static void main(String'' args)   

      throws IOException; InterruptedException {  

    InetAddress addr =   

      InetAddress。getByName(null);  

    while(true) {  

      if(JabberClientThread。threadCount()   

         《 MAX_THREADS)  

        new JabberClientThread(addr);  

      Thread。currentThread()。sleep(100);  

    }  

  }  

} ///:~  

  



                                                                                             546 


…………………………………………………………Page 548……………………………………………………………

JabberClientThread 构建器获取一个 InetAddress,并用它打开一个套接字。大家可能已看出了这样的一个 

套路:Socket 肯定用于创建某种 Reader 以及/或者Writer (或者InputStream和/或 OutputStream)对 

象,这是运用Socket 的唯一方式(当然,我们可考虑编写一、两个类,令其自动完成这些操作,避免大量重 

复的代码编写工作)。同样地,start()执行线程的初始化,并调用run()。在这里,消息发送给服务器,而 

来自服务器的信息则在屏幕上回显出来。然而,线程的“存在时间”是有限的,最终都会结束。注意在套接 

字创建好以后,但在构建器完成之前,假若构建器失败,套接字会被清除

返回目录 上一页 下一页 回到顶部 0 1

你可能喜欢的