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

Java网络编程入门:从基础原理到实践(二)

目录

1. 网络编程基础:搞懂设备通信的底层逻辑

1.1 为啥需要网络编程?—— 让设备 “互通有无”

1.2 什么是网络编程?—— 给数据 “定规矩、找路线”

1.3 网络编程的基本概念:理清通信里的角色和流程

1.3.1 发送端和接收端 —— 数据的 “发信人” 和 “收信人”

1.3.2 请求和响应 —— 通信的 “一问一答” 

1.3.3 客户端和服务端 —— 稳定的 “需求方” 和 “服务方” 

1.3.4 常见的客户端服务端模型

2. Socket 套接字:网络通信的 “连接器” 

2.1 概念:网络通信的 “电话”

2.2 分类:两种通信 “风格

2.2.1 数据报套接字

2.2.2 流套接字

2.3 Java数据报套接字通信模型

2.4 Java流套接字通信模型

2.5 Socket 编程注意事项:避坑要点 

3. UDP 数据报套接字编程:简单高效的 “快传” 实践 

3.1 API 介绍:核心工具

3.1.1 DatagramSocket

3.1.2 DatagramPacket 

3.1.3 InetSocketAddress

3.2 代码示例:UDP 通信全流程 

3.2.1 UDP Echo Server

3.2.2 UDP Echo Client

3.2.3 3. UDP Dict Serve 字典服务器

4. TCP 流套接字编程:可靠传输的 “保障”

4.1 API 介绍:构建可靠连接

4.1.1 ServerSocket

4.1.2 Socket

​编辑4.2 代码示例:TCP 通信实践

4.2.1 TCP Echo Server

4.2.2 TCP Echo Client

5. 长短连接:按需选择通信模式

5.1 短连接与长连接的定义

5.2 短连接与长连接的核心区别

5.2.1 连接建立与关闭的耗时差异

5.2.2 主动请求的发起方差异

5.2.3 适用场景差异

5.3 长连接的 “扩展痛点” 与优化方向

5.3.1 BIO 长连接的资源瓶颈

5.3.2 NIO 优化长连接的思路

5.4 总结:按需选型,平衡效率与资源


        网络编程听着高深,其实就是解决 “设备之间咋传数据” 的问题。想象一下,你手机刷短视频,本质是手机(客户端)和短视频服务器(服务端)在传数据;玩联机游戏,是你电脑和游戏服务器、队友设备在传数据。这篇就把网络编程最基础的逻辑和核心工具 Socket,掰开揉碎了讲,保证像唠家常一样好懂 。

1. 网络编程基础:搞懂设备通信的底层逻辑

1.1 为啥需要网络编程?—— 让设备 “互通有无”

        用手机点外卖,手机得把 “我要点宫保鸡丁” 的需求发给外卖平台服务器;玩联机游戏,你操控角色的动作得传给队友的设备;智能手表测的心率数据,得传到手机 App 上显示……网络编程就是让这些 “需求、动作、数据”,能在不同设备(或同一设备的不同程序)之间准确、高效传递的技术

简单说:没有网络编程,所有跨设备的功能全废!手机只能当计算器,电脑连不上网页,智能设备数据也传不出去,所有的网络资源都无法访问,世界直接 “断网瘫痪” 。

  • 所谓的网络资源,其实就是在网络中可以获取的各种数据资源。
  • 而所有的网络资源,都是通过网络编程来进行数据传输的。

1.2 什么是网络编程?—— 给数据 “定规矩、找路线”

        网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)。

        当然,我们只要满足进程不同就行;所以即便是同一个主机,只要是不同进程,基于网络来传输数据,也属于网络编程。 

        特殊的,对于开发来说,在条件有限的情况下,一般也都是在一个主机中运行多个进程来完成网络编程。

        但是,我们一定要明确,我们的目的是提供网络上不同主机,基于网络来传输数据资源:

  • 进程A:编程来获取网络资源
  • 进程B:编程来提供网络资源

        网络编程的核心,就是控制程序按照 “网络协议”(比如 TCP、UDP),把数据打包、发送、接收、解析。举个例子:你用微信发消息 “吃了吗”,手机里的微信程序会:

  1. 打包:把文字转成符合网络协议的 “数据包”(类似把信装进信封,写上收件人地址);
  2. 发送:通过网络(WiFi、基站)把数据包传输出去(类似快递小哥把信运到目的地);
  3. 接收:对方的微信程序收到数据包(类似收件人拿到信);
  4. 解析:把数据包还原成 “吃了吗” 的文字(类似拆信封读内容)。

        整个过程,就是网络编程在 “暗中操作”,让数据能跨设备 “跑来跑去”。

