当前位置: 首页 > news >正文

Java 网络编程:Socket 与网络通信

1 引言

在古代,由于通信不便利,人们利用鸽子的飞行能力和方向辨识能力,驯化鸽子进行消息传递,即所谓的“飞鸽传书”。在现代计算机网络中,套接字(Socket)扮演了类似的角色。套接字是应用程序通过网络发送或接收数据的抽象层,允许应用程序将输入输出(I/O)操作应用于网络中,并与其他应用程序进行通信。

2 套接字(Socket)简介

套接字是 IP 地址与端口的组合,是网络通信的基础。通过套接字,应用程序可以像操作文件一样打开、读写和关闭网络连接。

3 网络调试工具:ping 与 telnet

在调试网络程序时,pingtelnet 是两个非常有用的工具。

  • ping:用于测试数据包能否通过 IP 协议到达特定主机。ping 会向目标主机发出一个 ICMP 请求回显数据包,并等待接收回显响应数据包。通过 ping 命令,可以检查网络连接是否正常。

    例如,我们 ping 一下百度。截图如下。
    在这里插入图片描述

  • telnet:用于远程登录到另一台计算机。通过 telnet,用户可以在本地计算机上登录到远程计算机,进行交互式操作。在 Windows 系统中,telnet 通常是默认安装但未激活的,可以通过控制面板启用。使用 telnet 时,远程计算机需要运行一个服务,该服务持续监听网络连接请求。当接收到客户端的连接请求时,服务器进程会被唤醒,并为两者建立连接,直到某一方中止连接。

  • 然而,由于 telnet 是明文传输协议,用户的所有内容(包括用户名和密码)都没有经过加密,因此在现代网络技术中,telnet 的安全性受到质疑,并不被广泛使用。
    例如,我们 telnet 一下火(shui)土(mu)社区。截图如下。
    在这里插入图片描述

4 Socket 实例:客户端

以下是一个简单的 Java 客户端套接字(Socket)示例,模拟 telnet 命令连接远程服务器并读取数据。

import java.io.InputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import java.io.IOException;public class SocketClientExample {public static void main(String[] args) {try (Socket socket = new Socket("bbs.newsmth.net", 23)) {InputStream is = socket.getInputStream();Scanner scanner = new Scanner(is, "gbk");while (scanner.hasNextLine()) {String line = scanner.nextLine();System.out.println(line);}} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}
}

4.1 代码解析:

  1. 建立套接字连接

    Socket socket = new Socket("bbs.newsmth.net", 23);
    
    • host 为主机名,port 为端口号(23 为默认的 telnet 端口号)。
    • 如果无法确定主机的 IP 地址,则抛出 UnknownHostException 异常;如果在创建套接字时发生 IO 错误,则抛出 IOException 异常。
    • 需要注意的是,套接字在建立的时候,如果远程主机不可访问,这段代码会阻塞很长时间,直到底层操作系统的限制而抛出异常。因此,通常会在套接字建立后设置一个超时时间:
      socket.setSoTimeout(10000); // 单位为毫秒
      
  2. 获取输入流并读取数据

    InputStream is = socket.getInputStream();
    Scanner scanner = new Scanner(is, "gbk");while (scanner.hasNextLine()) {String line = scanner.nextLine();System.out.println(line);
    }
    
    • 通过 java.net.Socket 类的 getInputStream() 方法获取输入流。

    • 使用 Scanner 类将输入流中的内容按行读取并打印出来。

    • 部分结果如下图所示(完整结果建议自己亲手实践一下):

      在这里插入图片描述

通过这种方式,我们可以模拟 telnet 命令的行为,直接与远程主机进行交互,体验网络通信的底层细节。

5 ServerSocket 实例:服务器端

以下是一个简单的 Java 服务器端套接字(ServerSocket)示例,模拟一个远程服务。

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;public class ServerSocketExample {public static void main(String[] args) {try (ServerSocket server = new ServerSocket(8888);Socket socket = server.accept();InputStream is = socket.getInputStream();OutputStream os = socket.getOutputStream();Scanner scanner = new Scanner(is)) {PrintWriter pw = new PrintWriter(new OutputStreamWriter(os, "gbk"), true);pw.println("你好啊,欢迎关注「沉默王二」公众号,回复关键字「2048」领取程序员进阶必读资料包");boolean done = false;while (!done && scanner.hasNextLine()) {String line = scanner.nextLine();System.out.println(line);if ("2048".equals(line)) {done = true;}}} catch (IOException e) {e.printStackTrace();}}
}

