JAVA套接字(Socket)101七天系列—第四天【一个简单示例】

一个简单示例
 1. 背景

我们将在本部分讨论的示例将阐明在 Java 代码中如何使用 SocketServerSocket。客户机用Socket 连接到服务器。服务器用
ServerSocket 在端口 3000 侦听。客户机请求服务器 C: 驱动器上的文件内容。

为清楚起见,我们把示例分解成客户机端和服务器端。最后我们将把它们组合起来以使您能看到整体模样。

我们在使用 JDK 1.2 的 IBM VisualAge for Java 3.5 上开发这些代码。要自己创建这个示例,您应有完好的 JDK 1.1.7 或更高版本。客户机和服务器将只在一台机器上运行,所以您不必担心是否有一个可用的网络。

 2. 创建 RemoteFileClient 类

这里是 RemoteFileClient 类的结构:

[java] view plaincopyprint?

  1. import java.io.*;
  2. import java.net.*;
  3. public class RemoteFileClient {
  4. protected String hostIp;
  5. protected int hostPort;
  6. protected BufferedReader socketReader;
  7. protected PrintWriter socketWriter;
  8. public RemoteFileClient(String aHostIp, int aHostPort) {
  9. hostIp = aHostIp;
  10. hostPort = aHostPort;
  11. }
  12. public static void main(String[] args) {
  13. }
  14. public void setUpConnection() {
  15. }
  16. public String getFile(String fileNameToGet) {
  17. }
  18. public void tearDownConnection() {
  19. }
  20. }

首先我们导入 java.netjava.iojava.net 包为您提供您需要的套接字工具。java.io
包为您提供对流进行读写的工具,这是您与 TCP 套接字通信的唯一途径。

我们给我们的类实例变量以支持对套接字流的读写和存储我们将连接到的远程主机的详细信息。

我们类的构造器有两个参数:远程主机的 IP 地址和端口号各一个,而且构造器将它们赋给实例变量。

我们的类有一个 main() 方法和三个其它方法。稍后我们将探究这些方法的细节。现在您只需知道setUpConnection() 将连接到远程服务器,getFile()
将向远程服务器请求fileNameToGet 的内容以及tearDownConnection() 将从远程服务器上断开。

 3. 实现 main()

这里我们实现 main() 方法,它将创建 RemoteFileClient 并用它来获取远程文件的内容,然后打印结果:

[java] view plaincopyprint?

  1. public static void main(String[] args) {
  2. RemoteFileClient remoteFileClient = new RemoteFileClient("127.0.0.1", 3000);
  3. remoteFileClient.setUpConnection();
  4. String fileContents =
  5. remoteFileClient.getFile("C:\\WINNT\\Temp\\RemoteFile.txt");
  6. remoteFileClient.tearDownConnection();
  7. System.out.println(fileContents);
  8. }

main() 方法用主机的 IP 地址和端口号实例化一个新RemoteFileClient(客户机)。然后,我们告诉客户机建立一个到主机的连接(稍后有更详细的讨论)。接着,我们告诉客户机获取主机上一个指定文件的内容。最后,我们告诉客户机断开它到主机的连接。我们把文件内容打印到控制台,只是为了证明一切都是按计划进行的。

4. 建立连接

这里我们实现 setUpConnection() 方法,它将创建我们的 Socket 并让我们访问该套接字的流:

[java] view plaincopyprint?

  1. public void setUpConnection() {
  2. try {
  3. Socket client = new Socket(hostIp, hostPort);
  4. socketReader = new BufferedReader(
  5. new InputStreamReader(client.getInputStream()));
  6. socketWriter = new PrintWriter(client.getOutputStream());
  7. } catch (UnknownHostException e) {
  8. System.out.println("Error setting up socket connection: unknown host at " + hostIp + ":" + hostPort);
  9. } catch (IOException e) {
  10. System.out.println("Error setting up socket connection: " + e);
  11. }
  12. }

setUpConnection() 方法用主机的 IP 地址和端口号创建一个Socket

[java] view plaincopyprint?

  1. Socket client = new Socket(hostIp, hostPort);

我们把 SocketInputStream 包装进BufferedReader 以使我们能够读取流的行。然后,我们把Socket
OutputStream 包装进
PrintWriter 以使我们能够发送文件请求到服务器:

[java] view plaincopyprint?

  1. socketReader = new BufferedReader(new InputStreamReader(client.getInputStream()));
  2. socketWriter = new PrintWriter(client.getOutputStream());

