提示文章写完后目录可以自动生成如何生成可参考右边的帮助文档文章目录导语一、网络编程概述先有一张总图1.1 什么是网络编程通俗版1.2 TCP与 UDP打电话vs明信片1.3 Java网络编程技术体系1.4 再补一层画面数据到底怎么【走】的1.5 端口再讲清楚一点二、Java Socket 编程打电话式可靠通信2.1 Socket 与 ServerSocket 是干什么的2.2 最小可运行一问一答TCP2.3 多客户端线程池当个【多名柜员】2.4 半成品聊天室理解【广播】与协议约定2.5 运行说明TCP系列通用三、UDP编程寄明信片式尽力而为3.1 DatagramSocket 与 DatagramPacket3.2 可运行示例3.3 TCP vs UDP写代码时怎么选四、Java NIO一个前台盯很多窗口4.1 BIO / NIO / AIO 对比4.2 三个核心概念4.3 非阻塞 Echo 服务器精简可运行4.4 Buffer 状态机4.5 什么时候不该上NIO五、基于HTTP的网络通信5.1 Java11 HttpClientGET / POST5.2 和 Socket 编程的关系别把层搅在一起5.3 超时怎么设才不算拍脑袋六、异常处理与资源管理6.1 用代码固定「正确关闭」习惯七、安全与工程实践7.1 长度头协议最小可运行拆包示意八、常见问题与排查建议补一张「学习时最容易混」的对照表九、总结与学习路线导语网络编程不是「背 API」而是让两台或同一台机器上的程序按约定交换字节。本文面向有 Java 基础、刚开始学网络编程的开发者先用类比建立总图再落到 Socket、UDP、NIO、HttpClient最后补上异常处理、安全和工程实践。文中代码基于Java 17尽量完整可运行为便于演示部分示例省略了生产级限流、鉴权等能力文中会明确说明。理论脉络可对照 [小林 coding《图解网络》](https://xiaolincoding.com/network/) 中的 TCP/IP、HTTP 等篇章本文侧重 **Java 怎么写、为什么这样写、踩坑怎么排**。提示以下是本篇文章正文内容下面案例可供参考一、网络编程概述先有一张总图1.1 什么是网络编程通俗版网络编程用代码控制【本进程 -对端进程】如何建立通道、发送/接收字节、处理错误与关闭。一句话IP找到机器端口找到进程协议约定怎么说话。1.2 TCP与 UDP打电话vs明信片1.3.Java网络编程技术体系应用层 HTTP ──► HttpClient / 各类 Web 框架传输层 TCP ──► Socket / ServerSocket / NIO / Netty传输层 UDP ──► DatagramSocket / DatagramPacket名称解析 ──► InetAddressDNS-BIOBlocking I/O一个连接卡住读写当前线程就等着——像一个柜员只服务一位顾客。-NIONew I/O / Non-blocking少量线程用 Selector 盯很多通道——像一个前台同时盯多个窗口灯。-AIOAsynchronous I/O真正异步完成回调Java 里用得相对少生产更多见 Netty 等封装。先掌握 Socket 多线程再学 NIO最后按需上 Netty / Spring WebFlux——这条路线最稳。1.4 再补一层画面数据到底怎么【走】的我们在Java里写 out.write(你好\n.getBytes(UTF_8))脑子里可以走一遍这条路径【不必背死但要有图1.应用把字符串变成字节2.操作系统把字节放进TCP发送缓存区3.TCP按窗口切成段segment带上序号4.IP加上源/目的IP路由出去5.对端反向拆包字节接收缓存区6.我的in.readLine() 从缓冲区读到 \n 才返回一行。所有Java API管的是【进程视角的流】可靠、重传、乱序重组大多在操作系统协议栈里完成。我要负责的是协议边界什么叫一条消息、超时、并发、资源关闭和安全校验。1.5 端口再讲清楚一点0-1023常见系统服务HTTP 80、HTTPS 443普通用户绑定可能需要权限。1024-49151注册端口应用常用如 80803306。49152-65535临时端口客户端 connect时OS常自动分配。一个TCP连接的五元组是协议源IP源端口目的IP目的端口。所有【服务端固定8080】不妨碍同时服务成千上万客户端——因为每个客户端源端口不同。二、Java Socket 编程打电话式可靠通信2.1 Socket 与 ServerSocket 是干什么的- ServerSocket服务端「对外营业」——绑定端口、accept() 等客人推门进来。- Socket一次连接的两端各有一个——拿到它就能用输入/输出流读写字节。2.2最小可运行一问一答TCP下面示例在本机 127.0.0.1:9090 上跑通「发一行 → 回一行」。先启动服务端再启动客户端。服务端package Exercise.network; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.StandardCharsets; /** 演示用单线程 TCP 回显服务端一次只认真服务一个连接结束后再接下一个。 生产环境请改为线程池/NIO并限制消息长度。 */ public class EchoServer { private static final int PORT 9090; private static final int MAX_LINE 1024; public static void main(String[] args) { System.out.println(EchoServer 监听窗口 PORT); try (ServerSocket serverSocket new ServerSocket(PORT)){ while (true){ try (Socket socket serverSocket.accept(); BufferedReader in new BufferedReader( new InputStreamReader(socket.getInputStream(),StandardCharsets.UTF_8) ); BufferedWriter out new BufferedWriter( new OutputStreamWriter(socket.getOutputStream(),StandardCharsets.UTF_8) )){ String line in.readLine(); if (line null){ continue; } if (line.length() MAX_LINE){ out.write(ERR;message too long\n); out.flush(); continue; } System.out.println(收到line); out.write(ECHO:line\n); out.flush(); } } } catch (IOException e) { System.err.println(处理连接失败: e.getMessage()); System.err.println(无法绑定端口: e.getMessage()); } } }客户端package Exercise.network; import java.io.*; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.charset.StandardCharsets; public class EchoClient { public static void main(String[] args) { String host 127.0.0.1; int port 9090; String message args.length0 ? args[0] : hello-network; try(Socket socket new Socket()) { //连接超时 3秒避免永远卡住connect socket.connect(new InetSocketAddress(host,port),3000); socket.setSoTimeout(3000);//读超时 BufferedWriter out new BufferedWriter( new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8) ); BufferedReader in new BufferedReader( new InputStreamReader(socket.getInputStream(),StandardCharsets.UTF_8) ); out.write(message \n); out.flush(); String response in.readLine(); System.out.println(服务端回复response); } catch (IOException e) { System.out.println(通信失败e.getMessage()); } } }逐步解释1.ServerSocket(PORT)占用本机 9090别人连这个端口就是连你。2.·accept()·阻塞等到一个客户端完成TCP三次握手后返回业务·Socket·。3.用 **UTF-8 按行协议**\n 结尾双方约定「一行一条消息」先避开粘包难题。4.try-with-resources自动关流和 Socket减少泄漏。5. 客户端 connect / setSoTimeout把「连不上、对端不回」变成可预期的超时异常。2.3 多客户端线程池当个【多名柜员】单线程·accept·→ 处理 → 再 accept第二个客户会排长队。常见做法是主线程只负责 accept业务丢给线程池。package Exercise.network; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.StandardCharsets; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; public class MultiClientEchoServer { private static final int PORT 9091; private static final int MAX_LINE 1024; public static void main(String[] args) throws InterruptedException { //固定大小线程池防止恶意海量连接把机器打满 ExecutorService pool Executors.newFixedThreadPool(16); try (ServerSocket serverSocket new ServerSocket(PORT)){ System.out.println(多客户端Echo监听 PORT); while (!Thread.currentThread().isInterrupted()){ Socket socket serverSocket.accept(); try { pool.execute(()-handle(socket)); }catch (RejectedExecutionException ex){ closeQuietly(socket); System.err.println(线程池繁忙拒绝新连接); } } } catch (IOException e) { System.out.println(服务异常e.getMessage()); }finally { pool.shutdown(); pool.awaitTermination(6, TimeUnit.SECONDS); } } private static void closeQuietly(Socket socket) { try (socket; BufferedReader in new BufferedReader( new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8) ); BufferedWriter out new BufferedWriter( new OutputStreamWriter(socket.getOutputStream(),StandardCharsets.UTF_8) ) ){ String line in.readLine(); if (line null || line.length() gt; MAX_LINE){ return; } out.write(ECHO line\n); out.flush(); } catch (IOException e) { System.out.println(客户端处理失败e.getMessage()); } } private static void handle(Socket socket) { try { socket.close(); } catch (IOException e) { throw new RuntimeException(e); } } }主线程是门口保安只开门线程池是柜员真正办事。柜员数量有限客太多就拒绝或排队避免【无限 new Thread】把内存和FD耗尽。说明这仍是BIO。连接数到几千且多数空闲时线程成本会很高——那是NIO/Netty的舞台。2.4 半成品聊天室理解【广播】与协议约定下面把【一问一答】升级成【多人广播】骨架每个客户端一个读线程服务端把某人说的化转发给其他人。为控制篇幅这里只给服务端核心结构完整工程可自行补客户端UI。仍是教学示例无登录、无加密、无背压。package Exercise.network; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.StandardCharsets; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** 简易聊天室服务端骨架按行协议行首可约定 NAME:xxx / MSG:内容。 */ public class TinyChatServer { private static final int PORT 9094; private static final int MAX_LINE 512; private static final SetClientSession CLIENTS ConcurrentHashMap.newKeySet(); public static void main(String[] args) { ExecutorService pool Executors.newCachedThreadPool(); try (ServerSocket serverSocket new ServerSocket(PORT)){ System.out.println(聊天室端口 PORT); while (true){ Socket socket serverSocket.accept(); pool.execute(()-serve(socket)); } } catch (IOException e) { throw new RuntimeException(e); } } private static void serve(Socket socket) { ClientSession session null; try { BufferedReader in new BufferedReader( new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8) ); BufferedWriter out new BufferedWriter( new OutputStreamWriter(socket.getOutputStream(),StandardCharsets.UTF_8) ); session new ClientSession(socket,out); CLIENTS.add(session); String line; while ((line in.readLine()) !null){ if (line.length() MAX_LINE){ session.send(ERR:too long); break; } if (line.startsWith(NAME:)){ session.name line.substring(5).trim(); broadcast(系统,session.name加入了聊天室); }else if (line.startsWith(MSG)){ String name session.name null ?匿名:session.name; broadcast(name,line.substring(4)); }else { session.send(ERR:unknown command); } } } catch (IOException e) { System.out.println(会话结束:e.getMessage()); }finally { if(session !null){ CLIENTS.remove(session); session.close(); } } } private static void broadcast(String from, String content) { String payload from : content; for (ClientSession c : CLIENTS){ c.send(payload); } } static final class ClientSession { final Socket socket; final BufferedWriter out; volatile String name; ClientSession(Socket socket,BufferedWriter out){ this.socket socket; this.out out; } synchronized void send(String msg){ try { out.write(msg\n); out.flush(); } catch (IOException e) { throw new RuntimeException(e); } } void close(){ try { socket.close(); } catch (IOException e) { throw new RuntimeException(e); } } } }我们从这段代码应带走三件事1.协议先行NAME: / MSG: 是应用层约定不是 TCP 自带的。2.共享集合要线程安全多线程广播用ConcurrentHashMap.newKeySet()。3.写要同步同一 BufferedWriter 并发写会乱所以 send 加了 synchronized。2.5 运行说明TCP系列通用1.使用JDK 17javac EchoServer.java java EchoServer。2.另开中断跑客户端。防火墙若拦截先放行本地端口。3.Windows上看端口netstat -ano | findstr 9090。4.若报端口占用换端口或结束旧Java进程。三、UDP编程寄明信片式尽力而为3.1 DatagramSocket 与 DatagramPacket- DatagramSocketUDP「邮箱」。- DatagramPacket一封有长度、有目标地址的「明信片」。没有连接状态也没有【对端一定收到】的保证——DNS、心跳实时音频视频常选它。3.2可运行示例服务端package Exercise.network; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; import java.nio.charset.StandardCharsets; public class UdpEchoServer { public static void main(String[] args) { final int port 9092; final int maxLen 1024; byte[] buf new byte[maxLen]; try (DatagramSocket socket new DatagramSocket(port)){ System.out.println(UDP Echo 监听 port); while (true){ DatagramPacket request new DatagramPacket(buf,buf.length); socket.receive(request);//阻塞收——包 String msg new String(request.getData(),0,request.getLength(), StandardCharsets.UTF_8); System.out.println(来自 request.getAddress() : request.getPort()- msg); byte[] resp (ECHO: msg).getBytes(StandardCharsets.UTF_8); DatagramPacket response new DatagramPacket( resp,resp.length,request.getAddress(),request.getPort() ); socket.send(response); } } catch (SocketException e) { throw new RuntimeException(e); } catch (IOException e) { System.out.println(UDP服务失败e.getMessage()); } } }客户端package Exercise.network; import java.io.IOException; import java.net.*; import java.nio.charset.StandardCharsets; public class UdpEchoClient { public static void main(String[] args) { String message args.length 0 ? args[0]: ping-udp; byte[] data message.getBytes(StandardCharsets.UTF_8); try (DatagramSocket socket new DatagramSocket()){ socket.setSoTimeout(3000); InetAddress address InetAddress.getByName(127.0.0.1); DatagramPacket request new DatagramPacket(data,data.length,address,9092); socket.send(request); byte[] buf new byte[1024]; DatagramPacket response new DatagramPacket(buf, buf.length); socket.receive(response); String reply new String(response.getData(),0,response.getLength(),StandardCharsets.UTF_8); System.out.println(收到reply); }catch (IOException e) { System.out.println(UDP 通信失败可能丢包或超时e.getMessage()); } } }3.3 TCP vs UDP写代码时怎么选四、Java NIO一个前台盯很多窗口4.1 BIO / NIO / AIO 对比4.2 三个核心概念1.Channel双向通道如 ServerSocketChannel、SocketChannel。2.Buffer字节的暂存区读写都围绕 Buffer 的 position / limit / capacity。3.Selector多路复用器——一次 select() 找出「可读/可写/可接受」的 Channel。4.3 非阻塞 Echo 服务器精简可运行package Exercise.network; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; import java.util.Iterator; import java.util.Set; /** 演示用 NIO 回显服务单线程 Selector 驱动。 未实现完整拆包/粘包与写半包缓冲生产请用 Netty 等框架。 */ public class NioEchoServer { public static void main(String[] args) throws IOException { Selector selector Selector.open(); ServerSocketChannel server ServerSocketChannel.open(); server.bind(new InetSocketAddress(9093)); server.configureBlocking(false); server.register(selector, SelectionKey.OP_ACCEPT); System.out.println(NIP Echo 监听 9093); while (true){ selector.select(); SetSelectionKey keys selector.selectedKeys(); IteratorSelectionKey it keys.iterator(); while (it.hasNext()){ SelectionKey key it.next(); it.remove(); if(!key.isValid()){ continue; } if (key.isAcceptable()){ accept(key,selector); } else if (key.isReadable()) { read(key); } } } } private static void read(SelectionKey key) throws IOException{ SocketChannel client (SocketChannel) key.channel(); ByteBuffer buffer (ByteBuffer) key.attachment(); int n client.read(buffer); if(n -1){ client.close(); return; } buffer.flip(); String msg StandardCharsets.UTF_8.decode(buffer).toString().trim(); buffer.clear(); if (msg.isEmpty()){ return; } ByteBuffer out ByteBuffer.wrap((ECHO:msg \n).getBytes(StandardCharsets.UTF_8)); while (out.hasRemaining()){ client.write(out);//演示简化未处理写不完的情况 } } private static void accept(SelectionKey key, Selector selector) throws IOException { ServerSocketChannel server (ServerSocketChannel) key.channel(); SocketChannel client server.accept(); if (client null){ return; } client.configureBlocking(false); client.register(selector,SelectionKey.OP_READ,ByteBuffer.allocate(1024)); System.out.println(接入client.getRemoteAddress()); } }NIO 解决什么用少量线程扛大量连接尤其是「连着但不说话」的连接。复杂性Buffer 状态、半包写、协议拆包、线程模型都要自己管——所以业务项目多直接上Netty。局限不是「无脑比 BIO 快」业务很重、连接很少时BIO 线程池往往更简单够用。4.4 Buffer 状态机很多人写 NIO 时在 flip / clear / compact 上栽跟头。记住三个数-capacity容量固定。-position下一个读写位置。-limit可读写的上限。写完数据要改成「读模式」→ flip()limitpositionposition0。读完要继续写 → clear()丢弃未读或 compact()把未读挪到前面。类比Buffer 是一张可擦写草稿纸flip 是「翻到另一面开始读你刚写的字」。4.5什么时候不该上NIO- 连接数几十、业务逻辑复杂DB、RPC 串行——瓶颈不在 I/O 模型。- 团队不熟 NIO却硬造「自研协议框架」——维护成本远高于收益。- 已经有 Spring MVC 合理线程池能满足 SLA——先优化慢 SQL 和超时再谈模型升级。五、基于HTTP的网络通信应用层协议HTTP跑在TCPHTTP/3则基于QUIC/UDP)之上。请求/响应可粗看成请求行 GET /api/hello HTTP/1.1头部 Host: example.comContent-Type: application/json空行可选正文 {name:Ada}状态码直觉2xx成功4xx客户端问题5xx服务端问题。5.1Java11 HttpClientGET / POST优先用 JDK 自带 java.net.http.HttpClient无需额外依赖。package Exercise.network; import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; public class HttpClientDemo { public static void main(String[] args) throws IOException, InterruptedException { HttpClient client HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(3)) .build(); //GET HttpRequest get HttpRequest.newBuilder() .uri(URI.create(https://httpbin.org/get)) .timeout(Duration.ofSeconds(5)) .header(Accept,application/json) .GET() .build(); HttpResponselt;Stringgt; getResp client.send(get,HttpResponse.BodyHandlers.ofString()); System.out.println(GET statusgetResp.statusCode()); System.out.println(getResp.body().substring(0,Math.min(200,getResp.body().length()))); //POST JSON(正文仅演示真实项目用可好JSON库并检验响应 String json{\msg\:\hello\}; HttpRequest post HttpRequest.newBuilder() .uri(URI.create(https://httpbin.org/post)) .timeout(Duration.ofSeconds(5)) .header(Content-Type,application/json) .POST(HttpRequest.BodyPublishers.ofString(json)) .build(); HttpResponselt;Stringgt; postResp client.send(post,HttpResponse.BodyHandlers.ofString()); System.out.println(PSOT statuspostResp.statusCode()); } }要点- connectTimeout建连超时timeout整次请求超时。- 看 statusCode()不要假设一定是 200。- JSONJDK 无强推标准库解析可用 Jackson / Gson引入要写清依赖。例如 Mavendependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.17.2/version /dependency版本请按项目统一管理此处仅为说明「第三方库要显式依赖」。5.2 和 Socket 编程的关系别把层搅在一起- 写 Socket你在**传输层**视角搬字节自己定消息格式。- 写 HttpClient你在**应用层**发「有语义的请求」TCP/TLS 由实现代劳。- 写 Servlet / Spring MVC容器帮你把 HTTP 解析成 HttpServletRequest你更关心业务。学习路径上先碰 Socket 是为了理解「流、阻塞、粘包」做业务时优先 HTTP 生态不要用裸 Socket 重复造 HTTP。5.3 超时怎么设才不算拍脑袋经验对内网 RPC 可较短跨公网或依赖第三方要结合 P99 留余量并配合熔断/舱壁而不是无限加大超时。六、异常处理与资源管理原则1. 网络对象用 try-with-resources 或 finally 关闭。2. 线程池要 shutdown任务要能响应中断避免线程泄漏。3. 读写都设超时禁止无限阻塞拖垮线程池。4. 日志记对端地址、耗时、异常类型不要把密码、Token 打进日志。6.1 用代码固定「正确关闭」习惯下面展示「先设超时、再通信、异常分类日志」的客户端模板可直接当脚手架改package Exercise.network; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; import java.nio.charset.StandardCharsets; public class SafeSocketClientTemplate { public static void main(String[] args) { try (Socket socket new Socket()){ socket.connect(new InetSocketAddress(127.0.0.1,9090),2000); socket.setSoTimeout(2000); socket.getOutputStream().write(ping\n.getBytes(StandardCharsets.UTF_8)); socket.getOutputStream().flush(); byte[] buf new byte[256]; int n socket.getInputStream().read(buf); if (n0){ System.out.println(new String(buf,0,n,StandardCharsets.UTF_8)); } } catch (SocketException e) { System.err.println(超时: e.getMessage()); } catch (IOException e) { System.out.println(I/0失败e.getMessage()); } } }排查口诀先确认「对端听没听」→ 再确认「协议是否一致换行、编码」→ 再确认「超时与线程是否被堵」。七、安全与工程实践1.输入校验长度限制防止超大包占满内存前面MAX_LINE 就是最小实践。2.限制连接与线程固定线程池、必要时限流防简单资源耗尽。3.敏感信息Token、Cookie、密码脱敏4.生产传输公网用HTTPS/TLS配合身份认证Token、mTLS等。5.粘包/拆包TCP是字节流没有【消息边界】。工程上常用v- 固定长度- 分隔符如 \n- 长度头 body最常见。6.字符编码双方统一UTF-8避免「半个中文」乱码。7.1 长度头协议最小可运行拆包示意约定前 4 字节是大端 int表示后面 UTF-8 正文长度。这是生产里非常常见的做法Netty 的 LengthFieldBasedFrameDecoder 就是这类思想。package Exercise.network; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.StandardCharsets; public class LengthPrefixedEchoServer { private static final int MAX 4096; public static void main(String[] args) { try (ServerSocket server new ServerSocket(9095)){ System.out.println(长度头协议监听 9095); while (true){ try (Socket socket server.accept(); DataInputStream in new DataInputStream(socket.getInputStream()); DataOutputStream out new DataOutputStream(socket.getOutputStream())){ int len in.readInt(); if (len 0 || len MAX){ throw new IOException(非法长度len); } byte[] body in.readNBytes(len); if (body.length !len){ throw new IOException(正文不完整); } String msg new String(body, StandardCharsets.UTF_8); byte[] resp (ECHO:msg).getBytes(StandardCharsets.UTF_8); out.writeInt(resp.length); out.write(resp); out.flush(); } } } catch (IOException e) { System.out.println(连接处理失败e.getMessage()); } } }一句话复述TCP 只保证「字节按序到达」不保证「一次 read 正好一条业务消息」。长度头相当于先告诉对方「后面还有几页说明书」对方就能精确拆开。八、常见问题与排查建议1. 客户端卡住不动是否忘了 flush是否在等 \n 而对方没发是否没设读超时2.只连得上第一个客户是不是单线程同步处理改线程池或 NIO。3.中文乱码检查两端 Charset 是否都是 UTF-8。4.偶发丢消息UDP正常现象关键数据不要裸用 UDP或自己做确认重传。5.本机调试curl/telnet 测端口日志打出 getRemoteAddress()抓包用 Wireshark有需要时再上。补一张「学习时最容易混」的对照表把这张表贴在显示器边上排障时先问自己「我现在卡在哪一层」——这比上来就改代码更快。九、总结与学习路线核心知识一句话串联起来用 IP端口找到进程 → 用 TCP/UDP 约定怎么传 → 用 Socket/NIO 在 Java 里操作字节 → 用 HTTP 表达业务语义 → 用超时、线程池、校验和 TLS 让程序在真实网络里活得下去。学习路线建议1. BIO Socket / ServerSocket 按行协议2. 线程池多客户端3. UDP 对比体验4. NIO 三件套理解即可5. HttpClient JSON6. 进阶**Netty** 或 **Spring WebFlux**响应式/高并发网关场景。
阅读完成 · 觉得有帮助?