5.1 代码解析:

  1. 建立服务器端的套接字

    ServerSocket server = new ServerSocket(8888);
    
    • 创建一个 ServerSocket 对象,并指定端口号 8888。端口号 0~1023 通常被系统预留,因此我们选择了一个大于 1023 的端口号。
  2. 等待客户端套接字的连接请求

    Socket socket = server.accept();
    InputStream is = socket.getInputStream();
    OutputStream os = socket.getOutputStream();
    
    • 调用 ServerSocket 对象的 accept() 方法,等待客户端的连接请求。一旦有客户端连接,accept() 方法会返回一个 Socket 对象,表示连接已建立。
    • 通过 Socket 对象获取输入流和输出流,分别用于读取客户端发送的数据和向客户端发送数据。
  3. 向客户端发送消息

    PrintWriter pw = new PrintWriter(new OutputStreamWriter(os, "gbk"), true);
    pw.println("你好啊,欢迎关注「沉默王二」 公众号,回复关键字「2048」 领取程序员进阶必读资料包");
    
    • 使用 PrintWriter 将消息发送到客户端。PrintWriter 的构造函数中使用了 OutputStreamWriter 来指定字符编码为 gbk,并且第二个参数 true 表示自动刷新缓冲区。
  4. 读取客户端发送的消息

    Scanner scanner = new Scanner(is);
    boolean done = false;
    while (!done && scanner.hasNextLine()) {String line = scanner.nextLine();System.out.println(line);if ("2048".equals(line)) {done = true;}
    }
    
    • 使用 Scanner 读取客户端发送的每一行数据。
    • 当客户端发送字符串 "2048" 时,服务器端会中断连接,客户端会显示“遗失对主机的连接”。

5.2 运行服务并测试:

  1. 运行服务器端代码

    • 在命令行或 IDE 中运行上述 ServerSocketExample 类。
  2. 使用 telnet 连接服务器

    • 打开一个新的命令行窗口,输入以下命令连接到服务器:
      telnet localhost 8888
      
    • 连接成功后,你会看到服务器发送的消息:
      你好啊,欢迎关注「沉默王二」 公众号,回复关键字「2048」 领取程序员进阶必读资料包
      
    • telnet 窗口中输入 2048,服务器端会中断连接,telnet 窗口会显示“遗失对主机的连接”。
      在这里插入图片描述

通过这种方式,我们可以模拟一个简单的远程服务,并通过 telnet 进行测试和交互。

6 为多个客户端服务

在这里插入图片描述
。以下是优化后的代码示例:

服务器端代码(MultiThreadedServer.java)

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;public class MultiThreadedServer {public static void main(String[] args) throws IOException {int port = 12345;ServerSocket serverSocket = new ServerSocket(port);System.out.println("Server is listening on port " + port);while (true) {Socket socket = serverSocket.accept();System.out.println("Client connected");new ClientHandler(socket).start();}}
}class ClientHandler extends Thread {private Socket socket;public ClientHandler(Socket socket) {this.socket = socket;}public void run() {try {InputStream input = socket.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(input));OutputStream output = socket.getOutputStream();PrintWriter writer = new PrintWriter(output, true);String line;while ((line = reader.readLine()) != null) {System.out.println("Received: " + line);writer.println("Server: " + line);}socket.close();} catch (IOException e) {System.out.println("Client disconnected");}}
}

客户端代码(Client.java)

import java.io.*;
import java.net.Socket;public class Client {public static void main(String[] args) throws IOException {String hostname = "localhost";int port = 12345;Socket socket = new Socket(hostname, port);System.out.println("Connected to the server");InputStream input = socket.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(input));OutputStream output = socket.getOutputStream();PrintWriter writer = new PrintWriter(output, true);writer.println("Hello, server!");String response = reader.readLine();System.out.println("Server response: " + response);socket.close();}
}

6.1 代码解析:

  1. 服务器端代码

    • MultiThreadedServer 类创建一个 ServerSocket 并监听指定端口(12345)。
    • 使用 while (true) 循环不断接受客户端的连接请求。
    • 每当有新的客户端连接时,创建一个新的 ClientHandler 线程来处理该连接。
  2. ClientHandler 类

    • ClientHandler 类继承自 Thread 类,用于处理单个客户端的连接。
    • run() 方法中,处理客户端的输入输出流,并向客户端发送消息。
    • 当客户端断开连接时,捕获 IOException 并打印“Client disconnected”。
  3. 客户端代码

    • Client 类创建一个 Socket 并连接到服务器。
    • 通过输入输出流与服务器进行通信。
    • 向服务器发送消息并接收服务器的响应。

6.2 运行服务并测试:

  1. 运行服务器端代码

    • 在命令行或 IDE 中运行 MultiThreadedServer 类。
  2. 运行多个客户端

    • 在多个命令行窗口中分别运行 Client 类。
    • 每个客户端都会连接到服务器,并向服务器发送消息。
    • 服务器会接收客户端的消息,并返回响应。
      在这里插入图片描述

通过这种方式,服务器端可以同时为多个客户端提供服务,每个客户端连接由一个独立的线程处理。这使得服务器能够高效地处理并发连接,满足一对多的需求。

7 UDP 通信:DatagramSocket 实例

