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

JavaEE初阶——中秋特辑:网络编程送祝福从 Socket 基础到 TCP/UDP 实战

在这里插入图片描述
在这里插入图片描述
—JAVAEE— ⬅(click)


月圆人团圆,用Java网络编程传递中秋祝福

🌕 又是一年中秋至,月圆人团圆。在这个充满温情的传统节日里,让我们用代码搭建沟通的桥梁,用网络编程传递中秋的祝福与思念。


一、中秋佳节与网络通信的浪漫邂逅

中秋的团圆 vs 网络的连接

中秋佳节,最重要的主题就是"团圆"。无论身在何方,人们都渴望与亲人团聚,共享天伦之乐。这正如网络编程中的客户端与服务端,虽然物理上相隔千里,但通过网络的纽带,彼此紧密相连。

发送请求
返回响应
客户端 Client
服务端 Server

中秋元素与网络概念的对应

中秋元素网络概念寓意
🌕 明月网络通道传递思念的媒介
🥮 月饼数据包承载温情的内容
🏠 家乡服务端温暖的港湾
✈️ 游子客户端远方的思念

二、UDP 数据报套接字编程

1. 协议简介

UDP 是无连接协议,数据传输前不需要建立连接,直接以「数据报」为单位发送。

通俗比喻:类似发短信,不需要先拨号建立连接,直接发送消息,但消息可能会丢失或顺序错乱。


2. 核心 API

2.1 DatagramSocket - UDP 套接字

用于发送和接收 UDP 数据报,相当于「通信的发射器 / 接收器」。

构造方法

方法签名说明
DatagramSocket()创建 UDP 套接字,绑定到本机随机端口(通常用于客户端,无需固定端口)。
DatagramSocket(int port)创建 UDP 套接字,绑定到本机指定端口(通常用于服务端,需固定端口供客户端连接)。

核心方法

方法签名说明
void send(DatagramPacket p)发送数据报(非阻塞,直接发送)。
void receive(DatagramPacket p)接收数据报(阻塞,无数据时会等待)。
void close()关闭套接字,释放资源。

2.2 DatagramPacket - UDP 数据报

用于封装 UDP 传输的数据,包含「数据字节数组、目标 IP、目标端口」等信息。

通俗比喻:类似短信,包含了短信内容 + 收件人手机号

构造方法

方法签名说明
DatagramPacket(byte[] buf, int length)用于接收数据:指定接收数据的字节数组 buf 和最大长度 length
DatagramPacket(byte[] buf, int length, SocketAddress address)用于发送数据:指定发送数据的字节数组 buf、长度 length,以及目标地址 address(包含 IP 和端口)。

核心方法

方法签名说明
InetAddress getAddress()获取数据报的源 IP(接收时)或目标 IP(发送时)。
int getPort()获取数据报的源端口(接收时)或目标端口(发送时)。
byte[] getData()获取数据报中的字节数组(传输的实际数据)。

2.3 InetSocketAddress - 目标地址

SocketAddress 的子类,用于封装「IP 地址 + 端口号」,作为 DatagramPacket 的目标地址参数。

  • 构造方法InetSocketAddress(InetAddress addr, int port)
  • 常用方式InetAddress.getByName(String ip) - 通过 IP 字符串(如 "127.0.0.1")获取 InetAddress 对象。
使用示例
// 创建目标地址:IP 为 127.0.0.1,端口为 8888
SocketAddress address = new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 8888
);// 将地址用于发送数据报
DatagramPacket packet = new DatagramPacket(data, data.length, address
);

三、TCP 流套接字编程

1. TCP 核心 API

TCP 通信需区分「服务端」和「客户端」,核心 API 为 ServerSocket(服务端套接字)和 Socket(客户端/服务端连接套接字)。

1.1 ServerSocket(TCP 服务端套接字)

仅用于服务端,负责「监听端口、接收客户端连接请求」,不直接传输数据。

构造方法说明
ServerSocket(int port)创建服务端套接字,绑定到指定端口(需固定)。
核心方法说明
Socket accept()阻塞等待客户端连接,连接成功后返回 Socket 对象(用于与该客户端通信)。
void close()关闭服务端套接字,释放端口。
1.2 Socket(TCP 连接套接字)