请记住我们的客户机和服务器只是来回传送字节。客户机和服务器都必须知道另一方即将发送的是什么以使它们能够作出适当的响应。在这个案例中,服务器知道我们将发送一条有效的文件路径。

当您实例化一个 Socket 时,将抛出UnknownHostException。这里我们不特别处理它,但我们打印一些信息到控制台以告诉我们发生了什么错误。同样地,当我们试图获取Socket
InputStream
OutputStream 时,如果抛出了一个一般IOException,我们也打印一些信息到控制台。这是本教程的一般做法。在产品代码中,我们应该做得更完善些。

 5. 与主机交谈

这里我们实现 getFile() 方法,它将告诉服务器我们想要什么文件并在服务器传回其内容时接收该内容。

[java] view plaincopyprint?

  1. public String getFile(String fileNameToGet) {
  2. StringBuffer fileLines = new StringBuffer();
  3. try {
  4. socketWriter.println(fileNameToGet);
  5. socketWriter.flush();
  6. String line = null;
  7. while ((line = socketReader.readLine()) != null)
  8. fileLines.append(line + "\n");
  9. } catch (IOException e) {
  10. System.out.println("Error reading from file: " + fileNameToGet);
  11. }
  12. return fileLines.toString();
  13. }

getFile() 方法的调用要求一个有效的文件路径String。它首先创建名为fileLines
StringBufferfileLines 用于存储我们读自服务器上的文件的每一行。

[java] view plaincopyprint?

  1. StringBuffer fileLines = new StringBuffer();

try{}catch{} 块中,我们用PrintWriter 把请求发送到主机,PrintWriter
是我们在创建连接期间建立的。

[java] view plaincopyprint?

  1. socketWriter.println(fileNameToGet);
  2. socketWriter.flush();

请注意这里我们是 flush()PrintWriter,而不是关闭它。这迫使数据被发送到服务器而不关闭Socket

一旦我们已经写到 Socket,我们就希望有一些响应。我们不得不在Socket
InputStream 上等待它,我们通过在while 循环中调用BufferedReader
上的readLine() 来达到这个目的。我们把每一个返回行附加到fileLines
StringBuffer(带有一个换行符以保护行):

[java] view plaincopyprint?

  1. String line = null;
  2. while ((line = socketReader.readLine()) != null)
  3. fileLines.append(line + "\n");

 6. 断开连接

这里我们实现 tearDownConnection() 方法,它将在我们使用完毕连接后负责“清除”:

[java] view plaincopyprint?

  1. public void tearDownConnection() {
  2. try {
  3. socketWriter.close();
  4. socketReader.close();
  5. } catch (IOException e) {
  6. System.out.println("Error tearing down socket connection: " + e);
  7. }
  8. }

tearDownConnection() 方法只是分别关闭我们在Socket
InputStream
OutputStream 上创建的BufferedReaderPrintWriter。这样做会关闭我们从Socket
获取的底层流,所以我们必须捕捉可能的IOException

 7. 总结一下客户机

我们的类研究完了。在我们继续往前讨论服务器端的情况之前,让我们回顾一下创建和使用 Socket 的步骤:

  1. 用您想连接的机器的 IP 地址和端口实例化 Socket(如有问题则抛出 Exception)。
  2. 获取 Socket 上的流以进行读写。
  3. 把流包装进 BufferedReader/PrintWriter 的实例,如果这样做能使事情更简单的话。
  4. Socket 进行读写。
  5. 关闭打开的流。

附:RemoteFileClient 的代码清单

