socket详解

zkn_CS_DN_2013 2016-06-03 05:54:52
6. 总结一下多线程服务器
我们的多线程服务器研究完了。在我们接着讨论带有连接池示例之前,让我们回顾一下创建和使用“多线程版”的服务器的步骤:
1. 修改 acceptConnections() 以用缺省为 50(或任何您想要的大于 1 的指定数字)实例化 ServerSocket。
2. 修改 ServerSocket 的 handleConnection() 以用 ConnectionHandler 的一个实例生成一个新的 Thread。
3. 借用 RemoteFileServer 的 handleConnection() 方法的代码实现 ConnectionHandler 类。
附: MultithreadedRemoteFileServer 的完整代码清单
Java代码
1. import java.io.*;
2. import java.net.*;
3.
4. public class MultithreadedRemoteFileServer {
5. protected int listenPort;
6. public MultithreadedRemoteFileServer(int aListenPort) {
7. listenPort = aListenPort;
8. }
9. public void acceptConnections() {
10. try {
11. ServerSocket server = new ServerSocket(listenPort, 5);
12. Socket incomingConnection = null;
13. while (true) {
14. incomingConnection = server.accept();
15. handleConnection(incomingConnection);
16. }
17. } catch (BindException e) {
18. System.out.println("Unable to bind to port " + listenPort);
19. } catch (IOException e) {
20. System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort);
21. }
22. }
23. public void handleConnection(Socket connectionToHandle) {
24. new Thread(new ConnectionHandler(connectionToHandle)).start();
25. }
26. public static void main(String[] args) {
27. MultithreadedRemoteFileServer server = new MultithreadedRemoteFileServer(3000);
28. server.acceptConnections();
29. }
30. }
[java] view plain copy
print?
1. import java.io.*;
2. import java.net.*;
3.
4. public class MultithreadedRemoteFileServer {
5. protected int listenPort;
6. public MultithreadedRemoteFileServer(int aListenPort) {
7. listenPort = aListenPort;
8. }
9. public void acceptConnections() {
10. try {
11. ServerSocket server = new ServerSocket(listenPort, 5);
12. Socket incomingConnection = null;
13. while (true) {
14. incomingConnection = server.accept();
15. handleConnection(incomingConnection);
16. }
17. } catch (BindException e) {
18. System.out.println("Unable to bind to port " + listenPort);
19. } catch (IOException e) {
20. System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort);
21. }
22. }
23. public void handleConnection(Socket connectionToHandle) {
24. new Thread(new ConnectionHandler(connectionToHandle)).start();
25. }
26. public static void main(String[] args) {
27. MultithreadedRemoteFileServer server = new MultithreadedRemoteFileServer(3000);
28. server.acceptConnections();
29. }
30. }