UDP 是一种无连接的传输协议,适用于对可靠性要求不高但对速度要求较高的场景。以下是一个简单的 UDP 通信示例。

服务器端代码(UDPServer.java)

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;public class UDPServer {public static void main(String[] args) throws IOException {int port = 12345;DatagramSocket serverSocket = new DatagramSocket(port);System.out.println("Server is listening on port " + port);byte[] buffer = new byte[1024];DatagramPacket packet = new DatagramPacket(buffer, buffer.length);serverSocket.receive(packet);String message = new String(packet.getData(), 0, packet.getLength());System.out.println("Received: " + message);serverSocket.close();}
}

客户端代码(UDPClient.java)

import java.io.IOException;
import java.net.*;public class UDPClient {public static void main(String[] args) throws IOException {String hostname = "localhost";int port = 12345;InetAddress address = InetAddress.getByName(hostname);DatagramSocket clientSocket = new DatagramSocket();String message = "Hello, server!";byte[] buffer = message.getBytes();DatagramPacket packet = new DatagramPacket(buffer, buffer.length, address, port);clientSocket.send(packet);System.out.println("Message sent");clientSocket.close();}
}

7.1 代码解析:

  1. 服务器端代码

    • UDPServer 类创建一个 DatagramSocket 并监听指定端口(12345)。
    • 创建一个 DatagramPacket 对象,用于存储接收到的数据包。
    • 使用 serverSocket.receive(packet) 方法阻塞,直到收到一个数据包。
    • 收到数据包后,从数据包中提取消息并打印。
    • 最后关闭 DatagramSocket
  2. 客户端代码

    • UDPClient 类解析服务器的 IP 地址。
    • 创建一个 DatagramSocket 对象。
    • 将要发送的消息转换为字节数组,并创建一个 DatagramPacket 对象,指定目标地址和端口。
    • 使用 clientSocket.send(packet) 方法发送数据包。
    • 最后关闭 DatagramSocket

7.2 运行结果:

  1. 运行服务器端代码

    • 在命令行或 IDE 中运行 UDPServer 类。
    • 服务器启动并监听端口 12345,输出:
      Server is listening on port 12345
      
  2. 运行客户端代码

    • 在命令行或 IDE 中运行 UDPClient 类。
    • 客户端发送消息到服务器,输出:
      Message sent
      
  3. 服务器端接收消息

    • 服务器接收到客户端发送的消息,输出:
      Received: Hello, server!
      

通过这个示例,我们可以看到如何使用 DatagramSocket 类实现基于 UDP 协议的通信。UDP 是一种无连接的协议,因此不需要建立连接,发送和接收数据包的速度通常比 TCP 更快,但可靠性较低。

8 总结

掌握 Java Socket 编程对于理解网络通信机制至关重要。通过编写简单的客户端和服务器端程序,可以更好地理解网络通信的基本原理和实现方式。无论是基于 TCP 的 Socket 和 ServerSocket,还是基于 UDP 的 DatagramSocket,都是网络编程中不可或缺的工具。通过实践,可以进一步提升网络编程技能,为开发更复杂的网络应用打下坚实基础。

9 思维导图

在这里插入图片描述

10 参考链接

Java Socket:飞鸽传书的网络套接字


http://www.mrgr.cn/news/75364.html

相关文章:

  • 搜维尔科技:Manus VR数据手套集成,遥操作五指灵巧手解决方案
  • 2分钟在阿里云ECS控制台部署个人应用(图文示例)
  • Java线程的sleep和wait的区别
  • Python自动化运维DevSecOps与安全自动化
  • STM32问题集
  • 【算法一周目】双指针(2)
  • 2.6 以太网扩展技术
  • 《向量数据库指南》——Mlivus Cloud:数据安全与合规性的守护者
  • 【月之暗面kimi-注册/登录安全分析报告】
  • Visual Studio 如何在终端窗口内嵌git bash
  • 光伏智能踏勘:让踏勘告别爬屋顶,开启光伏一点通新篇章
  • 社科基金资料汇总(选题、申请、撰写全流程的资料、经典范例和历年数据)1991-2022年
  • 充气膜场馆的保温效果如何?—轻空间
  • Python io.StringIO:高效的可变字符串处理工具
  • 深度学习-卷积神经网络CNN
  • 质数的来源-2
  • 会话信息处理: HttpSession、token序列化、收集登录设备信息、基于`spring-session-data-redis`实现session共享。
  • 数字信号处理Python示例(14)生成锯齿波和三角波
  • TypeORM在Node.js中的高级应用
  • ArkUI进阶-1
  • Java期末复习暨学校第七次上机课作业
  • 详细讲解 C 语言中的 #ifndef 和 #endif 语法
  • 【蓝牙协议栈】【BLE】【BAS】蓝牙电池服务
  • Tomcat(18) Tomcat默认端口
  • Linux实例内存未耗尽时触发 Out Of Memory
  • 从模型评估到绘制ROC曲线:用Python实现全面性能分析