1.3 网络编程的基本概念:理清通信里的角色和流程

        这些概念看着抽象,对应生活场景就秒懂了,一个个看:

1.3.1 发送端和接收端 —— 数据的 “发信人” 和 “收信人”

  • 发送端:数据的发送方进程,称为发送端,它是主动发数据的一方。比如你给朋友发微信,你手机就是发送端;游戏里你开枪,你的设备就是发送端(把 “开枪动作” 发出去)。发送端主机即网络通信的源主机。
  • 接收端:数据的接收方进程,称为接收端,它是被动收数据的一方。朋友的手机收你的微信、队友的设备收你 “开枪动作”,它们就是接收端。接收端主机即网络通信中的目的主机。
  • 收发端:发送端和接收端两端,也简称为收发端

注意:发送端和接收端只是相对的,角色会切换!只是一次网络数据传输产生数据流向后的概念。比如你和朋友互相发消息,你们的手机会轮流当 “发送端” 和 “接收端”,像打乒乓球一样来回传数据。

1.3.2 请求和响应 —— 通信的 “一问一答” 

        一般来说,获取一个网络资源,涉及到两次网络数据传输:

                •  第一次:请求数据的发送。

                •  第二次:响应数据的发送。

请求(Request):发送端主动提的 “需求”。比如你打开抖音,抖音 App 会给服务器发 “请求”:“给我推荐点搞笑视频”;你登录微信,微信 App 会发 “请求”:“验证这个账号密码对不对”。

响应(Response):接收端针对请求的 “回复”。服务器收到抖音的请求,回复 “这是搞笑视频列表”;收到微信登录请求,回复 “密码正确,登录成功”(或 “密码错误,失败” )

生活 analogy:你去餐厅(客户端)喊 “来份牛肉面”(请求),服务员(服务端)回复 “好的,马上做”(响应),就是典型的 “请求 - 响应”。

1.3.3 客户端和服务端 —— 稳定的 “需求方” 和 “服务方” 

  • 客户端(Client):主动找别人获取服务的程序 / 设备。手机 App(抖音、微信)、电脑上的浏览器(Chrome、Edge)、智能手表的 App,都是客户端。特点是 “按需连接”:需要服务时才主动连服务器,不用服务时就 “躺平”。
  • 服务端(Server):长期在线、专门给别人提供服务的程序 / 设备。抖音的后台服务器、微信的认证服务器、游戏的对战服务器,都是服务端。特点是 “7×24 小时待命”:不管有没有客户端找它,它都开着提供服务,随时准备响应请求。

再举个生活例子:你用美团 App(客户端)点外卖,美团的服务器(服务端)负责接收订单、分配骑手,就是 “客户端 - 服务端” 的典型交互。

1.3.4 常见的客户端服务端模型

最常见的场景,客户端是指给用户使用的程序,服务端是提供用户服务的程序:

  1. 客户端先发送请求到服务端
  2. 服务端根据请求数据,执行相应的业务处理
  3. 服务端返回响应:发送业务处理结果
  4. 客户端根据响应数据,展示处理结果(展示获取的资源,或提示保存资源的处理结果)

  • C/S 模型(客户端 / 服务端 ):需安装专门客户端软件(如微信 App )。优点是功能定制强,能利用本地资源;缺点是客户端需手动更新 。
  • B/S 模型(浏览器 / 服务端 ):通过浏览器访问(如知乎网页版 )。优点是使用方便、跨设备易访问;缺点是受浏览器功能限制,复杂交互体验弱于 C/S 。

2. Socket 套接字:网络通信的 “连接器” 

2.1 概念:网络通信的 “电话”

        Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元,是程序实现网络通信的基础载体。基于Socket套接字的网络程序开发就是网络编程。把它想象成 “网络电话”。程序通过 Socket 建立与其他设备的连接,在连接上发送和接收数据,就像通过电话线路实现双方通话。无论是 UDP 还是 TCP 通信,都得依靠 Socket 搭建数据传输的通道 。

2.2 分类:两种通信 “风格

2.2.1 数据报套接字

        使用传输层UDP协议,UDP,即User Datagram Protocol(用户数据报协议),传输层协议。类似 “发短信”,发送端把数据打包成 “数据报” 直接发,不管接收端状态。

