当前位置: 首页 > article >正文

TCP实现双向通信练习题

1. 客户端代码:Client.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

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. 多线程服务端
    使用线程池处理多个客户端同时上传:

    ExecutorService pool = Executors.newCachedThreadPool();
    while (true) {Socket socket = serverSocket.accept();pool.execute(() -> handleClient(socket));
    }
    
  2. 进度显示
    在客户端和服务端添加传输进度百分比计算:

    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));
    }
    
http://www.lryc.cn/news/2384620.html

相关文章:

  • PCIe学习笔记(3)链路初始化和训练
  • Python爬虫(35)Python爬虫高阶:基于Docker集群的动态页面自动化采集系统实战
  • 运维打铁:生产服务器用户权限管理方案全解析
  • 华为云Astro前端页面数据模型选型及绑定IoTDA物联网数据实施指南
  • 【工具类】常用的工具类——CollectionUtil
  • Oracle 11g导出数据库结构和数据
  • 零基础设计模式——创建型模式 - 抽象工厂模式
  • 解锁内心的冲突:神经症冲突的理解与解决之道
  • JVM—Java对象
  • Redisson读写锁和分布式锁的项目实践
  • Https流式输出一次输出一大段,一卡一卡的-解决方案
  • SkyWalking高频采集泄漏线程导致CPU满载排查思路
  • 【HarmonyOS 5】Map Kit 地图服务之应用内地图加载
  • ld: cpu type/subtype in slice (arm64e.old) does not match fat header (arm64e)
  • sentinel核心原理-高频问题
  • 通过vue-pdf和print-js实现PDF和图片在线预览
  • RxJS 核心操作符详细用法示例
  • 视频监控管理平台EasyCVR结合AI分析技术构建高空抛物智能监控系统,筑牢社区安全防护网
  • 2.2.1 05年T1复习
  • Python-11(集合)
  • 钉钉开发之AI消息和卡片交互开发文档收集
  • JMeter 教程:正则表达式提取器提取 JSON 字段数据
  • Opixs: Fluxim推出的全新显示仿真模拟软件
  • [数据集]无人机视角检测分割数据集合集
  • 佰力博与您探讨PVDF薄膜极化特性及其影响因素
  • C++ std::find() 函数全解析
  • 自动获取ip地址安全吗?如何自动获取ip地址
  • STM32:深度解析RS-485总线与SP3485芯片
  • 亚马逊搜索代理: 终极指南
  • QGraphicsView界面的坑(fitInView()函数没反应)