客户端和服务端均会使用:

  • 客户端:通过 Socket 发起连接并传输数据;
  • 服务端:通过 accept() 返回的 Socket 与对应客户端通信。
构造方法说明
Socket(String host, int port)客户端使用:创建套接字并与指定 IP(host)、端口(port)的服务端建立连接。
核心方法说明
InputStream getInputStream()获取套接字的输入流(用于读取对方发送的数据)。
OutputStream getOutputStream()获取套接字的输出流(用于向对方发送数据)。
InetAddress getInetAddress()获取对方的 IP 地址。
int getPort()获取对方的端口号。
void close()关闭套接字,释放连接(TCP 会进行「四次挥手」关闭连接)。

四、用Socket编程实现"简易回显服务器"

在这个中秋佳节,让我们用Java网络编程构建一个充满温情的祝福传递系统。

先让我们了解一下UDPTCP的区别

特点TCP(传输控制协议)UDP(用户数据报协议)
连接性有连接无连接
传输可靠性可靠传输不可靠传输
数据传输方式面向字节流,无边界,可分多次收发面向数据报,有边界,一次传输一个数据报
缓冲区有接收缓冲区和发送缓冲区有接收缓冲区,无发送缓冲区
传输数据大小大小不限大小受限,一次最多传输 64k

UDP版本

UDP就像中秋的流星,快速而直接。
在这里插入图片描述

UDP回显服务器
package Network.UDP;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;// 回显服务器
// 1. 从客户端读取到请求内容
// 2. 根据请求计算响应
// 3. 把响应返回客户端public class EchoServer {// 先创建socket对象private DatagramSocket socket;// 构造方法public EchoServer(int port) throws SocketException {socket = new DatagramSocket(port);}// 启动服务器,去完成主要的业务逻辑public void start() throws IOException {// 由于是服务器,所以需要一直运行,直到用户主动退出System.out.println("服务器启动!");while (true) {// 1. 读取请求并解析//  1) 创建一个空白的DatagramPacket对象,用于存储读取到的请求DatagramPacket requPacket = new DatagramPacket(new byte[4096], 4096);//  2) 通过receive读取网卡数据,如果没有读取到请求,那么receive会阻塞等待socket.receive(requPacket);  // 这里receive需要传入一个输出型参数,用于存储读取到的请求//  3) 从DatagramPacket中提取出请求内容,并解析成字符串String request = new String(requPacket.getData(), 0, requPacket.getLength()); // 取出有效数据的部分// 2. 根据请求计算响应String response = process(request);// 3. 把响应返回客户端//  1) 把响应构造回DatagramPacket对象DatagramPacket respPacket = new DatagramPacket(response.getBytes(), response.getBytes().length,requPacket.getSocketAddress()); // 第三个参数是为了拿到请求这个数据报对应的响应的目标ip和端口//  2) 通过send发送响应socket.send(respPacket);  // 由于UDP是无连接的,里面不保存目标地址和端口,所以发送响应时需要指定目标地址和端口//  3) 打印日志System.out.printf("[%s:%d] req: %s, resp: %s\n",requPacket.getSocketAddress().toString(),requPacket.getPort(),request,response);}}// 由于是回显服务器,所以响应内容和请求内容是一样的,这里直接返回请求内容即可public String process(String request) {return request;}public static void main(String[] args) throws IOException {EchoServer server = new EchoServer(9090);server.start();}
}
UDP回显客户端
package Network.UDP;import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
import java.io.IOException;// 回显客户端
// 1. 从控制台读取用户输入内容
// 2. 通过网络发送给服务器
// 3. 从服务器读取到响应
// 4. 把响应结果显示到控制台public class EchoClient {private  DatagramSocket socket;private String serverIp;private int serverPort;// 构造方法// 1. 初始化 socket// 2. 客户端的构造方法需要记录服务器的 IP 和 Port// 作为服务器, 发数据的时候, 就可以通过收到的请求, 直到是要发给谁.// 作为客户端,主动发起的一方,必须得事先知道服务器在哪里.(程序员手动指定的)// 且客户端在创建socket对象的时候,不需要指定端口号,操作系统会自动分配一个可用的端口号public EchoClient(String serverIp, int serverPort) throws IOException {this.serverIp = serverIp;this.serverPort = serverPort;socket = new DatagramSocket();}// 启动客户端public void start() throws IOException {Scanner scanner = new Scanner(System.in);System.out.println("客户端启动!");// System.out.println("动物字典翻译客户端启动!");while (true) {// 1. 从控制台读取用户输入内容System.out.println(">");// System.out.println("请输入动物英文>");String request = scanner.next();// 2. 构造成UDP请求包并发送给服务器DatagramPacket reqPacket = new DatagramPacket(request.getBytes(), request.getBytes().length, InetAddress.getByName(serverIp), serverPort);socket.send(reqPacket);// 3. 从服务器读取到响应DatagramPacket respPacket = new DatagramPacket(new byte[4096], 4096);socket.receive(respPacket);String response = new String(respPacket.getData(), 0, respPacket.getLength());// 4. 把响应结果显示到控制台 System.out.println(response);}}public static void main(String[] args) throws IOException {// EchoClient echoClient = new EchoClient("192.168.36.65", 9090);EchoClient echoClient = new EchoClient("127.0.0.1", 9090);echoClient.start();}
}