ConnectionHandler 的完整代码清单
Java代码
1. import java.io.*;
2. import java.net.*;
3.
4. public class ConnectionHandler implements Runnable {
5. protected Socket socketToHandle;
6. public ConnectionHandler(Socket aSocketToHandle) {
7. socketToHandle = aSocketToHandle;
8. }
9. public void run() {
10. try {
11. PrintWriter streamWriter = new PrintWriter(socketToHandle.getOutputStream());
12. BufferedReader streamReader = new BufferedReader(new InputStreamReader(socketToHandle.getInputStream()));
13.
14. String fileToRead = streamReader.readLine();
15. BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead));
16.
17. String line = null;
18. while ((line = fileReader.readLine()) != null)
19. streamWriter.println(line);
20.
21. fileReader.close();
22. streamWriter.close();
23. streamReader.close();
24. } catch (Exception e) {
25. System.out.println("Error handling a client: " + e);
26. }
27. }
28. }
[java] view plain copy
print?
1. import java.io.*;
2. import java.net.*;
3.
4. public class ConnectionHandler implements Runnable {
5. protected Socket socketToHandle;
6. public ConnectionHandler(Socket aSocketToHandle) {
7. socketToHandle = aSocketToHandle;
8. }
9. public void run() {
10. try {
11. PrintWriter streamWriter = new PrintWriter(socketToHandle.getOutputStream());
12. BufferedReader streamReader = new BufferedReader(new InputStreamReader(socketToHandle.getInputStream()));
13.
14. String fileToRead = streamReader.readLine();
15. BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead));
16.
17. String line = null;
18. while ((line = fileReader.readLine()) != null)
19. streamWriter.println(line);
20.
21. fileReader.close();
22. streamWriter.close();
23. streamReader.close();
24. } catch (Exception e) {
25. System.out.println("Error handling a client: " + e);
26. }
27. }
28. }
...全文
191 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
9. 总结一下带有连接池的服务器 我们的带有连接池的服务器研究完了。让我们回顾一下创建和使用“池版”服务器的步骤: 1. 创建一个新种类的连接处理程序(我们称之为 PooledConnectionHandler)来处理池中的连接。 2. 修改服务器以创建和使用一组 PooledConnectionHandler。 附: PooledRemoteFileServer 的完整代码清单 Java代码 1. import java.io.*; 2. import java.net.*; 3. import java.util.*; 4. 5. public class PooledRemoteFileServer { 6. protected int maxConnections; 7. protected int listenPort; 8. protected ServerSocket serverSocket; 9. public PooledRemoteFileServer(int aListenPort, int maxConnections) { 10. listenPort = aListenPort; 11. this.maxConnections = maxConnections; 12. } 13. public void acceptConnections() { 14. try { 15. ServerSocket server = new ServerSocket(listenPort, 5); 16. Socket incomingConnection = null; 17. while (true) { 18. incomingConnection = server.accept(); 19. handleConnection(incomingConnection); 20. } 21. } catch (BindException e) { 22. System.out.println("Unable to bind to port " + listenPort); 23. } catch (IOException e) { 24. System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort); 25. } 26. } 27. protected void handleConnection(Socket connectionToHandle) { 28. PooledConnectionHandler.processRequest(connectionToHandle); 29. } 30. public static void main(String[] args) { 31. PooledRemoteFileServer server = new PooledRemoteFileServer(3000, 3); 32. server.setUpHandlers(); 33. server.acceptConnections(); 34. } 35. public void setUpHandlers() { 36. for (int i = 0; i < maxConnections; i++) { 37. PooledConnectionHandler currentHandler = new PooledConnectionHandler(); 38. new Thread(currentHandler, "Handler " + i).start(); 39. } 40. } 41. } [java] view plain copy print? 1. import java.io.*; 2. import java.net.*; 3. import java.util.*; 4. 5. public class PooledRemoteFileServer { 6. protected int maxConnections; 7. protected int listenPort; 8. protected ServerSocket serverSocket; 9. public PooledRemoteFileServer(int aListenPort, int maxConnections) { 10. listenPort = aListenPort; 11. this.maxConnections = maxConnections; 12. } 13. public void acceptConnections() { 14. try { 15. ServerSocket server = new ServerSocket(listenPort, 5); 16. Socket incomingConnection = null; 17. while (true) { 18. incomingConnection = server.accept(); 19. handleConnection(incomingConnection); 20. } 21. } catch (BindException e) { 22. System.out.println("Unable to bind to port " + listenPort); 23. } catch (IOException e) { 24. System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort); 25. } 26. } 27. protected void handleConnection(Socket connectionToHandle) { 28. PooledConnectionHandler.processRequest(connectionToHandle); 29. } 30. public static void main(String[] args) { 31. PooledRemoteFileServer server = new PooledRemoteFileServer(3000, 3); 32. server.setUpHandlers(); 33. server.acceptConnections(); 34. } 35. public void setUpHandlers() { 36. for (int i = 0; i < maxConnections; i++) { 37. PooledConnectionHandler currentHandler = new PooledConnectionHandler(); 38. new Thread(currentHandler, "Handler " + i).start(); 39. } 40. } 41. } PooledConnectionHandler 的完整代码清单 Java代码 1. import java.io.*; 2. import java.net.*; 3. import java.util.*; 4. 5. public class PooledConnectionHandler implements Runnable { 6. protected Socket connection; 7. protected static List pool = new LinkedList(); 8. public PooledConnectionHandler() { 9. } 10. public void handleConnection() { 11. try { 12. PrintWriter streamWriter = new PrintWriter(connection.getOutputStream()); 13. BufferedReader streamReader = new BufferedReader(new InputStreamReader(connection.getInputStream())); 14. 15. String fileToRead = streamReader.readLine(); 16. BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead)); 17. 18. String line = null; 19. while ((line = fileReader.readLine()) != null) 20. streamWriter.println(line); 21. 22. fileReader.close(); 23. streamWriter.close(); 24. streamReader.close(); 25. } catch (FileNotFoundException e) { 26. System.out.println("Could not find requested file on the server."); 27. } catch (IOException e) { 28. System.out.println("Error handling a client: " + e); 29. } 30. } 31. public static void processRequest(Socket requestToHandle) { 32. synchronized (pool) { 33. pool.add(pool.size(), requestToHandle); 34. pool.notifyAll(); 35. } 36. } 37. public void run() { 38. while (true) { 39. synchronized (pool) { 40. while (pool.isEmpty()) { 41. try { 42. pool.wait(); 43. } catch (InterruptedException e) { 44. return; 45. } 46. } 47. connection = (Socket) pool.remove(0); 48. } 49. handleConnection(); 50. } 51. } 52. }
  • 打赏
  • 举报
