691 字
2 分钟
Java网络编程详解
用Java写一个简单的TCP聊天室是我大学时候最有成就感的事情之一。Socket编程虽然基础,但理解TCP/IP协议栈对排查网络问题帮助真的很大。这篇文章从基础概念讲起,一步步带你实现网络通信。
Java网络编程详解
网络编程基础概念
IP地址 & 端口号
IP地址定位设备,端口号定位应用。打个比方:IP是门牌号,端口是房间号。
- IPv4:32位,如
192.168.1.1 - IPv6:128位,如
2001:0db8:85a3:0000:0000:8a2e:0370:7334 - 端口范围:0-65535,0-1023是系统保留端口,别乱用
TCP vs UDP
| 特性 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接(先握手) | 无连接(直接发) |
| 可靠性 | 可靠(有重传机制) | 不可靠(丢了不管) |
| 速度 | 慢 | 快 |
| 适用场景 | 文件传输、网页 | 直播、游戏、DNS |
实战经验:TCP写起来相对简单,因为不用操心丢包的问题。UDP虽然快,但得自己实现可靠性,业务复杂度上去之后,直接上TCP或者Netty更省心。
Java网络编程核心类
1. InetAddress
// 获取本机IPInetAddress localHost = InetAddress.getLocalHost();System.out.println("本机: " + localHost);
// 获取域名对应的IPInetAddress baidu = InetAddress.getByName("www.baidu.com");System.out.println("百度IP: " + baidu.getHostAddress());
// 检查可达性(会发ICMP包)boolean reachable = baidu.isReachable(5000);2. Socket & ServerSocket(TCP)
服务端:
public class TCPServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8080); System.out.println("服务端启动,监听8080端口...");
while (true) { // accept() 是阻塞的,直到有客户端连接 Socket socket = serverSocket.accept(); // 每个客户端开一个线程处理 new Thread(() -> handleClient(socket)).start(); } }
private static void handleClient(Socket socket) { try (BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter( socket.getOutputStream(), true)) {
String message; while ((message = in.readLine()) != null) { System.out.println("收到: " + message); out.println("服务端回复: " + message); if ("bye".equals(message)) break; } } catch (IOException e) { e.printStackTrace(); } }}客户端:
public class TCPClient { public static void main(String[] args) throws IOException { try (Socket socket = new Socket("localhost", 8080); BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter( socket.getOutputStream(), true); BufferedReader keyboard = new BufferedReader( new InputStreamReader(System.in))) {
System.out.println("已连接到服务器"); String message; while ((message = keyboard.readLine()) != null) { out.println(message); String response = in.readLine(); System.out.println("服务端回复: " + response); if ("bye".equals(message)) break; } } }}3. DatagramSocket & DatagramPacket(UDP)
服务端:
public class UDPServer { public static void main(String[] args) throws Exception { DatagramSocket socket = new DatagramSocket(8080); byte[] buffer = new byte[1024];
while (true) { DatagramPacket packet = new DatagramPacket(buffer, buffer.length); socket.receive(packet); // 阻塞等待
String message = new String(packet.getData(), 0, packet.getLength()); System.out.println("收到: " + message + " 来自: " + packet.getAddress());
// 回复 String response = "服务端收到: " + message; byte[] responseData = response.getBytes(); DatagramPacket responsePacket = new DatagramPacket( responseData, responseData.length, packet.getAddress(), packet.getPort()); socket.send(responsePacket); } }}客户端:
public class UDPClient { public static void main(String[] args) throws Exception { DatagramSocket socket = new DatagramSocket(); InetAddress serverAddr = InetAddress.getByName("localhost");
BufferedReader keyboard = new BufferedReader( new InputStreamReader(System.in));
String message; while ((message = keyboard.readLine()) != null) { // 发送 byte[] data = message.getBytes(); DatagramPacket packet = new DatagramPacket( data, data.length, serverAddr, 8080); socket.send(packet);
// 接收回复 byte[] buffer = new byte[1024]; DatagramPacket responsePacket = new DatagramPacket(buffer, buffer.length); socket.receive(responsePacket); String response = new String(responsePacket.getData(), 0, responsePacket.getLength()); System.out.println("服务端回复: " + response);
if ("bye".equals(message)) break; } socket.close(); }}HTTP客户端
方式一:HttpURLConnection(Java原生)
URL url = new URL("https://api.github.com/users/octocat");HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");conn.setRequestProperty("Accept", "application/json");
int responseCode = conn.getResponseCode();System.out.println("响应码: " + responseCode);
BufferedReader in = new BufferedReader( new InputStreamReader(conn.getInputStream()));String line;StringBuilder content = new StringBuilder();while ((line = in.readLine()) != null) { content.append(line);}in.close();conn.disconnect();
System.out.println("响应内容: " + content);方式二:HttpClient(Java 11+,推荐)
Java 11引入了新的HttpClient,API比HttpURLConnection友好太多了:
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.github.com/users/octocat")) .header("Accept", "application/json") .build();
HttpResponse<String> response = client.send( request, HttpResponse.BodyHandlers.ofString());
System.out.println("响应码: " + response.statusCode());System.out.println("响应内容: " + response.body());实战贴士
- 资源管理:Socket、Stream这些用完一定要关,用try-with-resources最省心
- 超时设置:
socket.setSoTimeout(5000)设置读取超时,避免网络问题导致线程卡死 - 编码问题:网络传输统一用UTF-8,不然乱码问题查到你怀疑人生
- 连接池:高并发场景下不要频繁创建销毁连接,用连接池复用
- 优先用HttpClient:Java 11+的项目直接用HttpClient,别再用HttpURLConnection了
常见坑
- 连接泄漏:忘了关连接,线上应用连接数一直涨,最后把数据库连接池打满了。这个我踩过,场面一度非常尴尬。
- 没设超时:网络一卡,线程就永远挂在那了,慢慢就把线程池耗尽了。
- 编码不一致:服务端用GBK,客户端用UTF-8,出来的全是乱码,debug半天才发现。
- 端口被占用:启动时报
Address already in use,换个端口或者kill掉占用进程。 - 防火墙拦截:开发环境跑得好好的,部署到服务器就连不上,十有八九是防火墙没开端口。
分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时
相关文章 智能推荐
1
Linux网络管理命令和技巧
Linux 网络出问题的时候tcpdump和ss是最可靠的伙伴。这篇整理了Linux网络配置、路由、防火墙的日常操作,都是排查网络问题时实打实用过的命令。
2
Java多线程编程详解
Java 多线程是Java面试的重灾区,synchronized、volatile、Lock的区别能聊一个小时。这篇文章从线程的创建到并发工具类,把多线程的知识点串了一遍。
3
Java泛型编程详解
Java 泛型刚出来的时候我觉得不就是个类型参数嘛,直到被PECS原则(Producer Extends Consumer Super)绕晕了才明白泛型的水有多深。
4
Java面向对象编程基础详解
Java 封装、继承、多态——这三个词背了无数遍,但真正理解面向对象是在写了上万行代码之后的事。这篇文章用代码实例讲清楚OOP的核心概念。
5
Java NIO详解
Java NIO的Selector模型让我第一次感受到了单线程处理万级连接的魅力。相比传统IO的阻塞读写,Channel加Buffer的组合简直是为高性能网络编程量身定做。