优点:传输速度快,无需建立连接开销;

缺点:可能丢数据、数据无序。适合实时性要求高、容忍少量丢包的场景(如在线视频直播、游戏实时位置同步 )

  • 无连接
  • 不可靠传输
  • 面向数据报
  • 有接收缓冲区,无发送缓冲区
  • 大小受限:一次最多传输64k

        对于数据报来说,可以简单的理解为,传输数据是一块一块的,发送一块数据假如100个字节,必须一次发送,接收也必须一次接收100个字节,而不能分100次,每次接收1个字节。

2.2.2 流套接字

        使用传输层TCP协议,TCP,即Transmission Control Protocol(传输控制协议),传输层协议。如同 “打电话”,通信前需三次握手建立连接,保证数据可靠、有序传输,传输完四次挥手断开连接

优点:数据传输可靠;

缺点:建立 / 断开连接有额外耗时,速度稍慢。适合文件传输(需完整数据 )、登录验证(关键数据不能丢 )等场景 。

  • 有连接
  • 可靠传输
  • 面向字节流
  • 有接收缓冲区,也有发送缓冲区
  • 大小不限

        对于字节流来说,可以简单的理解为,传输数据是基于IO流,流式数据的特征就是在IO流没有关闭的情况下,是无边界的数据,可以多次发送,也可以分开多次接收。

2.3 Java数据报套接字通信模型

        对于UDP协议来说,具有无连接,面向数据报的特征,即每次都是没有建立连接,并且一次发送全部数据报,一次接收全部的数据报。

        java中使用UDP协议通信。主要基于 DatagramSocket(收发器 )和 DatagramPacket(数据报载体 )实现。发送时,把数据转字节数组,构建 DatagramPacket 并指定目标地址 / 端口,通过 DatagramSocket 发送;接收时,创建空 DatagramPacket 当缓冲区,用 DatagramSocket 接收,再解析数据 。对于一次发送及接收UDP数据报的流程如下:

        以上只是一次发送端的UDP数据报发送,及接收端的数据报接收,并没有返回的数据。也就是只有请求,没有响应。对于一个服务端来说,重要的是提供多个客户端的请求处理及响应,流程如下: 

2.4 Java流套接字通信模型

        java中使用TCP通信协议,主要依赖 ServerSocket(服务端 “大门”,监听客户端连接 )和 Socket(客户端 / 服务端连接后的数据通道 )。服务端用 ServerSocket 绑定端口监听,如 ServerSocket serverSocket = new ServerSocket(9999); ;客户端用 Socket 连服务端,如 Socket clientSocket = new Socket("127.0.0.1", 9999); 。连接建立后,通过 Socket 的输入输出流(像水管 )收发数据,保证数据有序、可靠传输 。

2.5 Socket 编程注意事项:避坑要点 

在 Socket 编程开发中,这些关键问题得留意,避免踩坑:

  1. 客户端与服务端部署场景
    开发调试时,常在同一主机开两个进程模拟客户端、服务端,但真实环境里,客户端和服务端一般分属不同主机 。开发要考虑跨主机通信的网络差异(如防火墙、网段限制),别只依赖本地调试逻辑。

  2. 目标标识:IP + 端口
    数据传输得明确 “终点”,目的 IP 定位目标主机,目的端口号 定位主机内接收数据的进程。编程时要准确设置这两个参数,否则数据发错地方,通信直接失败。

  3. 套接字类型与协议层
    Socket 编程用 流套接字(基于 TCP) 或 数据报套接字(基于 UDP) 开发,对应传输层 TCP/UDP 协议。但光有传输层还不够,应用层协议得自己设计(比如定义数据包格式、指令含义),这部分后续会详细讲怎么规划。

  4. 端口占用冲突问题                                                                                                                  如果一个进程A已经绑定了一个端口,再启动一个进程B绑定该端口,就会报错,这种情况也叫端口被占用。对于java进程来说,端口被占用的常见报错信息如下: 此时需要检查进程B绑定的是哪个端口,再查看该端口被哪个进程占用。

解决端口被占用的问题:

  • 如果占用端口的进程A不需要运行,就可以关闭A后,再启动需要绑定该端口的进程B
  • 如果需要运行A进程,则可以修改进程B的绑定端口,换为其他没有使用的端口。

