socket详解

zkn_CS_DN_2013 2016-06-03 05:53:08
8. 创建 RemoteFileServer 类
这里是 RemoteFileServer 类的结构:

Java代码
1. import java.io.*;
2. import java.net.*;
3.
4. public class RemoteFileServer {
5. protected int listenPort = 3000;
6. public static void main(String[] args) {
7. }
8. public void acceptConnections() {
9. }
10. public void handleConnection(Socket incomingConnection) {
11. }
12. }
[java] view plain copy
print?
1. import java.io.*;
2. import java.net.*;
3.
4. public class RemoteFileServer {
5. protected int listenPort = 3000;
6. public static void main(String[] args) {
7. }
8. public void acceptConnections() {
9. }
10. public void handleConnection(Socket incomingConnection) {
11. }
12. }
跟客户机中一样,我们首先导入 java.net 的 java.io。接着,我们给我们的类一个实例变量以保存端口,我们从该端口侦听进入的连接。缺省情况下,端口是 3000。
我们的类有一个 main() 方法和两个其它方法。稍后我们将探究这些方法的细节。现在您只需知道 acceptConnections() 将允许客户机连接到服务器以及handleConnection() 与客户机 Socket 交互以将您所请求的文件的内容发送到客户机。

9. 实现 main()
这里我们实现 main() 方法,它将创建 RemoteFileServer 并告诉它接受连接:

Java代码
1. public static void main(String[] args) {
2. RemoteFileServer server = new RemoteFileServer();
3. server.acceptConnections();
4. }
[java] view plain copy
print?
1. public static void main(String[] args) {
2. RemoteFileServer server = new RemoteFileServer();
3. server.acceptConnections();
4. }
服务器端的 main() 方法甚至比客户机端的更简单。我们实例化一个新 RemoteFileServer,它将在缺省侦听端口上侦听进入的连接请求。然后我们调用acceptConnections() 来告诉该 server 进行侦听。

10. 接受连接
这里我们实现 acceptConnections() 方法,它将创建一个 ServerSocket 并等待连接请求:
Java代码
1. public void acceptConnections() {
2. try {
3. ServerSocket server = new ServerSocket(listenPort);
4. Socket incomingConnection = null;
5. while (true) {
6. incomingConnection = server.accept();
7. handleConnection(incomingConnection);
8. }
9. } catch (BindException e) {
10. System.out.println("Unable to bind to port " + listenPort);
11. } catch (IOException e) {
12. System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort);
13. }
14. } <span>
15. </span>
[java] view plain copy
print?
1. public void acceptConnections() {
2. try {
3. ServerSocket server = new ServerSocket(listenPort);
4. Socket incomingConnection = null;
5. while (true) {
6. incomingConnection = server.accept();
7. handleConnection(incomingConnection);
8. }
9. } catch (BindException e) {
10. System.out.println("Unable to bind to port " + listenPort);
11. } catch (IOException e) {
12. System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort);
13. }
14. } <span>
15. </span>

