在 Java 里,实现网络连接主要有两种常见的方式,分别是使用 java.net 包中的 Socket 和 ServerSocket 进行 TCP 连接,以及使用 DatagramSocket 和 DatagramPacket 进行 UDP 连接。
TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议,Java 提供了 Socket 和 ServerSocket 类来实现 TCP 连接。
下面是一个简单的 TCP 连接网络通信:
package com.hxstrive.java_io.network; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; /** * TCP网路连接简单示例 * @author hxstrive.com */ public class NetworkDemo { public static void main(String[] args) { // 服务端 new Thread(() -> { try (Socket socket = new Socket("localhost", 9999); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in))) { // 用户输入数据 String userInput; while ((userInput = stdIn.readLine()) != null) { out.println(userInput); System.out.println("服务器响应:" + in.readLine()); } } catch (IOException e) { e.printStackTrace(); } }).start(); // 客户端 new Thread(() -> { try (ServerSocket serverSocket = new ServerSocket(9999)) { System.out.println("服务器已启动,等待客户端连接..."); try (Socket socket = serverSocket.accept()) { System.out.println("客户端已连接:" + socket.getInetAddress()); // 获取输入流 BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 获取输出流 PrintWriter out = new PrintWriter(socket.getOutputStream(), true); // 接收用户的输入 String inputLine; while ((inputLine = in.readLine()) != null) { System.out.println("客户端消息:" + inputLine); out.println("服务器已收到消息:" + inputLine); } } } catch (IOException e) { e.printStackTrace(); } }).start(); } }
运行结果如下:
服务器已启动,等待客户端连接... 客户端已连接:/127.0.0.1 hello world 客户端消息:hello world 服务器响应:服务器已收到消息:hello world
上述代码中,
服务端:
运用 ServerSocket 监听 9999 端口。
借助 accept() 方法等待客户端连接,一旦有连接就返回一个 Socket 对象。
最后,通过 getInputStream() 和 getOutputStream() 方法获取输入输出流,进而与客户端进行通信。
客户端:
利用 Socket 连接到服务器的指定地址和端口,即 9999 端口。
同样通过 getInputStream() 和 getOutputStream() 方法获取输入输出流,以此和服务器通信。
注意,当服务端和客户端成功建立连接后,服务端和客户端均会获取到一个 InputStream 和 OutputStream 对象,如下图:
其中,InputStream 表示输入,OutputStream 表示输出。
由于网络连接是数据的常见来源或目的地,这使得你可以使用 Java IO API 通过网络连接进行通信。一旦两个进程之间建立了网络连接,它们就会像使用文件一样通过网络连接进行通信:
使用 InputStream 读取数据,使用 OutputStream 写入数据。换句话说,Java Networking API 用于在进程间建立网络连接,而 Java IO 则用于在建立连接后在进程间交换数据。
这基本上意味着,如果你的代码能够将内容写入文件,那么同样的内容也可以轻松写入网络连接。所需要的只是,你的写入组件依赖于 OutputStream 而不是 FileOutputStream。由于 FileOutputStream 是 OutputStream 的子类,这应该不成问题。
实际上,从文件中读取数据也是如此。能从文件中读取数据的组件也能轻松地从网络连接中读取同样的数据。只要确保你的读取组件依赖于 InputStream 来读取数据,而不是 FileInputStream。
下面是一个例子:
public class MyClass { public static void main(String[] args) { // 从文件读取数据 InputStream inputStream = new FileInputStream("c:\\myfile.txt"); process(inputStream); } // 这里不仅仅可以从文件读取,也能从网络读取数据 public static void process(InputStream input) throws IOException { // 对 InputStream 进行一些处理 } }
在这个示例中,process() 方法无法识别作为参数的 InputStream 是来自文件系统还是网络(示例只显示了文件系统版本)。因此,process() 方法只能使用 InputStream 作为参数,如下面示例:
package com.hxstrive.java_io.network; import java.io.*; import java.net.ServerSocket; import java.net.Socket; /** * 网络IO示例 * @author hxstrive.com */ public class NetWorkDemo1 { public static void main(String[] args) throws Exception { // 写入数据到文件 write(new FileOutputStream("E:\\test.txt"), "Hello World!"); // 从文件读取数据 process(new FileInputStream("E:\\test.txt")); } // 这个方法的 input 来源可以是 FileInputStream, Socket.getInputStream() 等 public static void process(InputStream input) throws IOException { // 对 InputStream 进行一些处理 try (BufferedReader in = new BufferedReader(new InputStreamReader(input))) { String inputLine; while ((inputLine = in.readLine())!= null) { System.out.println("接收来自客户端的数据: " + inputLine); } } catch (IOException e) { e.printStackTrace(); } } // 将信息 message 写入到 OutputStream 中,output 可以来源于 FileOutputStream, Socket.getOutputStream() 等 public static void write(OutputStream output, String message) throws IOException { try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(output))) { out.write(message); out.flush(); } catch (IOException e) { e.printStackTrace(); } } }
改进上述 NetWorkDemo1 示例,改为从网络读取数据,如下:
package com.hxstrive.java_io.network; import java.io.*; import java.net.ServerSocket; import java.net.Socket; /** * 网络IO示例 * @author hxstrive.com */ public class NetWorkDemo2 { public static void main(String[] args) { server(); client(); } // 这个方法的 input 来源可以是 FileInputStream, Socket.getInputStream() 等 public static void process(InputStream input) throws IOException { // 对 InputStream 进行一些处理 try (BufferedReader in = new BufferedReader(new InputStreamReader(input))) { String inputLine; while ((inputLine = in.readLine())!= null) { System.out.println("接收来自客户端的数据: " + inputLine); } } catch (IOException e) { e.printStackTrace(); } } // 将信息 message 写入到 OutputStream 中,output 可以来源于 FileOutputStream, Socket.getOutputStream() 等 public static void write(OutputStream output, String message) throws IOException { try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(output))) { out.write(message); out.flush(); } catch (IOException e) { e.printStackTrace(); } } // 服务端 public static void server() { new Thread(() -> { try (ServerSocket serverSocket = new ServerSocket(9999)) { System.out.println("服务端监听 8888 端口"); // 接收数据 while(true) { // 等待客户端连接 Socket clientSocket = serverSocket.accept(); System.out.println("客户端已连接"); // 看这里,重点 process(clientSocket.getInputStream()); } } catch (Exception e) { e.printStackTrace(); } }).start(); } // 客户端 public static void client() { new Thread(() -> { try (Socket socket = new Socket("localhost", 9999)) { try (OutputStream out = socket.getOutputStream()) { write(out, "Hello World"); // 发送数据 System.out.println("发送数据给服务器: Hello World"); } catch (IOException e) { e.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); } }).start(); } }
运行程序,输出结果:
服务端监听 8888 端口 客户端已连接 发送数据给服务器: Hello World 接收来自客户端的数据: Hello World