3. UDP 数据报套接字编程:简单高效的 “快传” 实践 

3.1 API 介绍:核心工具

3.1.1 DatagramSocket

        UDP 通信的 “收发器”,用于发送和接收UDP数据报。服务端常绑定固定端口(如 DatagramSocket serverSocket = new DatagramSocket(8888); ),方便客户端定位;客户端一般不绑定固定端口(DatagramSocket clientSocket = new DatagramSocket(); ),由系统分配 。

构造方法:

常用方法: 

3.1.2 DatagramPacket 

        “数据报载体”,包含数据、目标地址(发送时 )或源地址(接收时 )。发送时,构建 DatagramPacket 需指定数据字节数组、目标地址 / 端口;接收时,创建空 DatagramPacket 当缓冲区,用 DatagramSocket 接收后解析数据 。

构造方法:

常用方法: 

        构造UDP发送的数据报时,需要传入 SocketAddress ,该对象可以使用 InetSocketAddress
来创建。 

3.1.3 InetSocketAddress

        InetSocketAddress 是 SocketAddress 的子类。

构造方法:

3.2 代码示例:UDP 通信全流程 

3.2.1 UDP Echo Server

public class UdpEchoServer {// 用于网络通信的套接字对象,就像服务器的"通信接口"private DatagramSocket socket = null;// 服务器构造方法,需要指定一个端口号来启动服务// 端口号就像快递柜的编号,方便客户端找到对应的服务public UdpEchoServer(int port) throws SocketException {// 绑定指定端口,启动服务器的通信接口socket = new DatagramSocket(port);}// 服务器的启动方法,一旦调用就开始工作public void start() throws IOException {System.out.println("服务器启动成功!正在等待客户端连接...");// 服务器需要一直运行,用死循环来保持工作状态while (true) {// 每次循环处理一个客户端的请求和响应// 1. 创建一个数据包对象,用来接收客户端发来的数据// 就像准备一个"收件盒",指定最大能装4096字节的数据DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);// 等待接收客户端的数据,这一步会"卡住"直到有数据到来socket.receive(requestPacket);// 把接收到的字节数据转换成字符串,方便处理// 注意:只转换实际收到的长度,避免多余的空字符String request = new String(requestPacket.getData(), 0, requestPacket.getLength());// 2. 处理请求,得到响应结果// 对于回显服务器来说,直接把收到的内容返回即可String response = process(request);// 3. 把响应结果发回给客户端// 准备一个"发件盒",装着要发送的内容,以及客户端的地址和端口DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),  // 要发送的内容(转换为字节数组)response.getBytes().length,  // 内容的长度requestPacket.getSocketAddress()  // 客户端的地址和端口(从请求中获取));// 发送响应数据socket.send(responsePacket);// 打印日志,记录通信详情System.out.printf("[客户端 %s:%d] 收到: %s, 回复: %s\n",requestPacket.getAddress().toString(),  // 客户端IP地址requestPacket.getPort(),  // 客户端端口request,  // 客户端发送的内容response  // 服务器回复的内容);}}// 处理请求的方法// 这里实现的是回显功能:输入什么,就返回什么public String process(String request) {return request;}// 程序入口:启动服务器public static void main(String[] args) throws IOException {// 创建服务器实例,指定端口号9090UdpEchoServer server = new UdpEchoServer(9090);// 启动服务器server.start();}
}

3.2.2 UDP Echo Client