TCP版本

TCP就像中秋的明月,稳定而持久,适合传递长篇的思念之情。
在这里插入图片描述

TCP回显服务器
package Network.TCP;import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;public class EchoServer {// 这个属性表示服务器端的 socket 对象,用来监听客户端的连接请求private  ServerSocket serverSocket;// 构造方法public EchoServer(int port) throws IOException {// 服务器启动就会创建 ServerSocket 对象,绑定到指定端口// 这个类主要负责监听客户端的连接请求,建立连接serverSocket = new ServerSocket(port);}// 启动服务器public void start() throws IOException{System.out.println("TCP服务器启动!");// 创建一个线程池, 用来处理客户端连接ExecutorService threadPool = Executors.newCachedThreadPool();while (true) {// 由于TCP是有连接的,不能直接一上来就读写数据,需要先处理连接请求// 建立连接的过程,是在操作系统内核中完成了,不需要我们自己实现,我们只需要把系统建立好的连接拿上来就可以// 当有客户端连接时,服务器会创建一个新的 Socket 对象,用来和客户端通信Socket socket = serverSocket.accept();   // accept相当于接听电话,需要等待客户端连接,如果没有客户端连接,就会产生阻塞// 多线程版本// 每当收到一个客户端连接时,就创建一个新的线程来处理这个连接,实现并发// Thread thread = new Thread(() -> {//     // 这个地方的this和processConnection方法中的socket是不同的//     // 这个地方的this是指EchoServer对象, 而processConnection方法中的socket是指客户端的socket对象//     this.processConnection(socket);// });// thread.start();// 线程池版本// 每当收到一个客户端连接时,就把这个连接放到线程池中, 线程池会自动分配一个线程来处理这个连接threadPool.submit(() -> {processConnection(socket);//此处其实还有问题// 假设现在有非常多的客户端,这些客户端连接之后,不会立刻销毁,而是会存在一定的时间~~// 此时如果客户端很多,并且又不快速销毁,就会短时间内出现大量的线程对于操作系统来说,线程的数目也不是无限的~~// 解决办法:IO多路复用// 即一个线程可以同时监听多个socket对象, 当有socket对象就绪时, 就会触发事件, 我们就可以处理这个事件// 这样就可以避免创建大量的线程, 减少了系统资源的消耗});}}// 处理一个客户端/一个连接逻辑private void processConnection(Socket socket){System.out.printf("[%s:%d] 客户端上线!\n", socket.getInetAddress().toString(), socket.getPort());try(InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()){// 以下实现通信代码// 由于一个客户端可能会与服务器有多轮的请求响应交互,// 所以我们需要在一个循环中, 不断地读取客户端的请求, 并发送响应// 直到客户端主动关闭连接// 由于这俩对象自身不持有文件描述符, 所以不需要在 finally 中关闭Scanner scanner = new Scanner(inputStream);  // 这个写法表示从socket的输入流中读取数据,而不是从控制台读取PrintWriter writer = new PrintWriter(outputStream);  // 这个写法表示把socket的输出流包装成一个打印流,方便我们写入字符串while (true) {// 针对客户端逻辑下线进行处理,如果客户端断开连接了(比如客户端结束了)// 此时的hasNext()方法会返回false,if(!scanner.hasNext()) {System.out.printf("[%s:%d] 客户端下线!\n", socket.getInetAddress().toString(), socket.getPort());break;}// 没有执行到这个打印,说明上面的hasNext()方法没有解除阻塞,大概率说明客户端没有发来数据System.out.println("服务器收到数据了!");// 1. 读取请求并解析,这个地方有个更简单的办法String request = scanner.next();// 2. 根据请求计算响应String response = process(request);// 3. 把响应发送给客户端writer.println(response);writer.flush();  // 刷新缓冲区, 确保响应及时发送给客户端 System.out.printf("[%s:%d] req: %s, resp: %s\n", socket.getInetAddress().toString(), socket.getPort(), request, response);}}catch(IOException e){e.printStackTrace();}finally{// 因为会不断进行频繁连接, 所以需要在 finally 中释放资源// 释放资源try{socket.close();}catch(IOException e){e.printStackTrace();}}}private String process(String request) {return request;}public static void main(String[] args) throws IOException {EchoServer server = new EchoServer(9090);server.start();}
}
TCP回显客户端
package Network.TCP;import java.net.Socket;
import java.util.Scanner;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;public class EchoClient {private Socket socket;// 构造方法, 用于初始化客户端的 socket 对象public EchoClient(String serverIP, int serverPort) throws IOException {// 1. 创建一个 socket 对象, 并指定服务器的 IP 和 Port// 在new这个对象的时候,就会涉及到"建立连接操作"// 由于连接建立好了之后,服务器的信息就在系统中被TCP协议记录下来了,所以我们在应用层就不需要再指定服务器的 IP 和 Portsocket = new Socket(serverIP, serverPort);}public void start() throws IOException {System.out.println("TCP客户端启动!");Scanner scanner = new Scanner(System.in);  // 这个Scanner 是用来读取用户从控制台输入的内容的try(InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()){Scanner scannerNet = new Scanner(inputStream);  // 这个Scanner 是用来读取服务器返回的响应的PrintWriter writer = new PrintWriter(outputStream);while (true) {// 1.先从控制台读取内容System.out.print("> ");String request = scanner.next();// 2.构造请求发送给服务器writer.println(request);  // 此处 println 是执行到了,但是 println 只是把数据先写到缓冲区(内存)中,没有真正的写入网卡,也就没有真正发送writer.flush();  // 手动刷新缓冲区, 确保数据被发送出去// 3.读取服务器的响应if (!scannerNet.hasNextLine()) {System.out.println("服务器返回的响应为空!");break;}String response = scannerNet.next();// 4.把响应显示在控制台上System.out.println(response);}}catch(IOException e){e.printStackTrace();}}public static void main(String[] args) throws IOException {EchoClient echoClient = new EchoClient("127.0.0.1", 9090);echoClient.start();}
}

