Java网络编程入门:TCP Socket通信与文件传输实战

Evan
Evan
发布时间:2026-10-15 | 3人浏览过
原创

Java网络编程主要通过java.net包实现,核心是基于TCP协议的Socket通信。本文梳理从IP地址获取到套接字连接的完整步骤,提供TCP字节流、字符流的双向通信代码,以及文件上传下载的实操示例,助您快速搭建可靠的客户端与服务端交互机制。

1. 网络编程基础与协议对比

网络编程旨在编写运行在多个通过网络连接的设备上的程序。java.net 包提供了低层次的通信细节,让开发者能专注于业务逻辑而非底层通信机制。

Java支持两种主要传输层协议:

  • TCP(传输控制协议):面向连接、可靠、基于字节流。位于IP层之上,保障应用间可靠通信,如HTTP请求。
  • UDP(用户数据报协议):无连接、不可靠。应用程序需自行处理丢失或重复的数据包,适用于对速度要求高且容错率较高的场景。

2. InetAddress类使用指南

InetAddress 类用于表示互联网协议(IP)地址。以下操作常用于Socket编程前的环境确认:

  1. 获取本机地址:调用 InetAddress.getLocalHost() 返回包含主机名和IP的对象。
  2. 解析域名/主机名:使用 InetAddress.getByName("hostname") 获取远程主机的IP信息。
  3. 提取信息:通过 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步:建立连接的标准流程

连接建立遵循严格的握手顺序:

  1. 服务器监听:实例化 ServerSocket(port) 并在指定端口监听。
  2. 等待接入:调用 serverSocket.accept(),此方法会阻塞直到有客户端连接。
  3. 客户端请求:创建 Socket(serverIP, port) 尝试连接服务器。
  4. 连接成功:若连接建立,服务器 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 提升性能并处理字符集。

注意要点:

  1. 使用 newLine() 插入换行符作为逻辑分隔。
  2. 使用 flush() 强制刷新缓冲区,否则数据可能滞留内存。
  3. 接收端使用 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网络编程的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表