public class UdpEchoClient {// 客户端的通信接口,就像打电话用的"手机"private DatagramSocket socket = null;// 服务器的IP地址,类似对方的"电话号码"private String serverIp;// 服务器的端口号,类似对方"手机上的某个APP"private int serverPort;// 客户端构造方法:需要知道服务器的IP和端口才能连接// IP地址格式是"点分十进制",比如"192.168.1.1"public UdpEchoClient(String serverIp, int serverPort) throws SocketException {this.serverIp = serverIp;this.serverPort = serverPort;// 初始化客户端的通信接口(不用指定端口,系统会自动分配一个)socket = new DatagramSocket();}// 客户端启动方法:开始和服务器通信public void start() throws IOException {System.out.println("客户端启动成功!可以开始发送消息了...");// 创建Scanner对象,用于读取用户在控制台输入的内容Scanner scanner = new Scanner(System.in);// 循环处理:不断读取用户输入并和服务器交互while (true) {// 显示提示符号,告诉用户可以输入内容了System.out.print("-> ");// 检查用户是否还有输入(如果输入结束就退出循环)if (!scanner.hasNext()) {break;}// 读取用户输入的内容(这就是要发给服务器的请求)String request = scanner.next();// 构造要发送的数据包:相当于把消息装进"信封"DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),  // 要发送的内容(转成字节数组)request.getBytes().length,  // 内容的长度InetAddress.getByName(serverIp),  // 服务器的IP地址(把字符串转成网络地址)serverPort  // 服务器的端口号);// 发送数据包:相当于把信封"寄出去"socket.send(requestPacket);// 准备接收服务器的回复:创建一个"收件盒"DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);// 等待接收服务器的回复:这一步会"卡住"直到收到消息socket.receive(responsePacket);// 把服务器回复的字节数据转成字符串String response = new String(responsePacket.getData(), 0, responsePacket.getLength());// 在控制台显示服务器的回复内容System.out.println(response);}}// 程序入口:启动客户端public static void main(String[] args) throws IOException {// 创建客户端实例,连接到本机(127.0.0.1)的9090端口服务器UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);// 启动客户端client.start();}
}

3.2.3 3. UDP Dict Serve 字典服务器

思路:客户端发单词,服务器查字典(用 Map 存单词 - 翻译 )返回翻译。只需要重写 process。比如初始化 Map<String, String> dict = new HashMap<>(); ,存入 dict.put("apple", "苹果"); 等。服务器接收单词后,在 dict 中查找,把翻译当响应发回;客户端发请求、收翻译并展示。通过这个示例,能更灵活理解 UDP 套接字的应用 。

4. TCP 流套接字编程:可靠传输的 “保障”

4.1 API 介绍:构建可靠连接

4.1.1 ServerSocket

        ServerSocket 是创建TCP服务端Socket的API。是服务端 “大门”,监听客户端连接。创建时绑定端口,如ServerSocket serverSocket = new ServerSocket(9090);然后通过 serverSocket.accept() 阻塞等客户端连接,有连接时返回 Socket 用于通信 。

构造方法:

其它方法: 

4.1.2 Socket

        Socket 是客户端发起连接或服务端通过 ServerSocket.accept () 响应连接后得到的通信端点,双方建立连接后,用其保存的对端信息收发数据,客户端创建时需指定服务端 IP 和端口 ,服务端通过该 Socket 的输入输出流交互 。

构造方法:

其它方法:

4.2 代码示例:TCP 通信实践

4.2.1 TCP Echo Server

public class TcpEchoServer {// 服务器的"总机",负责接听客户端的连接请求ServerSocket serverSocket = null;// 构造方法:创建服务器并指定端口号(就像给总机分配一个电话号码)public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);}// 启动服务器的方法public void start() throws IOException {System.out.println("服务器启动成功!等待客户端连接...");// 创建一个线程池,用来处理多个客户端的请求(相当于多个接线员)// newCachedThreadPool表示可以根据需要自动创建新线程ExecutorService pool = Executors.newCachedThreadPool();// 服务器一直运行,不断接收新的客户端连接while (true) {// 等待客户端连接(总机接听电话)// 这一步会"卡住",直到有客户端来连接Socket clientSocket = serverSocket.accept();// 收到连接后,交给线程池处理(安排一个接线员专门服务这个客户端)pool.submit(new Runnable() {@Overridepublic void run() {processConnection(clientSocket);}});}}// 处理单个客户端连接的方法(接线员和客户的通话过程)private void processConnection(Socket clientSocket) {// 打印客户端上线信息:包含客户端的IP和端口System.out.printf("[%s:%d] 客户端上线啦\n", clientSocket.getInetAddress(), clientSocket.getPort());// try-with-resources语法:自动关闭资源,不用手动写close()try (// 获取输入流:用来读取客户端发送的消息(相当于听客户说话)InputStream inputStream = clientSocket.getInputStream();// 获取输出流:用来向客户端发送消息(相当于跟客户说话)OutputStream outputStream = clientSocket.getOutputStream()) {// 创建Scanner对象,方便读取输入流中的文本Scanner scanner = new Scanner(inputStream);// 循环处理客户端的请求while (true) {// 判断客户端是否还有数据发送(如果没有,说明客户端要下线了)if (!scanner.hasNext()) {System.out.printf("[%s:%d] 客户端下线啦\n", clientSocket.getInetAddress(), clientSocket.getPort());break;}// 1. 读取客户端发送的请求内容String request = scanner.next();// 2. 处理请求(回显服务器直接返回相同内容)String response = process(request);// 3. 把响应写回给客户端// 创建PrintWriter方便写入文本PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(response);// 刷新缓冲区:确保数据立刻发送出去(不然可能留在缓存里)printWriter.flush();// 打印日志:记录这次通信的详情System.out.printf("[%s:%d] 收到: %s, 回复: %s\n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);}} catch (IOException e) {// 捕获并打印异常信息(比如网络中断等问题)e.printStackTrace();}}// 处理请求的核心方法(回显逻辑:输入什么返回什么)private String process(String request) {return request;}// 程序入口:启动服务器public static void main(String[] args) throws IOException {// 创建服务器实例,绑定9090端口TcpEchoServer server = new TcpEchoServer(9090);// 启动服务器server.start();}
}