[java] view plaincopyprint?

  1. <span style="font-size:12px;">import java.io.*;
  2. import java.net.*;
  3. public class RemoteFileClient {
  4. protected BufferedReader socketReader;
  5. protected PrintWriter socketWriter;
  6. protected String hostIp;
  7. protected int hostPort;
  8. public RemoteFileClient(String aHostIp, int aHostPort) {
  9. hostIp = aHostIp;
  10. hostPort = aHostPort;
  11. }
  12. public String getFile(String fileNameToGet) {
  13. StringBuffer fileLines = new StringBuffer();
  14. try {
  15. socketWriter.println(fileNameToGet);
  16. socketWriter.flush();
  17. String line = null;
  18. while ((line = socketReader.readLine()) != null)
  19. fileLines.append(line + "\n");
  20. } catch (IOException e) {
  21. System.out.println("Error reading from file: " + fileNameToGet);
  22. }
  23. return fileLines.toString();
  24. }
  25. public static void main(String[] args) {
  26. RemoteFileClient remoteFileClient = new RemoteFileClient("127.0.0.1", 3000);
  27. remoteFileClient.setUpConnection();
  28. String fileContents = remoteFileClient.getFile("C:\\WINNT\\Temp\\RemoteFile.txt");
  29. remoteFileClient.tearDownConnection();
  30. System.out.println(fileContents);
  31. }
  32. public void setUpConnection() {
  33. try {
  34. Socket client = new Socket(hostIp, hostPort);
  35. socketReader = new BufferedReader(new InputStreamReader(client.getInputStream()));
  36. socketWriter = new PrintWriter(client.getOutputStream());
  37. } catch (UnknownHostException e) {
  38. System.out.println("Error setting up socket connection: unknown host at " + hostIp + ":" + hostPort);
  39. } catch (IOException e) {
  40. System.out.println("Error setting up socket connection: " + e);
  41. }
  42. }
  43. public void tearDownConnection() {
  44. try {
  45. socketWriter.close();
  46. socketReader.close();
  47. } catch (IOException e) {
  48. System.out.println("Error tearing down socket connection: " + e);
  49. }
  50. }
  51. }
  52. </span>

 8. 创建 RemoteFileServer 类

这里是 RemoteFileServer 类的结构:

[java] view plaincopyprint?

  1. import java.io.*;
  2. import java.net.*;
  3. public class RemoteFileServer {
  4. protected int listenPort = 3000;
  5. public static void main(String[] args) {
  6. }
  7. public void acceptConnections() {
  8. }
  9. public void handleConnection(Socket incomingConnection) {
  10. }
  11. }

跟客户机中一样,我们首先导入 java.net
java.io。接着,我们给我们的类一个实例变量以保存端口,我们从该端口侦听进入的连接。缺省情况下,端口是 3000。

我们的类有一个 main() 方法和两个其它方法。稍后我们将探究这些方法的细节。现在您只需知道
acceptConnections() 将允许客户机连接到服务器以及
handleConnection() 与客户机
Socket 交互以将您所请求的文件的内容发送到客户机。

 9. 实现 main()

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

[java] view plaincopyprint?

  1. public static void main(String[] args) {
  2. RemoteFileServer server = new RemoteFileServer();
  3. server.acceptConnections();
  4. }

服务器端的 main() 方法甚至比客户机端的更简单。我们实例化一个新 RemoteFileServer,它将在缺省侦听端口上侦听进入的连接请求。然后我们调用
acceptConnections() 来告诉该 server 进行侦听。

 10. 接受连接

这里我们实现 acceptConnections() 方法,它将创建一个 ServerSocket 并等待连接请求:

[java] view plaincopyprint?

  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. }

acceptConnections() 用欲侦听的端口号来创建
ServerSocket。然后我们通过调用该
ServerSocket
accept() 来告诉它开始侦听。accept() 方法将造成阻塞直到来了一个连接请求。此时,accept()
返回一个新的 Socket,这个
Socket 绑定到服务器上一个随机指定的端口,返回的
Socket 被传递给
handleConnection()。请注意我们在一个无限循环中处理对连接的接受。这里不支持任何关机。

无论何时如果您创建了一个无法绑定到指定端口(可能是因为别的什么控制了该端口)的 ServerSocket,Java 代码都将抛出一个错误。所以这里我们必须捕捉可能的
BindException。就跟在客户机端上时一样,我们必须捕捉
IOException,当我们试图在
ServerSocket 上接受连接时,它就会被抛出。请注意,您可以通过用毫秒数调用
setSoTimeout() 来为
accept() 调用设置超时,以避免实际长时间的等待。调用
setSoTimeout() 将使
accept() 经过指定占用时间后抛出
IOException

 11. 处理连接

这里我们实现 handleConnection() 方法,它将用连接的流来接收输入和写输出:

[java] view plaincopyprint?

  1. public void handleConnection(Socket incomingConnection) {
  2. try {
  3. OutputStream outputToSocket = incomingConnection.getOutputStream();
  4. InputStream inputFromSocket = incomingConnection.getInputStream();
  5. BufferedReader streamReader =
  6. new BufferedReader(new InputStreamReader(inputFromSocket));
  7. FileReader fileReader = new FileReader(new File(streamReader.readLine()));
  8. BufferedReader bufferedFileReader = new BufferedReader(fileReader);
  9. PrintWriter streamWriter =
  10. new PrintWriter(incomingConnection.getOutputStream());
  11. String line = null;
  12. while ((line = bufferedFileReader.readLine()) != null) {
  13. streamWriter.println(line);
  14. }
  15. fileReader.close();
  16. streamWriter.close();
  17. streamReader.close();
  18. } catch (Exception e) {
  19. System.out.println("Error handling a client: " + e);
  20. }
  21. }