acceptConnections() 用欲侦听的端口号来创建 ServerSocket。然后我们通过调用该 ServerSocket 的 accept() 来告诉它开始侦听。accept() 方法将造成阻塞直到来了一个连接请求。此时,accept() 返回一个新的 Socket,这个 Socket 绑定到服务器上一个随机指定的端口,返回的 Socket 被传递给handleConnection()。请注意我们在一个无限循环中处理对连接的接受。这里不支持任何关机。
无论何时如果您创建了一个无法绑定到指定端口(可能是因为别的什么控制了该端口)的 ServerSocket,Java 代码都将抛出一个错误。所以这里我们必须捕捉可能的 BindException。就跟在客户机端上时一样,我们必须捕捉 IOException,当我们试图在 ServerSocket 上接受连接时,它就会被抛出。请注意,您可以通过用毫秒数调用 setSoTimeout() 来为 accept() 调用设置超时,以避免实际长时间的等待。调用 setSoTimeout() 将使 accept() 经过指定占用时间后抛出 IOException。
...全文
138 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
4. 处理连接:第 2 部分 这里是 ConnectionHandler 类的结构: Java代码 1. import java.io.*; 2. import java.net.*; 3. 4. public class ConnectionHandler implements Runnable{ 5. Socket socketToHandle; 6. 7. public ConnectionHandler(Socket aSocketToHandle) { 8. socketToHandle = aSocketToHandle; 9. } 10. 11. public void run() { 12. } 13. } [java] view plain copy print? 1. import java.io.*; 2. import java.net.*; 3. 4. public class ConnectionHandler implements Runnable{ 5. Socket socketToHandle; 6. 7. public ConnectionHandler(Socket aSocketToHandle) { 8. socketToHandle = aSocketToHandle; 9. } 10. 11. public void run() { 12. } 13. } 这个助手类相当简单。跟我们到目前为止的其它类一样,我们导入 java.net 和 java.io。该类只有一个实例变量 socketToHandle,它保存由该实例处理的 Socket。 类的构造器用一个 Socket 实例作参数并将它赋给 socketToHandle。 请注意该类实现了 Runnable 接口。实现这个接口的类都必须实现 run() 方法,我们的类就是这样做的。稍后我们将探究 run() 的细节。现在只需知道它将实际处理连接,所用的代码跟我们先前在 RemoteFileServer 类中看到的是一样的。 5. 实现 run() 这里我们实现 run() 方法,它将攫取我们的连接的流,用它来读写该连接,并在任务完成之后关闭它: Java代码 1. public void run() { 2. try { 3. PrintWriter streamWriter = new PrintWriter(socketToHandle.getOutputStream()); 4. BufferedReader streamReader = 5. new BufferedReader(new InputStreamReader(socketToHandle.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 (Exception e) { 18. System.out.println("Error handling a client: " + e); 19. } 20. } [java] view plain copy print? 1. public void run() { 2. try { 3. PrintWriter streamWriter = new PrintWriter(socketToHandle.getOutputStream()); 4. BufferedReader streamReader = 5. new BufferedReader(new InputStreamReader(socketToHandle.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 (Exception e) { 18. System.out.println("Error handling a client: " + e); 19. } 20. } ConnectionHandler 的 run() 方法所做的事情就是 RemoteFileServer 上的 handleConnection() 所做的事情。首先,我们把 InputStream 和 OutputStream分别包装(用 Socket 的 getOutputStream() 和 getInputStream())进 BufferedReader 和 PrintWriter。然后我们用这些代码逐行地读目标文件: Java代码 1. FileReader fileReader = new FileReader(new File(streamReader.readLine())); 2. BufferedReader bufferedFileReader = new BufferedReader(fileReader); 3. String line = null; 4. while ((line = bufferedFileReader.readLine()) != null) { 5. streamWriter.println(line); 6. } [java] view plain copy print? 1. FileReader fileReader = new FileReader(new File(streamReader.readLine())); 2. BufferedReader bufferedFileReader = new BufferedReader(fileReader); 3. String line = null; 4. while ((line = bufferedFileReader.readLine()) != null) { 5. streamWriter.println(line); 6. } 请记住我们应该从客户机获取一条有效的文件路径,这样用该路径名构造一个新 File,把它包装进 FileReader 以处理读文件的操作,然后把它包装进BufferedReader 以让我们逐行地读该文件。我们在 while 循环中调用 BufferedReader 上的 readLine() 直到不再有要读的行。请记注,对 readLine() 的调用将造成阻塞,直到有字节来到为止。我们获取一些字节之后就把它们放到本地的 line 变量中,然后写出到客户机上。完成读写操作之后,我们关闭打开的流。
  • 打赏
  • 举报
回复
一个多线程的示例 1. 介绍 前面的示例教给您基础知识,但并不能令您更深入。如果您到此就停止了,那么您一次只能处理一台客户机。原因是 handleConnection() 是一个阻塞方法。只有当它完成了对当前连接的处理时,服务器才能接受另一个客户机。在多数时候,您将需要(也有必要)一个多线程服务器。 要开始同时处理多台客户机,并不需要对 RemoteFileServer 作太多改变。事实上,要是我们前面讨论过待发(backlog),那我们就只需改变一个方法,虽然我们将需要创建一些新东西来处理进入的连接。这里我们还将向您展示 ServerSocket 如何处理众多等待(备份)使用服务器的客户机。本示例对线程的低效使用,所以请耐心点。 2. 接受(太多)连接 这里我们实现改动过的 acceptConnections() 方法,它将创建一个能够处理待发请求的 ServerSocket,并告诉 ServerSocket 接受连接: Java代码 1. public void acceptConnections() { 2. try { 3. ServerSocket server = new ServerSocket(listenPort, 5); 4. Socket incomingConnection = null; 5. while (true) { 6. incomingConnection = server.accept(); 7. handleConnection(incomingConnection); 8. } 9. } catch (BindException e) { 10. System.out.println("Unable to bind to port " + listenPort); 11. } catch (IOException e) { 12. System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort); 13. } 14. } [java] view plain copy print? 1. public void acceptConnections() { 2. try { 3. ServerSocket server = new ServerSocket(listenPort, 5); 4. Socket incomingConnection = null; 5. while (true) { 6. incomingConnection = server.accept(); 7. handleConnection(incomingConnection); 8. } 9. } catch (BindException e) { 10. System.out.println("Unable to bind to port " + listenPort); 11. } catch (IOException e) { 12. System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort); 13. } 14. } 新的 server 仍然需要 acceptConnections(),所以这些代码实际上是一样的。突出显示的行表示一个重大的不同。对这个多线程版,我们现在可以指定客户机请求的最大数目,这些请求都能在实例化 ServerSocket 期间处于待发状态。如果我们没有指定客户机请求的最大数目,则我们假设使用缺省值 50。 这里是它的工作机制。假设我们指定待发数(backlog 值)是 5 并且有五台客户机请求连接到我们的服务器。我们的服务器将着手处理第一个连接,但处理该连接需要很长时间。由于我们的待发值是 5,所以我们一次可以放五个请求到队列中。我们正在处理一个,所以这意味着还有其它五个正在等待。等待的和正在处理的一共有六个。当我们的服务器仍忙于接受一号连接(记住队列中还有 2―6 号)时,如果有第七个客户机提出连接申请,那么,该第七个客户机将遭到拒绝。我们将在带有连接池服务器示例中说明如何限定能同时连接的客户机数目。 3. 处理连接:第 1 部分 这里我们将讨论 handleConnection() 方法的结构,这个方法生成一个新的 Thread 来处理每个连接。我们将分两部分讨论这个问题。这一屏我们将着重该方法本身,然后在下一屏研究该方法所使用的 ConnectionHandler 助手类的结构。 Java代码 1. public void handleConnection(Socket connectionToHandle) { 2. new Thread(new ConnectionHandler(connectionToHandle)).start(); 3. } [java] view plain copy print? 1. public void handleConnection(Socket connectionToHandle) { 2. new Thread(new ConnectionHandler(connectionToHandle)).start(); 3. } 我们对 RemoteFileServer 所做的大改动就体现在这个方法上。我们仍然在服务器接受一个连接之后调用 handleConnection(),但现在我们把该 Socket 传递给 ConnectionHandler 的一个实例,它是 Runnable 的。我们用 ConnectionHandler 创建一个新 Thread 并启动它。ConnectionHandler 的 run() 方法包含Socket 读/写和读 File 的代码,这些代码原来在 RemoteFileServer 的 handleConnection() 中。
  • 打赏
  • 举报
回复
11. 处理连接 这里我们实现 handleConnection() 方法,它将用连接的流来接收输入和写输出: Java代码 1. public void handleConnection(Socket incomingConnection) { 2. try { 3. OutputStream outputToSocket = incomingConnection.getOutputStream(); 4. InputStream inputFromSocket = incomingConnection.getInputStream(); 5. 6. BufferedReader streamReader = 7. new BufferedReader(new InputStreamReader(inputFromSocket)); 8. 9. FileReader fileReader = new FileReader(new File(streamReader.readLine())); 10. 11. BufferedReader bufferedFileReader = new BufferedReader(fileReader); 12. PrintWriter streamWriter = 13. new PrintWriter(incomingConnection.getOutputStream()); 14. String line = null; 15. while ((line = bufferedFileReader.readLine()) != null) { 16. streamWriter.println(line); 17. } 18. 19. fileReader.close(); 20. streamWriter.close(); 21. streamReader.close(); 22. } catch (Exception e) { 23. System.out.println("Error handling a client: " + e); 24. } 25. } [java] view plain copy print? 1. public void handleConnection(Socket incomingConnection) { 2. try { 3. OutputStream outputToSocket = incomingConnection.getOutputStream(); 4. InputStream inputFromSocket = incomingConnection.getInputStream(); 5. 6. BufferedReader streamReader = 7. new BufferedReader(new InputStreamReader(inputFromSocket)); 8. 9. FileReader fileReader = new FileReader(new File(streamReader.readLine())); 10. 11. BufferedReader bufferedFileReader = new BufferedReader(fileReader); 12. PrintWriter streamWriter = 13. new PrintWriter(incomingConnection.getOutputStream()); 14. String line = null; 15. while ((line = bufferedFileReader.readLine()) != null) { 16. streamWriter.println(line); 17. } 18. 19. fileReader.close(); 20. streamWriter.close(); 21. streamReader.close(); 22. } catch (Exception e) { 23. System.out.println("Error handling a client: " + e); 24. } 25. } 跟在客户机中一样,我们用 getOutputStream() 和 getInputStream() 来获取与我们刚创建的 Socket 相关联的流。跟在客户机端一样,我们把InputStream 包装进 BufferedReader,把 OutputStream 包装进 PrintWriter。在服务器端上,我们需要添加一些代码,用来读取目标文件和把内容逐行发送到客户机。这里是重要的代码: Java代码 1. FileReader fileReader = new FileReader(new File(streamReader.readLine())); 2. BufferedReader bufferedFileReader = new BufferedReader(fileReader); 3. String line = null; 4. while ((line = bufferedFileReader.readLine()) != null) { 5. streamWriter.println(line); 6. } [java] view plain copy print? 1. FileReader fileReader = new FileReader(new File(streamReader.readLine())); 2. BufferedReader bufferedFileReader = new BufferedReader(fileReader); 3. String line = null; 4. while ((line = bufferedFileReader.readLine()) != null) { 5. streamWriter.println(line); 6. } 这些代码值得详细解释。让我们一点一点来看: Java代码 1. FileReader fileReader = new FileReader(new File(streamReader.readLine())); [java] view plain copy print? 1. FileReader fileReader = new FileReader(new File(streamReader.readLine())); 首先,我们使用 Socket 的 InputStream 的 BufferedReader。我们应该获取一条有效的文件路径,所以我们用该路径名构造一个新 File。我们创建一个新FileReader 来处理读文件的操作。 Java代码 1. BufferedReader bufferedFileReader = new BufferedReader(fileReader); [java] view plain copy print? 1. BufferedReader bufferedFileReader = new BufferedReader(fileReader); 这里我们把 FileReader 包装进 BufferedReader 以使我们能够逐行地读该文件。 接着,我们调用 BufferedReader 的 readLine()。这个调用将造成阻塞直到有字节到来。我们获取一些字节之后就把它们放到本地的 line 变量中,然后再写出到客户机上。完成读写操作之后,我们就关闭打开的流。 请注意我们在完成从 Socket 的读操作之后关闭 streamWriter 和 streamReader。您或许会问我们为什么不在读取文件名之后立刻关闭 streamReader。原因是当您这样做时,您的客户机将不会获取任何数据。如果您在关闭 streamWriter 之前关闭 streamReader,则您可以往 Socket 写任何东西,但却没有任何数据能通过通道(通道被关闭了)。

23,404

社区成员

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

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