4.2.2 TCP Echo Client

public class TcpEchoClient {// 客户端的"电话",用来和服务器建立连接并通话private Socket clientSocket = null;// 构造方法:初始化客户端,需要知道服务器的IP和端口(相当于知道对方的电话号码)public TcpEchoClient(String serverIp, int serverPort) throws IOException {// 连接服务器:就像拨打指定的电话号码clientSocket = new Socket(serverIp, serverPort);}// 启动客户端:开始和服务器通信public void start() {System.out.println("客户端启动成功!可以开始聊天啦...");// try-with-resources语法:自动关闭输入输出流,不用手动关闭try (// 输入流:用来接收服务器发过来的消息(相当于耳朵,听服务器说话)InputStream inputStream = clientSocket.getInputStream();// 输出流:用来向服务器发送消息(相当于嘴巴,跟服务器说话)OutputStream outputStream = clientSocket.getOutputStream()) {// 扫描器1:用来读取用户在控制台输入的内容(从键盘读)Scanner scannerConsole = new Scanner(System.in);// 扫描器2:用来读取服务器发送过来的消息(从网络读)Scanner scannerNetwork = new Scanner(inputStream);// 打印器:用来向服务器发送消息(包装输出流,方便写文本)PrintWriter writer = new PrintWriter(outputStream);// 循环聊天:不断读取用户输入并发送给服务器,再接收服务器回复while (true) {// 显示提示符号,告诉用户可以输入内容了System.out.printf("-> ");// 检查用户是否还有输入(如果没有输入,就退出循环)if (!scannerConsole.hasNext()) {break;}// 1. 读取用户在控制台输入的内容(要发给服务器的消息)String request = scannerConsole.next();// 2. 把消息发送给服务器writer.println(request);// 刷新缓冲区:确保消息立刻发出去(不然可能存在缓存里没发送)writer.flush();// 3. 等待并读取服务器的回复String response = scannerNetwork.next();// 4. 在控制台显示服务器的回复内容System.out.println(response);}} catch (IOException e) {// 捕获并打印异常(比如网络断开等问题)e.printStackTrace();} finally {// 最后一定要关闭客户端的"电话",释放资源try {clientSocket.close();} catch (IOException e) {e.printStackTrace();}}}// 程序入口:启动客户端public static void main(String[] args) throws IOException {// 创建客户端实例,连接到本机(127.0.0.1)的9090端口服务器TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);// 启动客户端,开始通信client.start();}
}

5. 长短连接:按需选择通信模式

        在 TCP 通信体系里,连接的建立与关闭时机,直接界定了短连接和长连接两种模式。合理选用,能让程序在效率、资源占用间找到平衡,适配不同业务场景。

5.1 短连接与长连接的定义

TCP 传输数据依赖先建立连接,“何时关闭连接” 是区分短、长连接的核心

  • 短连接:每次完成 “接收数据 + 返回响应” 后,立即关闭连接 。如同 “一锤子买卖”,一次连接仅支撑单次收发数据,下次交互需重新建连。
  • 长连接:保持连接状态不关闭,允许双方持续收发数据 。像 “持续对话”,一次建连可支撑多次数据交互,直至主动断开或网络异常。

5.2 短连接与长连接的核心区别

对比短、长连接,从建连开销到适用场景,差异显著:

5.2.1 连接建立与关闭的耗时差异

  • 短连接:每次请求 - 响应,都要经历 “建连→传数据→关连” 全流程 。频繁交互时,建连、关连的耗时会叠加,拖慢整体效率。
  • 长连接:仅首次需完整建连,后续请求 - 响应直接复用已连通道 。省掉重复建连、关连的耗时,高频交互场景下效率优势明显。

