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.net.*; 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: 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(); } } } ///:~
每次有新客户请求成立一个毗连时,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.net.*; 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())); // 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(IOException 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); } } } ///:~
#p#分页标题#e#
JabberClientThread构建器获取一个InetAddress,并用它打开一个套接字。各人大概已看出了这样的一个套路:Socket必定用于建设某种Reader以及/可能Writer(可能InputStream和/或OutputStream)工具,这是运用Socket的独一方法(虽然,我们可思量编写一、两个类,令其自动完成这些操纵,制止大量反复的代码编写事情)。同样地,start()执行线程的初始化,并挪用run()。在这里,动静发送给处事器,而来自处事器的信息则在屏幕上回显出来。然而,线程的“存在时间”是有限的,最终城市竣事。留意在套接字建设好今后,但在构建器完成之前,假使构建器失败,套接字会被排除。不然,为套接字挪用close()的责任便落到了run()要领的头上。
threadcount跟踪计较今朝存在的JabberClientThread工具的数量。它将作为构建器的一部门增值,并在run()退出时减值(run()退出意味着线程中止)。在MultiJabberClient.main()中,各人可以看到线程的数量会获得查抄。若数量太多,则多余的临时不建设。要领随后进入“休眠”状态。这样一来,一旦部门线程最后被中止,多作的那些线程就可以建设了。各人可试验一下逐渐增大MAX_THREADS,看看对付你利用的系统来说,成立几多线程(毗连)才会使您的系统资源低落到危险水平。