五、网络编程核心概念与中秋寓意

1. 客户端与服务端:游子与家乡

// 游子就像客户端,主动发起连接
Socket socket = new Socket("家乡的IP", 8080);// 家乡就像服务端,永远等待游子归来
ServerSocket serverSocket = new ServerSocket(8080);

2. 请求与响应:思念与回复

就像中秋时节:

  • 请求:游子发送"我想家了"
  • 响应:家乡回复"月亮圆了,该回来了"

3. 长短连接:短暂的问候与持续的牵挂

连接类型中秋寓意代码表现
短连接节日的短暂问候每次发送祝福后关闭连接
长连接持续的亲情牵挂保持连接,持续对话

六、特色功能

基于UDP实现简易英汉字典模拟器

/*** 模拟中秋月光强度,根据日期计算月亮圆度*/
package Network.UDP;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;// 动物字典翻译服务器public class DictServer extends EchoServer {private Map<String, String> dict = new HashMap<>();// 构造方法public DictServer(int port) throws IOException {super(port);dict.put("cat", "猫");dict.put("dog", "狗");dict.put("fish", "鱼");dict.put("bird", "鸟");dict.put("mouse", "老鼠");dict.put("snake", "蛇");dict.put("elephant", "大象");dict.put("monkey", "猴子");dict.put("rabbit", "兔子");dict.put("tiger", "老虎");dict.put("lion", "狮子");dict.put("giraffe", "长颈鹿");dict.put("horse", "马");dict.put("zebra", "斑马");dict.put("penguin", "企鹅");dict.put("koala", "考拉");dict.put("panda", "熊猫");dict.put("chicken", "鸡");dict.put("duck", "鸭");dict.put("goat", "山羊");dict.put("cow", "奶牛");dict.put("sheep", "羊");}@Overridepublic String process(String request) {// 方法重写,本质就是对父类的功能进行拓展// 如,本来没有翻译功能,现在需要添加翻译功能return dict.getOrDefault(request, "没有找到该单词的翻译");}public static void main(String[] args) throws IOException {DictServer server = new DictServer(9090);server.start();}}