5.2.2 主动请求的发起方差异

  • 短连接:通常由客户端主动发起请求,服务端被动响应 。典型如浏览器访问静态网页,客户端发请求,服务端回数据后关连,服务端很少主动 “推送”。
  • 长连接:支持双向主动通信 。既允许客户端主动发请求(如聊天时发消息 ),也支持服务端主动推送数据(如即时通讯的新消息通知、实时行情更新 )。

5.2.3 适用场景差异

  • 短连接:适配客户端请求频率低的场景 。如浏览新闻网页(单次请求即可获取内容 )、查询静态数据接口(查天气、物流信息 ),无需持续保持连接,“即用即关” 省资源。
  • 长连接:专为客户端与服务端高频通信设计 。像聊天室(持续收发消息 )、实时游戏(同步玩家操作、状态 )、金融行情推送(秒级更新数据 ),依赖长连接实现低延迟、高实时性交互。

5.3 长连接的 “扩展痛点” 与优化方向

        长连接虽高效,但若基于传统 BIO(同步阻塞 IO )实现,会面临系统资源占用过高问题:

5.3.1 BIO 长连接的资源瓶颈

        BIO 模式下,每个长连接对应一个阻塞线程 。连接需持续 “阻塞等待” 数据,若有 1 万长连接,服务端需创建 1 万线程,内存、线程切换开销会直接 “压垮” 系统。

5.3.2 NIO 优化长连接的思路

        为解决 BIO 缺陷,Java 引入 NIO(同步非阻塞 IO ) ,通过 Selector(多路复用器 ) 实现:

  • 少量线程管理大量连接,线程无需阻塞等待,而是由 Selector 监听 “哪些连接有数据可读 / 可写”,按需处理。
  • 极大降低资源消耗,支撑高并发长连接场景(如百万级在线的即时通讯系统 ),主流框架(Netty )也基于 NIO 封装,简化长连接开发。

5.4 总结:按需选型,平衡效率与资源

        短连接 “简单轻量”,适合低频交互;长连接 “高效持久”,适配高频、实时场景 。实际开发中,需结合业务需求:

  • 若做新闻网站、静态数据接口,选短连接省心省力;
  • 若开发聊天、实时游戏,长连接是必选项,且建议基于 NIO/Netty 优化资源占用。
    理解长短连接的本质差异,才能让网络通信既高效又稳定,贴合业务需求 。
http://www.xdnf.cn/news/1181521.html

相关文章:

  • 算法第三十八天:动态规划part06(第九章)
  • uboot FPGA调试环境搭建
  • io_uring:Linux异步I/O的革命性突破
  • 星慈光编程虫2号小车讲解第四篇--触摸按键
  • 平时遇到的错误码及场景?404?400?502?都是什么场景下什么含义,该怎么做 ?
  • vue3核心语法
  • (进阶向)Python第十四期OpenCv图像预处理方法[2]
  • 跨境支付入门~国际支付结算(稳定币)
  • 深度分析Java多线程机制
  • AI实践:Pydantic
  • Spring之SSM整合流程详解(Spring+SpringMVC+MyBatis)
  • 【Linux】常用命令(一)
  • 洛谷P1512 伊甸园日历游戏
  • SQL基础⑫ | 视图篇
  • C++ - 仿 RabbitMQ 实现消息队列--服务端核心模块实现(三)
  • 基于深度学习的图像分类:使用MobileNet实现高效分类
  • Python进阶第三方库之Matplotlib
  • 深度学习(鱼书)day01--感知机
  • LeetCode 23:合并 K 个升序链表
  • 【C++】使用中值滤波算法过滤数据样本中的尖刺噪声
  • rust-方法语法
  • C++STL系列之set和map系列
  • 基于python django的农业可视化系统,以奶牛牧场为例
  • 用 Function Call 让 AI 主动调用函数(超入门级示例)|保姆级大模型应用开发实战
  • SpringBoot航空订票系统的设计与实现
  • 进阶系统策略
  • 技术赋能多元探索:我的技术成长与行业洞察
  • Linux应用开发基础知识——进程学习2(exec函数、system函数、popen函数)(三)
  • 斐波那契数列策略
  • 人形机器人_双足行走动力学:Maxwell模型及在拟合肌腱特性中的应用