跟在客户机中一样,我们用 getOutputStream()
getInputStream() 来获取与我们刚创建的
Socket 相关联的流。跟在客户机端一样,我们把
InputStream 包装进
BufferedReader,把
OutputStream 包装进
PrintWriter。在服务器端上,我们需要添加一些代码,用来读取目标文件和把内容逐行发送到客户机。这里是重要的代码:

[java] view plaincopyprint?

  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 plaincopyprint?

  1. FileReader fileReader = new FileReader(new File(streamReader.readLine()));

首先,我们使用 Socket
InputStream
BufferedReader。我们应该获取一条有效的文件路径,所以我们用该路径名构造一个新
File。我们创建一个新
FileReader 来处理读文件的操作。

[java] view plaincopyprint?

  1. BufferedReader bufferedFileReader = new BufferedReader(fileReader);

这里我们把 FileReader 包装进
BufferedReader 以使我们能够逐行地读该文件。

接着,我们调用 BufferedReader
readLine()。这个调用将造成阻塞直到有字节到来。我们获取一些字节之后就把它们放到本地的
line 变量中,然后再写出到客户机上。完成读写操作之后,我们就关闭打开的流。

请注意我们在完成从 Socket 的读操作之后关闭
streamWriter
streamReader。您或许会问我们为什么不在读取文件名之后立刻关闭
streamReader。原因是当您这样做时,您的客户机将不会获取任何数据。如果您在关闭
streamWriter 之前关闭
streamReader,则您可以往
Socket 写任何东西,但却没有任何数据能通过通道(通道被关闭了)。

 12. 总结一下服务器

在我们接着讨论另一个更实际的示例之前,让我们回顾一下创建和使用 ServerSocket 的步骤:

  1. 用一个您想让它侦听传入客户机连接的端口来实例化一个 ServerSocket(如有问题则抛出 Exception)。
  2. 调用 ServerSocketaccept() 以在等待连接期间造成阻塞。
  3. 获取位于该底层 Socket 的流以进行读写操作。
  4. 按使事情简单化的原则包装流。
  5. Socket 进行读写。
  6. 关闭打开的流(并请记住,永远不要在关闭 Writer 之前关闭 Reader)。

附: RemoteFileServer 的完整的代码清单

[java] view plaincopyprint?

    1. import java.io.*;
    2. import java.net.*;
    3. public class RemoteFileServer {
    4. int listenPort;
    5. public RemoteFileServer(int aListenPort) {
    6. listenPort = aListenPort;
    7. }
    8. public void acceptConnections() {
    9. try {
    10. ServerSocket server = new ServerSocket(listenPort);
    11. Socket incomingConnection = null;
    12. while (true) {
    13. incomingConnection = server.accept();
    14. handleConnection(incomingConnection);
    15. }
    16. } catch (BindException e) {
    17. System.out.println("Unable to bind to port " + listenPort);
    18. } catch (IOException e) {
    19. System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort);
    20. }
    21. }
    22. public void handleConnection(Socket incomingConnection) {
    23. try {
    24. OutputStream outputToSocket = incomingConnection.getOutputStream();
    25. InputStream inputFromSocket = incomingConnection.getInputStream();
    26. BufferedReader streamReader = new BufferedReader(new InputStreamReader(inputFromSocket));
    27. FileReader fileReader = new FileReader(new File(streamReader.readLine()));
    28. BufferedReader bufferedFileReader = new BufferedReader(fileReader);
    29. PrintWriter streamWriter = new PrintWriter(incomingConnection.getOutputStream());
    30. String line = null;
    31. while ((line = bufferedFileReader.readLine()) != null) {
    32. streamWriter.println(line);
    33. }
    34. fileReader.close();
    35. streamWriter.close();
    36. streamReader.close();
    37. } catch (Exception e) {
    38. System.out.println("Error handling a client: " + e);
    39. }
    40. }
    41. public static void main(String[] args) {
    42. RemoteFileServer server = new RemoteFileServer(3000);
    43. server.acceptConnections();
    44. }
时间: 2024-10-07 05:30:01

JAVA套接字(Socket)101七天系列—第四天【一个简单示例】的相关文章

Java套接字Socket