回复
6. 填充连接池 这里我们实现 PooledConnectionHandler 上的 processRequest() 方法,它将把传入请求添加到池中,并告诉其它正在等待的对象该池已经有一些内容: Java代码 1. public static void processRequest(Socket requestToHandle) { 2. synchronized (pool) { 3. pool.add(pool.size(), requestToHandle); 4. pool.notifyAll(); 5. } 6. } [java] view plain copy print? 1. public static void processRequest(Socket requestToHandle) { 2. synchronized (pool) { 3. pool.add(pool.size(), requestToHandle); 4. pool.notifyAll(); 5. } 6. } 理解这个方法要求有一点关于 Java 的关键字 synchronized 如何工作的背景知识。我们将简要讲述一下线程。 先来看一些定义: • 原子方法。在执行过程中不能被中断的方法(或代码块) • 互斥锁。客户机欲执行原子方法时必须获得的单个“锁” 因此,当对象 A 想使用对象 B 的 synchronized 方法 doSomething() 时,对象 A 必须首先尝试获取对象 B 的互斥锁。是的,这意味着当对象 A 拥有该互斥锁时,没有其它对象可以调用对象 B 上任何其它 synchronized 方法。 synchronized 块是个稍微有些不同的东西。您可以同步任何对象上的一个块,而不只是在本身的某个方法中含有该块的对象。在我们的示例中,processRequest() 方法包含有一个 pool(请记住它是一个 LinkedList,保存等待处理的连接池)的 synchronized块。我们这样做的原因是确保没有别人能跟我们同时修改连接池。 既然我们已经保证了我们是唯一“涉水”池中的人,我们就可以把传入的 Socket 添加到 LinkedList 的尾端。一旦我们添加了新的连接,我们就用以下代码通知其它正在等待该池的 Thread,池现在已经可用: Java代码 1. pool.notifyAll(); [java] view plain copy print? 1. pool.notifyAll(); Object 的所有子类都继承这个 notifyAll() 方法。这个方法,连同我们下一屏将要讨论的 wait() 方法一起,就使一个 Thread 能够让另一个 Thread 知道一些条件已经具备。这意味着该第二个 Thread 一定正在等待那些条件的满足。 7. 从池中获取连接 这里我们实现 PooledConnectionHandler 上需作改动的 run()方法,它将在连接池上等待,并且池中一有连接就处理它: Java代码 1. public void run() { 2. while (true) { 3. synchronized (pool) { 4. while (pool.isEmpty()) { 5. try { 6. pool.wait(); 7. } catch (InterruptedException e) { 8. return; 9. } 10. } 11. connection = (Socket) pool.remove(0); 12. } 13. handleConnection(); 14. } 15. } [java] view plain copy print? 1. public void run() { 2. while (true) { 3. synchronized (pool) { 4. while (pool.isEmpty()) { 5. try { 6. pool.wait(); 7. } catch (InterruptedException e) { 8. return; 9. } 10. } 11. connection = (Socket) pool.remove(0); 12. } 13. handleConnection(); 14. } 15. } 回想一下在前一屏讲过的:一个 Thread 正在等待有人通知它连接池方面的条件已经满足了。在我们的示例中,请记住我们有三个PooledConnectionHandler 在等待使用池中的连接。每个 PooledConnectionHandler 都在它自已的 Thread 中运行,并通过调用 pool.wait() 产生阻塞。当我们的 processRequest() 在连接池上调用 notifyAll() 时,所有正在等待的 PooledConnectionHandler 都将得到“池已经可用”的通知。然后各自继续前行调用 pool.wait(),并重新检查 while(pool.isEmpty()) 循环条件。除了一个处理程序,其它池对所有处理程序都将是空的,因此,在调用 pool.wait()时,除了一个处理程序,其它所有处理程序都将再次产生阻塞。恰巧碰上非空池的处理程序将跳出 while(pool.isEmpty()) 循环并攫取池中的第一个连接: Java代码 1. connection = (Socket) pool.remove(0); [java] view plain copy print? 1. connection = (Socket) pool.remove(0); 处理程序一旦有一个连接可以使用,就调用 handleConnection() 处理它。 在我们的示例中,池中可能永远不会有多个连接,只是因为事情很快就被处理掉了。如果池中有一个以上连接,那么其它处理程序将不必等待新的连接被添加到池。当它们检查 pool.isEmpty() 条件时,将发现其值为假,然后就从池中攫取一个连接并处理它。 还有另一件事需注意。当 run() 拥有池的互斥锁时,processRequest() 如何能够把连接放到池中呢?答案是对池上的 wait() 的调用释放锁,而 wait() 接着就在自己返回之前再次攫取该锁。这就使得池对象的其它同步代码可以获取该锁。 8. 处理连接:再一次 这里我们实现需做改动的 handleConnection() 方法,该方法将攫取连接的流,使用它们,并在任务完成之后清除它们: Java代码 1. public void handleConnection() { 2. try { 3. PrintWriter streamWriter = new PrintWriter(connection.getOutputStream()); 4. BufferedReader streamReader = 5. new BufferedReader(new InputStreamReader(connection.getInputStream())); 6. 7. String fileToRead = streamReader.readLine(); 8. BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead)); 9. 10. String line = null; 11. while ((line = fileReader.readLine()) != null) 12. streamWriter.println(line); 13. 14. fileReader.close(); 15. streamWriter.close(); 16. streamReader.close(); 17. } catch (FileNotFoundException e) { 18. System.out.println("Could not find requested file on the server."); 19. } catch (IOException e) { 20. System.out.println("Error handling a client: " + e); 21. } 22. } [java] view plain copy print? 1. public void handleConnection() { 2. try { 3. PrintWriter streamWriter = new PrintWriter(connection.getOutputStream()); 4. BufferedReader streamReader = 5. new BufferedReader(new InputStreamReader(connection.getInputStream())); 6. 7. String fileToRead = streamReader.readLine(); 8. BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead)); 9. 10. String line = null; 11. while ((line = fileReader.readLine()) != null) 12. streamWriter.println(line); 13. 14. fileReader.close(); 15. streamWriter.close(); 16. streamReader.close(); 17. } catch (FileNotFoundException e) { 18. System.out.println("Could not find requested file on the server."); 19. } catch (IOException e) { 20. System.out.println("Error handling a client: " + e); 21. } 22. } 跟在多线程服务器中不同,我们的 PooledConnectionHandler 有一个 handleConnection() 方法。这个方法的代码跟非池式的 ConnectionHandler 上的run() 方法的代码完全一样。首先,我们把 OutputStream 和 InputStream 分别包装进(用 Socket 上的 getOutputStream() 和getInputStream())BufferedReader 和 PrintWriter。然后我们逐行读目标文件,就象我们在多线程示例中做的那样。再一次,我们获取一些字节之后就把它们放到本地的 line 变量中,然后写出到客户机。完成读写操作之后,我们关闭 FileReader 和打开的流。
  • 打赏
  • 举报