中秋祝福语生成器

/*** 智能生成中秋祝福语*/
public class BlessingGenerator {private static String[] templates = {"愿明月带去我对{name}的{emotion},祝您{wish}!","{name},中秋快乐!愿您如明月般{wish}!","月圆人团圆,祝{name}{wish}!"};private static String[] emotions = {"思念", "祝福", "牵挂", "问候"};private static String[] wishes = {"身体健康", "事业有成", "家庭幸福", "万事如意"};public static String generateBlessing(String name) {Random random = new Random();String template = templates[random.nextInt(templates.length)];return template.replace("{name}", name).replace("{emotion}", emotions[random.nextInt(emotions.length)]).replace("{wish}", wishes[random.nextInt(wishes.length)]);}
}

七、技术总结与中秋感悟

技术要点

  • ✅ Socket编程基础(UDP/TCP)
  • ✅ 多线程并发处理
  • ✅ 网络通信协议理解
  • ✅ 异常处理机制

🎑 中秋寄语

月圆人团圆,代码传思念。
愿这轮明月,照亮每个程序员的归家路;
愿这段代码,传递每份游子的思乡情。
祝大家中秋快乐,阖家幸福!

在这里插入图片描述

http://www.dtcms.com/a/449127.html

相关文章:

  • 多模卫星导航定位与应用-原理与实践(RTKLib)3
  • 数字婵娟:一部关于中秋节的计算主义宣言
  • ED2K技术
  • 【数据结构】顺序表0基础知识讲解 + 实战演练
  • GPU即服务:Linux与云原生如何联手开启AI算力“自来水“时代
  • 【数据结构】算法复杂度
  • 校园网门户网站建设招聘网站如何做
  • 深度学习(十六):数据归一化处理
  • 力扣70.爬楼梯
  • 【深度学习计算机视觉】10:转置卷积
  • 电子商务网站策划素材网站 模板
  • Coze源码分析-资源库-编辑知识库-后端源码-安全/错误处理机制
  • 【无标题】标签单击事件
  • GAMES101:现代计算机图形学入门(Chapter5 光栅化1(三角形遍历))迅猛式学习笔记(附Homework 0)
  • 【Linux操作系统】进程概念
  • 【Linux】Linux进程信号(上)
  • 海思SS528/22AP30开发笔记之环境搭建和SDK编译
  • 算法二分法详解
  • 信号 | 基本描述 / 分类 / 运算
  • 【环境配置 升级gcc】RK3588 Ubuntu20.04 gcc9升级为gcc10
  • 资产信息收集与指纹识别:HTTPX联动工具实战指南
  • 鼠标消息超时处理——实现图形界面自动操作,避免鼠标消息阻塞
  • 用AI帮忙,开发刷题小程序:微信小程序在线答题系统架构解析
  • 用AI帮忙,开发刷题小程序:从零开始,构建微信小程序答题系统
  • 简单一点的网站建设个人网页设计页眉
  • 生成式人工智能赋能高中物理教学:范式转型、实践路径与效果评估
  • ✅XXL-JOB的基本使用
  • Windows+Docker+AI开发板打造智能终端助手
  • Linux如何修改主机名?
  • 虹桥做网站竞价推广平台