JAVA套接字(Socket)101七天系列—第五天【一个多线程的示例】
2012-06-05 09:25
453 查看
[b]一个多线程的示例
[/b]
1. 介绍
前面的示例教给您基础知识,但并不能令您更深入。如果您到此就停止了,那么您一次只能处理一台客户机。原因是
要开始同时处理多台客户机,并不需要对
2. 接受(太多)连接
这里我们实现改动过的
新的 server 仍然需要
这里是它的工作机制。假设我们指定待发数(backlog 值)是 5 并且有五台客户机请求连接到我们的服务器。我们的服务器将着手处理第一个连接,但处理该连接需要很长时间。由于我们的待发值是 5,所以我们一次可以放五个请求到队列中。我们正在处理一个,所以这意味着还有其它五个正在等待。等待的和正在处理的一共有六个。当我们的服务器仍忙于接受一号连接(记住队列中还有 2―6 号)时,如果有第七个客户机提出连接申请,那么,该第七个客户机将遭到拒绝。我们将在带有连接池服务器示例中说明如何限定能同时连接的客户机数目。
3. 处理连接:第 1 部分
这里我们将讨论
我们对
4. 处理连接:第 2 部分
这里是
这个助手类相当简单。跟我们到目前为止的其它类一样,我们导入
类的构造器用一个 Socket 实例作参数并将它赋给
请注意该类实现了
5. 实现 run()
这里我们实现
请记住我们应该从客户机获取一条有效的文件路径,这样用该路径名构造一个新
6. 总结一下多线程服务器
我们的多线程服务器研究完了。在我们接着讨论带有连接池示例之前,让我们回顾一下创建和使用“多线程版”的服务器的步骤:
修改
修改
借用
ConnectionHandler 的完整代码清单
[/b]
1. 介绍
前面的示例教给您基础知识,但并不能令您更深入。如果您到此就停止了,那么您一次只能处理一台客户机。原因是
handleConnection()是一个阻塞方法。只有当它完成了对当前连接的处理时,服务器才能接受另一个客户机。在多数时候,您将需要(也有必要)一个多线程服务器。
要开始同时处理多台客户机,并不需要对
RemoteFileServer作太多改变。事实上,要是我们前面讨论过待发(backlog),那我们就只需改变一个方法,虽然我们将需要创建一些新东西来处理进入的连接。这里我们还将向您展示
ServerSocket如何处理众多等待(备份)使用服务器的客户机。本示例对线程的低效使用,所以请耐心点。
2. 接受(太多)连接
这里我们实现改动过的
acceptConnections()方法,它将创建一个能够处理待发请求的
ServerSocket,并告诉
ServerSocket接受连接:
public void acceptConnections() { try { ServerSocket server = new ServerSocket(listenPort, 5); Socket incomingConnection = null; while (true) { incomingConnection = server.accept(); handleConnection(incomingConnection); } } catch (BindException e) { System.out.println("Unable to bind to port " + listenPort); } catch (IOException e) { System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort); } }
新的 server 仍然需要
acceptConnections(),所以这些代码实际上是一样的。突出显示的行表示一个重大的不同。对这个多线程版,我们现在可以指定客户机请求的最大数目,这些请求都能在实例化
ServerSocket期间处于待发状态。如果我们没有指定客户机请求的最大数目,则我们假设使用缺省值 50。
这里是它的工作机制。假设我们指定待发数(backlog 值)是 5 并且有五台客户机请求连接到我们的服务器。我们的服务器将着手处理第一个连接,但处理该连接需要很长时间。由于我们的待发值是 5,所以我们一次可以放五个请求到队列中。我们正在处理一个,所以这意味着还有其它五个正在等待。等待的和正在处理的一共有六个。当我们的服务器仍忙于接受一号连接(记住队列中还有 2―6 号)时,如果有第七个客户机提出连接申请,那么,该第七个客户机将遭到拒绝。我们将在带有连接池服务器示例中说明如何限定能同时连接的客户机数目。
3. 处理连接:第 1 部分
这里我们将讨论
handleConnection()方法的结构,这个方法生成一个新的
Thread来处理每个连接。我们将分两部分讨论这个问题。这一屏我们将着重该方法本身,然后在下一屏研究该方法所使用的
ConnectionHandler助手类的结构。
public void handleConnection(Socket connectionToHandle) { new Thread(new ConnectionHandler(connectionToHandle)).start(); }
我们对
RemoteFileServer所做的大改动就体现在这个方法上。我们仍然在服务器接受一个连接之后调用
handleConnection(),但现在我们把该
Socket传递给
ConnectionHandler的一个实例,它是
Runnable的。我们用
ConnectionHandler创建一个新
Thread并启动它。
ConnectionHandler的
run()方法包含
Socket读/写和读
File的代码,这些代码原来在
RemoteFileServer的
handleConnection()中。
4. 处理连接:第 2 部分
这里是
ConnectionHandler类的结构:
import java.io.*; import java.net.*; public class ConnectionHandler implements Runnable{ Socket socketToHandle; public ConnectionHandler(Socket aSocketToHandle) { socketToHandle = aSocketToHandle; } public void run() { } }
这个助手类相当简单。跟我们到目前为止的其它类一样,我们导入
java.net和
java.io。该类只有一个实例变量
socketToHandle,它保存由该实例处理的
Socket。
类的构造器用一个 Socket 实例作参数并将它赋给
socketToHandle。
请注意该类实现了
Runnable接口。实现这个接口的类都必须实现
run()方法,我们的类就是这样做的。稍后我们将探究
run()的细节。现在只需知道它将实际处理连接,所用的代码跟我们先前在
RemoteFileServer类中看到的是一样的。
5. 实现 run()
这里我们实现
run()方法,它将攫取我们的连接的流,用它来读写该连接,并在任务完成之后关闭它:
public void run() { try { PrintWriter streamWriter = new PrintWriter(socketToHandle.getOutputStream()); BufferedReader streamReader = new BufferedReader(new InputStreamReader(socketToHandle.getInputStream())); String fileToRead = streamReader.readLine(); BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead)); String line = null; while ((line = fileReader.readLine()) != null) streamWriter.println(line); fileReader.close(); streamWriter.close(); streamReader.close(); } catch (Exception e) { System.out.println("Error handling a client: " + e); } }
ConnectionHandler的
run()方法所做的事情就是
RemoteFileServer上的
handleConnection()所做的事情。首先,我们把
InputStream和
OutputStream分别包装(用
Socket的
getOutputStream()和
getInputStream())进
BufferedReader和
PrintWriter。然后我们用这些代码逐行地读目标文件:
FileReader fileReader = new FileReader(new File(streamReader.readLine())); BufferedReader bufferedFileReader = new BufferedReader(fileReader); String line = null; while ((line = bufferedFileReader.readLine()) != null) { streamWriter.println(line); }
请记住我们应该从客户机获取一条有效的文件路径,这样用该路径名构造一个新
File,把它包装进
FileReader以处理读文件的操作,然后把它包装进
BufferedReader以让我们逐行地读该文件。我们在
while循环中调用
BufferedReader上的
readLine()直到不再有要读的行。请记注,对
readLine()的调用将造成阻塞,直到有字节来到为止。我们获取一些字节之后就把它们放到本地的
line变量中,然后写出到客户机上。完成读写操作之后,我们关闭打开的流。
6. 总结一下多线程服务器
我们的多线程服务器研究完了。在我们接着讨论带有连接池示例之前,让我们回顾一下创建和使用“多线程版”的服务器的步骤:
修改
acceptConnections()以用缺省为 50(或任何您想要的大于 1 的指定数字)实例化
ServerSocket。
修改
ServerSocket的
handleConnection()以用
ConnectionHandler的一个实例生成一个新的
Thread。
借用
RemoteFileServer的
handleConnection()方法的代码实现
ConnectionHandler类。 附:
MultithreadedRemoteFileServer的完整代码清单
MultithreadedRemoteFileServer 的代码清单 第 4 页(共7 页) import java.io.*; import java.net.*; public class MultithreadedRemoteFileServer { protected int listenPort; public MultithreadedRemoteFileServer(int aListenPort) { listenPort = aListenPort; } public void acceptConnections() { try { ServerSocket server = new ServerSocket(listenPort, 5); Socket incomingConnection = null; while (true) { incomingConnection = server.accept(); handleConnection(incomingConnection); } } catch (BindException e) { System.out.println("Unable to bind to port " + listenPort); } catch (IOException e) { System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort); } } public void handleConnection(Socket connectionToHandle) { new Thread(new ConnectionHandler(connectionToHandle)).start(); } public static void main(String[] args) { MultithreadedRemoteFileServer server = new MultithreadedRemoteFileServer(3000); server.acceptConnections(); } }
ConnectionHandler 的完整代码清单
import java.io.*; import java.net.*; public class ConnectionHandler implements Runnable { protected Socket socketToHandle; public ConnectionHandler(Socket aSocketToHandle) { socketToHandle = aSocketToHandle; } public void run() { try { PrintWriter streamWriter = new PrintWriter(socketToHandle.getOutputStream()); BufferedReader streamReader = new BufferedReader(new InputStreamReader(socketToHandle.getInputStream())); String fileToRead = streamReader.readLine(); BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead)); String line = null; while ((line = fileReader.readLine()) != null) streamWriter.println(line); fileReader.close(); streamWriter.close(); streamReader.close(); } catch (Exception e) { System.out.println("Error handling a client: " + e); } } }
相关文章推荐
- JAVA套接字(Socket)101七天系列—第四天【一个简单示例】
- JAVA套接字(Socket)101七天系列—第六天【一个带有连接池的示例】
- JAVA套接字(Socket)101七天系列—第三天【一个秘密的套接字】
- JAVA套接字(Socket)101七天系列—第二天【套接字基础】 .
- JAVA套接字(Socket)101七天系列—第一天【百度百科的解释】
- JAVA套接字(Socket)101七天系列—第七天【现实生活中的套接字】
- 用Java的套接字编程实现一个多线程的回显(echo)服务器。
- Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例 (r)
- Java笔记(9)-网络编程、URL、InetAddress、套接字、Socket、ServerSocket、多线程、UDP、广播数据包、远程调用
- Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例
- Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例
- Java IO——Socket:通过多线程实现多客户端与一个服务端通信
- Java基于Socket实现简单的多线程回显服务器功能示例
- Java - 用Java的套接字编程实现一个多线程的回显(echo)服务器。
- java使用socket实现一个多线程web服务器
- Java IO——Socket:通过多线程实现多客户端与一个服务端通信
- Java网络与多线程系列之1:实现一个简单的对象池
- Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例
- Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例
- 0038 Java学习笔记-多线程-传统线程间通信、Condition、阻塞队列、《疯狂Java讲义 第三版》进程间通信示例代码存在的一个问题