Java网络编程入门:TCP Socket通信与文件传输实战
Java网络编程主要通过java.net包实现,核心是基于TCP协议的Socket通信。本文梳理从IP地址获取到套接字连接的完整步骤,提供TCP字节流、字符流的双向通信代码,以及文件上传下载的实操示例,助您快速搭建可靠的客户端与服务端交互机制。
1. 网络编程基础与协议对比
网络编程旨在编写运行在多个通过网络连接的设备上的程序。java.net 包提供了低层次的通信细节,让开发者能专注于业务逻辑而非底层通信机制。
Java支持两种主要传输层协议:
- TCP(传输控制协议):面向连接、可靠、基于字节流。位于IP层之上,保障应用间可靠通信,如HTTP请求。
- UDP(用户数据报协议):无连接、不可靠。应用程序需自行处理丢失或重复的数据包,适用于对速度要求高且容错率较高的场景。
2. InetAddress类使用指南
InetAddress 类用于表示互联网协议(IP)地址。以下操作常用于Socket编程前的环境确认:
- 获取本机地址:调用
InetAddress.getLocalHost()返回包含主机名和IP的对象。 - 解析域名/主机名:使用
InetAddress.getByName("hostname")获取远程主机的IP信息。 - 提取信息:通过
getHostAddress()获取IP字符串,getHostName()获取主机名。
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddressTest {
public static void main(String[] args) throws UnknownHostException {
// 获取本机地址
InetAddress local = InetAddress.getLocalHost();
System.out.println(local); // 输出: hostname/192.168.x.x
// 解析域名
InetAddress host = InetAddress.getByName("www.baidu.com");
System.out.println(host);
System.out.println(host.getHostAddress());
System.out.println(host.getHostName());
}
}
3. TCP Socket 连接原理
Socket是TCP通信的端点。ServerSocket用于服务器监听端口并等待连接,Socket用于客户端发起连接或服务端接受连接后的具体会话。
第1步:建立连接的标准流程
连接建立遵循严格的握手顺序:
- 服务器监听:实例化
ServerSocket(port)并在指定端口监听。 - 等待接入:调用
serverSocket.accept(),此方法会阻塞直到有客户端连接。 - 客户端请求:创建
Socket(serverIP, port)尝试连接服务器。 - 连接成功:若连接建立,服务器
accept()返回新的Socket对象,该对象即为具体的通信通道。
连接建立后,两端通过各自的输入流和输出流进行双向数据传输。
4. TCP 通信实战编码
实际开发中常需区分字节流和字符流,并注意通信结束标志以避免死锁。
第2步:TCP 字节流通信(简单收发)
目的:验证基础的TCP连通性。注意双方需正确关闭资源。
服务端代码:
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ByteStreamServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = serverSocket.accept(); // 阻塞等待
InputStream in = socket.getInputStream();
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) != -1) {
System.out.println(new String(buf, 0, len));
}
// 务必按顺序关闭资源
in.close();
socket.close();
serverSocket.close();
}
}
客户端代码:
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class ByteStreamClient {
public static void main(String[] args) throws Exception {
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
OutputStream out = socket.getOutputStream();
out.write("Hello Server".getBytes());
out.close();
socket.close();
System.out.println("客户端退出!");
}
}
第3步:实现双向回声通信(关键点:结束标记)
目的:实现客户端发一句,服务端回一句。如果不设置结束标记,read() 将一直等待新数据进入,导致双方僵持。
服务端:先读取客户端消息,写入回消息后,必须调用 socket.shutdownOutput() 通知客户端流已关闭。
// 服务端关键片段
OutputStream out = socket.getOutputStream();
out.write("hello, client".getBytes());
socket.shutdownOutput(); // 发送结束标记
客户端:发送消息后,同样调用 shutdownOutput(),然后开启读线程或阻塞等待服务端响应,直到读取到 -1。
第4步:基于字符流的交互(带换行识别)
目的:处理文本数据。利用 BufferedReader 和 BufferedWriter 结合转换流 InputStreamReader/OutputStreamWriter 提升性能并处理字符集。
注意要点:
- 使用
newLine()插入换行符作为逻辑分隔。 - 使用
flush()强制刷新缓冲区,否则数据可能滞留内存。 - 接收端使用
readLine()配合shutdownOutput()判断单行数据的结束。
// 客户端字符流示例
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("hello, server");
bw.newLine();
bw.flush();
socket.shutdownOutput();
// 读取响应...
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(br.readLine());
5. 网络文件上传与下载实战
文件传输本质是大量字节流的读写。为避免内存溢出,推荐使用缓冲流 (BufferedInputStream/BufferedOutputStream) 分块传输,但为了简化理解,本例采用一次性加载字节数组的方式(仅适用于小文件)。
第5步:文件上传(客户端 -> 服务端)
逻辑:**客户端读取本地文件转为字节数组,通过Socket输出流发送;服务端接收后写入磁盘。
// 服务端接收文件
ServerSocket server = new ServerSocket(8888);
Socket socket = server.accept();
// 假设有一个工具类 StreamUtils 将流转为byte[]
byte[] data = StreamUtils.streamToByteArray(socket.getInputStream());
FileOutputStream fos = new FileOutputStream("upload.jpg");
fos.write(data);
fos.close();
socket.close();
server.close();
第6步:文件下载(服务端 -> 客户端)
逻辑:**客户端发送文件名给服务端,服务端根据文件名查找文件,读取为字节流发送回客户端,客户端写盘。
// 客户端下载文件
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
OutputStream os = socket.getOutputStream();
os.write("target_file.jpg".getBytes());
socket.shutdownOutput(); // 通知服务端文件名发送完毕
// 接收服务端返回的文件字节流
InputStream is = socket.getInputStream();
byte[] fileData = StreamUtils.streamToByteArray(is);
FileOutputStream fos = new FileOutputStream("downloaded.jpg");
fos.write(fileData);
fos.close();
socket.close();
6. 常见问题排错
- Connection Refused:检查目标IP和端口是否正确,服务端是否已启动且在监听,是否有防火墙拦截。
- 程序卡死在读流位置:未调用
shutdownOutput()或未读取完所有数据导致阻塞。 - 中文乱码:
new String(byte[])默认使用平台默认编码,建议显式指定 UTF-8,或使用InputStreamReader(is, StandardCharsets.UTF_8)。
以上就是Java网络编程TCP Socket通信与文件上传下载的详细内容,更多关于Java网络编程的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









