TCP实现双向通信练习题

1. 客户端代码:Client.java

java 复制代码
package com.xie.javase.net3;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

/**
 * TCP客户端:向服务端发送图片,并接收服务端响应
 */
public class Client {
    public static void main(String[] args) {
        Socket socket = null;
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        BufferedReader br = null;

        try {
            // 1. 获取本机地址对象
            InetAddress localHost = InetAddress.getLocalHost();
            int port = 8888;

            // 2. 创建Socket并连接服务端
            socket = new Socket(localHost, port);
            System.out.println("已连接到服务端:" + socket.getRemoteSocketAddress());

            // 3. 准备发送图片
            bis = new BufferedInputStream(new FileInputStream("D:\\kakaluote.jpg"));
            bos = new BufferedOutputStream(socket.getOutputStream());

            // 4. 读取本地图片并发送到服务端
            byte[] buffer = new byte[1024];
            int readCount;
            while ((readCount = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, readCount);
            }
            bos.flush(); // 强制刷新输出流
            System.out.println("图片发送完成");

            // 5. 半关闭输出流(通知服务端数据发送完毕)
            socket.shutdownOutput();

            // 6. 接收服务端响应
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String response;
            while ((response = br.readLine()) != null) {
                System.out.println("服务端响应:" + response);
            }

        } catch (IOException e) {
            System.err.println("客户端异常:" + e.getMessage());
        } finally {
            // 7. 关闭资源(反向顺序)
            try {
                if (br != null) br.close();
                if (bos != null) bos.close();
                if (bis != null) bis.close();
                if (socket != null) socket.close();
            } catch (IOException e) {
                System.err.println("资源关闭异常:" + e.getMessage());
            }
        }
    }
}

2. 服务端代码:Server.java

java 复制代码
package com.xie.javase.net3;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * TCP服务端:接收客户端图片,保存后发送确认响应
 */
public class Server {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket clientSocket = null;
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        BufferedWriter bw = null;

        try {
            // 1. 创建服务端Socket并监听端口
            int port = 8888;
            serverSocket = new ServerSocket(port);
            System.out.println("服务端已启动,等待客户端连接...");

            // 2. 接受客户端连接
            clientSocket = serverSocket.accept();
            System.out.println("客户端已连接:" + clientSocket.getRemoteSocketAddress());

            // 3. 接收客户端发送的图片
            bis = new BufferedInputStream(clientSocket.getInputStream());
            bos = new BufferedOutputStream(new FileOutputStream("./dog.jpg")); // 保存到当前目录

            byte[] buffer = new byte[1024];
            int readCount;
            while ((readCount = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, readCount);
            }
            bos.flush();
            System.out.println("图片接收完成");

            // 4. 发送响应给客户端
            bw = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
            bw.write("图片已成功接收,大小:" + new File("./kakaluote.jpg").length() + "字节");
            bw.newLine(); // 添加换行符标识消息结束
            bw.flush();

        } catch (IOException e) {
            System.err.println("服务端异常:" + e.getMessage());
        } finally {
            // 5. 关闭资源(反向顺序)
            try {
                if (bw != null) bw.close();
                if (bos != null) bos.close();
                if (bis != null) bis.close();
                if (clientSocket != null) clientSocket.close();
                if (serverSocket != null) serverSocket.close();
            } catch (IOException e) {
                System.err.println("资源关闭异常:" + e.getMessage());
            }
        }
    }
}

关键代码解析

客户端
  1. socket.shutdownOutput()

    • 半关闭输出流,通知服务端数据发送完毕,但保持输入流开启以接收响应。
    • 避免服务端的bis.read()无限阻塞。
  2. 响应接收逻辑

    • 使用BufferedReader.readLine()读取服务端文本响应,需服务端发送换行符。
服务端
  1. 图片保存路径

    • new FileOutputStream("./kakaluote.jpg"):将文件保存到项目根目录。
  2. 响应消息优化

    • 动态计算接收文件大小,增强反馈信息:
      new File("./kakaluote.jpg").length()

执行流程

  1. 启动服务端

    复制代码
    服务端已启动,等待客户端连接...
  2. 启动客户端

    复制代码
    已连接到服务端:/127.0.0.1:8888
    图片发送完成
    服务端响应:图片已成功接收,大小:204800字节
  3. 服务端输出

    复制代码
    客户端已连接:/127.0.0.1:52345
    图片接收完成

注意事项

  1. 文件路径

    • 客户端确保D:\\kakaluote.jpg存在。
    • 服务端需要项目根目录有写入权限。
  2. 流关闭顺序

    • 先关闭外层流(如BufferedWriter),再关闭内层流(如OutputStream)和Socket。
  3. 异常处理

    • 打印具体异常信息,而非直接抛出RuntimeException,便于调试。

扩展建议

  1. 多线程服务端

    使用线程池处理多个客户端同时上传:

    java 复制代码
    ExecutorService pool = Executors.newCachedThreadPool();
    while (true) {
        Socket socket = serverSocket.accept();
        pool.execute(() -> handleClient(socket));
    }
  2. 进度显示

    在客户端和服务端添加传输进度百分比计算:

    java 复制代码
    File file = new File("D:\\kakaluote.jpg");
    long totalSize = file.length();
    long transferred = 0;
    while ((readCount = bis.read(buffer)) != -1) {
        bos.write(buffer, 0, readCount);
        transferred += readCount;
        System.out.printf("传输进度:%.2f%%\n", (transferred * 100.0 / totalSize));
    }
相关推荐
来一杯龙舌兰4 小时前
【Kubernetes】从零搭建K8s集群:虚拟机环境配置全指南(DNS/网络/防火墙/SELinux全解析一站式配置图文教程)
linux·网络·kubernetes
2501_915918414 小时前
还原线上 WebView 异常:手机端APP远程调试
websocket·网络协议·tcp/ip·http·网络安全·https·udp
chengbo_eva5 小时前
Eventsource vs Websocket vs Socket.io
网络·websocket·网络协议
海域云赵从友7 小时前
中泰制造企业组网新方案:中-泰企业国际组网专线破解泰国工厂访问国内 OA/ERP 卡顿难题
网络
夜空晚星灿烂9 小时前
C# 网络编程-关于HTTP/HTTPS的基础(一)
网络协议·http·https
heart000_19 小时前
如何用 eBPF 实现 Kubernetes 网络可观测性?实战指南
网络·云原生·容器·kubernetes
杰克逊的日记9 小时前
什么是RoCE网络技术
运维·服务器·网络·roce
2501_9159214312 小时前
使用Charles抓包工具提升API调试与性能优化效率
websocket·网络协议·tcp/ip·http·网络安全·https·udp
兴达易控13 小时前
汇川IS620N伺服驱动器如何通过etherCAT主站转profinet网关与西门子1200plc通讯
网络协议
AnalogElectronic13 小时前
openssl 自签证书生成步骤
网络协议