这篇博客是本人学习<Java网络程序设计>书中第4章套接字的学习总结.初学者网友学习这篇Java套接字文章,如果难于理解文章前面理论部分,可以先运行后面的程序,边看运行后面的程序边理解前面的原理,这对初学者是最好的方法.所有源代码都在文章后面我的github链接代码中. --惠州学院 13网络工程 吴成兵 20160607 目录 1 目录 1 一 流套接字概述 二 服务器套接字ServerSocket 21 ServerSocket的工程过程 22 ServerSocket构造方法 23 Se

专题七.网络编程之套接字Socket、TCP和UDP通信实例

https://blog.csdn.net/Eastmount/article/details/48909861 找工作笔试面试考察Socket套接字.TCP\UDP区别比较多,所以这篇文章主要精简了<Python核心编程(第二版)>第16章内容.内容包括:服务器和客户端架构.套接字Socket.TCP\UDP通信实例和常见笔试考题. https://www.cnblogs.com/alex3714/articles/5227251.html 1.Socket语法及相关 2.SocketSer

网络---中断套接字Socket

1 package socketpack_2; 2 import java.awt.BorderLayout; 3 import java.awt.EventQueue; 4 import java.awt.event.ActionEvent; 5 import java.awt.event.ActionListener; 6 import java.io.IOException; 7 import java.io.OutputStream; 8 import java.io.PrintWrit

Java套接字编程实现群聊与私聊[原理版]

简介 运用Java套接字我们几乎可以完成一个网络聊天软件的小产品,本文不涉及UI部分,仅对原理部分用代码演示一下.一个可以多人聊天的小功能,在Linux系统上用telnet亲测可用. 服务器代码 package demo0811.demo3; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.

Java套接字

前言: 本文补充一下Java关于套接字方面的内容,因为其应用相对比较简单,所以下面介绍两个程序实例. --------------------------------------------------------------------------------------- 1.Socket对接过程 (注:这是之前在网上见过一张图片,大致过程还是比较清晰的) 分析: Socket对接之后的数据传送类似于文件的读写: 流套接字采用TCP传输数据流,一旦数据发送失败将重传,所以具有安全无损的特点.

Java套接字编程

实现了基于TCP的Java Socket编程实例代码:简单实现了服务器和客户端的套接字编程,并传递简单的字符串.(在服务器声明套接字时需要绑定服务器的端口,端口为临界资源只能一个套接字使用.服务器编程时调用accept()方法,服务器进入等待连接状态.) Java中TCP/IP服务端连接建立的源码实现(socket->bind->listen->accept): (1)在进行套接字编程的时候,首先我们要建立一个服务端,并在服务端建立套接字,为套接字绑定接口,然后监听客户端可能发来的连接建

Linux进程间通信 -- 数据报套接字 socket()、bind()、sendto()、recvfrom()、close()

前一篇文章,Linux进程间通信——使用流套接字介绍了一些有关socket(套接字)的一些基本内容,并讲解了流套接字的使用,这篇文章将会给大家讲讲,数据报套接字的使用. 一.简单回顾——什么是数据报套接字 socket,即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行.也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信.也因为这样,套接字明确地将客户端和服务器区分开来. 相对于流套接字,数据报套接字的

回写、套接字socket 、JVM映像、实际/抽象回话

1.什么是回写? 回写:更新多维数据集单元值.成员或成员属性值. 操作系统和平台上的应用程序在运行的时候需要往磁盘写入临时数据,但是在无盘环境下,没有硬盘作为操作系统和应用程序临时的交换数据空间,所以这个任务必须交给服务器来完成 计算机回写:“Write Back(回写),在回写状态下,数据只有在要被从高速缓存中清除时才写到磁盘上.随着主存读取的数据增加,回写需要开始从高速缓存中向磁盘上写数据,并把更新的数据写入高速缓存中.由于一个数据可能会被写入高速缓存中许多次,而没有进行磁盘存取,所以回写的

套接字Socket

熟知套接字和应用层       必须有一种方法让你对指定的计算机打开连接,登录上去,告诉它你需要什么文件,并且控制文件的传输.(如果你想到不同的应用,例如计算机邮件,某种类似的协议也是需要的.)这个是由“应用层协议”完成的.应用层协议位于 TCP/IP协议的顶部.也就是说,当他们需要发送一个消息,他们会把那个消息交给TCP层.TCP层确保它输送到另一端.因为TCP层和IP层处理了所有的网络方面的细节,应用层协议可以认为一个网络连接是一个简单的字节流,就像一个终端或一条电话线.       在探究