回复
一个带有连接池的示例 1. 介绍 我们现在已经拥有的 MultithreadedServer 每当有客户机申请一个连接时都在一个新 Thread 中创建一个新 ConnectionHandler。这意味着可能有一捆Thread “躺”在我们周围。而且创建 Thread 的系统开销并不是微不足道的。如果性能成为了问题(也请不要事到临头才意识到它),更高效地处理我们的服务器是件好事。那么,我们如何更高效地管理服务器端呢?我们可以维护一个进入的连接池,一定数量的 ConnectionHandler 将为它提供服务。这种设计能带来以下好处: • 它限定了允许同时连接的数目。 • 我们只需启动 ConnectionHandler Thread 一次。 幸运的是,跟在我们的多线程示例中一样,往代码中添加“池”不需要来一个大改动。事实上,应用程序的客户机端根本就不受影响。在服务器端,我们在服务器启动时创建一定数量的 ConnectionHandler,我们把进入的连接放入“池”中并让 ConnectionHandler 打理剩下的事情。这种设计中有很多我们不打算讨论的可能存在的技巧。例如,我们可以通过限定允许在“池”中建立的连接的数目来拒绝客户机。 请注意:我们将不会再次讨论 acceptConnections()。这个方法跟前面示例中的完全一样。它无限循环地调用 ServerSocket 上的 accept() 并把连接传递到 handleConnection()。 2. 创建 PooledRemoteFileServer 类 这里是 PooledRemoteFileServer 类的结构: Java代码 1. import java.io.*; 2. import java.net.*; 3. import java.util.*; 4. 5. public class PooledRemoteFileServer { 6. protected int maxConnections; 7. protected int listenPort; 8. protected ServerSocket serverSocket; 9. 10. public PooledRemoteFileServer(int aListenPort, int maxConnections) { 11. listenPort = aListenPort; 12. this.maxConnections = maxConnections; 13. } 14. public static void main(String[] args) { 15. } 16. public void setUpHandlers() { 17. } 18. public void acceptConnections() { 19. } 20. protected void handleConnection(Socket incomingConnection) { 21. } 22. } [java] view plain copy print? 1. import java.io.*; 2. import java.net.*; 3. import java.util.*; 4. 5. public class PooledRemoteFileServer { 6. protected int maxConnections; 7. protected int listenPort; 8. protected ServerSocket serverSocket; 9. 10. public PooledRemoteFileServer(int aListenPort, int maxConnections) { 11. listenPort = aListenPort; 12. this.maxConnections = maxConnections; 13. } 14. public static void main(String[] args) { 15. } 16. public void setUpHandlers() { 17. } 18. public void acceptConnections() { 19. } 20. protected void handleConnection(Socket incomingConnection) { 21. } 22. } 请注意一下您现在应该熟悉了的 import 语句。我们给类以下实例变量以保存: • 我们的服务器能同时处理的活动客户机连接的最大数目 • 进入的连接的侦听端口(我们没有指定缺省值,但如果您想这样做,并不会受到限制) • 将接受客户机连接请求的 ServerSocket 类的构造器用的参数是侦听端口和连接的最大数目 我们的类有一个 main() 方法和三个其它方法。稍后我们将探究这些方法的细节。现在只须知道 setUpHandlers() 创建数目为 maxConnections 的大量PooledConnectionHandler,而其它两个方法则与我们前面已经看到的相似:acceptConnections() 在 ServerSocket 上侦听传入的客户机连接,而handleConnection 则在客户机连接一旦被建立后就实际处理它。 3. 实现 main() 这里我们实现需作改动的 main() 方法,该方法将创建能够处理给定数目的客户机连接的 PooledRemoteFileServer,并告诉它接受连接: Java代码 1. public static void main(String[] args) { 2. PooledRemoteFileServer server = new PooledRemoteFileServer(3000, 3); 3. server.setUpHandlers(); 4. server.acceptConnections(); 5. } [java] view plain copy print? 1. public static void main(String[] args) { 2. PooledRemoteFileServer server = new PooledRemoteFileServer(3000, 3); 3. server.setUpHandlers(); 4. server.acceptConnections(); 5. } 我们的 main() 方法很简单。我们实例化一个新的 PooledRemoteFileServer,它将通过调用 setUpHandlers() 来建立三个 PooledConnectionHandler。一旦服务器就绪,我们就告诉它 acceptConnections()。 4. 建立连接处理程序 Java代码 1. public void setUpHandlers() { 2. for (int i = 0; i < maxConnections; i++) { 3. PooledConnectionHandler currentHandler = new PooledConnectionHandler(); 4. new Thread(currentHandler, "Handler " + i).start(); 5. } 6. } [java] view plain copy print? 1. public void setUpHandlers() { 2. for (int i = 0; i < maxConnections; i++) { 3. PooledConnectionHandler currentHandler = new PooledConnectionHandler(); 4. new Thread(currentHandler, "Handler " + i).start(); 5. } 6. } setUpHandlers() 方法创建 maxConnections(例如 3)个 PooledConnectionHandler 并在新 Thread 中激活它们。用实现了 Runnable 的对象来创建 Thread使我们可以在 Thread 调用 start() 并且可以期望在 Runnable 上调用了 run()。换句话说,我们的 PooledConnectionHandler 将等着处理进入的连接,每个都在它自己的 Thread 中进行。我们在示例中只创建三个 Thread,而且一旦服务器运行,这就不能被改变。 5. 处理连接 这里我们实现需作改动的 handleConnections() 方法,它将委派 PooledConnectionHandler 处理连接: Java代码 1. protected void handleConnection(Socket connectionToHandle) { 2. PooledConnectionHandler.processRequest(connectionToHandle); 3. } [java] view plain copy print? 1. protected void handleConnection(Socket connectionToHandle) { 2. PooledConnectionHandler.processRequest(connectionToHandle); 3. } 我们现在叫 PooledConnectionHandler 处理所有进入的连接(processRequest() 是一个静态方法)。 这里是 PooledConnectionHandler 类的结构: Java代码 1. import java.io.*; 2. import java.net.*; 3. import java.util.*; 4. 5. public class PooledConnectionHandler implements Runnable { 6. protected Socket connection; 7. protected static List pool = new LinkedList(); 8. 9. public PooledConnectionHandler() { 10. } 11. public void handleConnection() { 12. } 13. public static void processRequest(Socket requestToHandle) { 14. } 15. public void run() { 16. } 17. } [java] view plain copy print? 1. import java.io.*; 2. import java.net.*; 3. import java.util.*; 4. 5. public class PooledConnectionHandler implements Runnable { 6. protected Socket connection; 7. protected static List pool = new LinkedList(); 8. 9. public PooledConnectionHandler() { 10. } 11. public void handleConnection() { 12. } 13. public static void processRequest(Socket requestToHandle) { 14. } 15. public void run() { 16. } 17. } 这个助手类与 ConnectionHandler 非常相似,但它带有处理连接池的手段。该类有两个实例变量: • connection 是当前正在处理的 Socket • 名为 pool 的静态 LinkedList 保存需被处理的连接

23,404

社区成员

发帖
与我相关
我的任务
社区描述
Java 非技术区
社区管理员
  • 非技术区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