---随笔--Java实现TCP通信(双端通信接收与发送)

---随笔--Java实现TCP通信(双端通信接收与发送)

    • 引言
    • 1. 什么是TCP通信
    • 2. 服务器与客户端核心代码
      • 2.1 服务器ServerSocket端核心代码
      • 2.2 用户Socket端核心代码
      • 2.3 小贴士之关于try-with-resources自动关闭资源的使用
    • 3. 具体服务器端实现
    • 4. 具体客户端实现
    • 5. 运行效果图
    • 6. 总结

引言

设计一个基于最基本Socket的P2P“聊天软件”(文本互传),要求在同一个网关内可以通过任意常用端口建立会话并进行socket通讯、双向文本收发。

1. 什么是TCP通信

TCP(Transmission Control Protocol),也称传输控制协议,是一种在计算机网络中常用的协议,用于在两个设备之间建立可靠的连接并进行数据交换。它是一种面向连接的协议,意味着通信的两端在交换数据之前必须先建立连接。TCP提供了许多功能,使得数据可以在网络上可靠地传输,包括数据分段、重传机制、流量控制和拥塞控制等。

具体来说,TCP通信的过程通常包括以下几个步骤:

  1. 建立连接(三次握手):通信的两端(客户端和服务器端)首先需要通过三次握手来建立连接。在这个过程中,客户端向服务器发送一个连接请求报文(SYN),服务器收到后回复一个确认报文(SYN-ACK),并确认客户端的连接请求,最后客户端再回复一个确认报文(ACK),确认服务器的确认。这样,连接就建立起来了。

  2. 数据传输:连接建立之后,通信双方就可以开始传输数据。数据会被分成小的数据段,并通过网络传输到接收端。TCP会对数据进行编号,以保证数据的顺序不会被打乱,同时会对数据进行检验,以确保数据的完整性。

  3. 连接终止(四次挥手):当数据传输完成后,通信的两端需要进行连接的关闭。这个过程包括四次挥手:首先,客户端发送一个连接释放报文(FIN),服务器接收到后发送确认报文(ACK),但仍然允许数据传输;然后,服务器发送一个连接释放报文(FIN),客户端收到后发送确认报文(ACK),进入 TIME-WAIT 状态。最后,客户端发送最后一个确认报文(ACK),完成连接的关闭。

关于更详细的三次握手和四次挥手可以参考我的以下文章:–随笔–带你轻松理解TCP中的三次握手,–随笔–带你轻松理解TCP中的四次挥手

2. 服务器与客户端核心代码

自己做了个图,方便大家理解下,看了图再往下看就能很容易理解了:
Java中TCP通信简易流程

2.1 服务器ServerSocket端核心代码

			//绑定监听端口ServerSocket serverSocket = new ServerSocket(port);System.out.println("Server is running, listening on port " + port);while (true) {// 等待并监听客户端的连接请求Socket clientSocket = serverSocket.accept();// 为客户端创建一个线程处理请求new Thread(new ClientHandler(clientSocket)).start();}

这段代码片段建立了一个服务器,它在指定的端口上监听传入的客户端连接。当客户端连接时,它创建一个新的线程来处理该客户端的请求,从而允许并发的客户端-服务器交互。这段代码的主要功能是创建一个能够处理多个客户端连接的多线程服务器。

以上代码主要进行了以下操作:

  1. 初始化ServerSocket:
    ServerSocket serverSocket = new ServerSocket(port);
    在这里,创建了一个名为serverSocket的ServerSocket对象,并将其绑定到特定的端口(port在main方法中预先定义为1234)。这个ServerSocket负责在指定端口上监听传入的客户端连接请求。

  2. 服务器运行反馈:
    System.out.println("Server is running, listening on port " + port);
    这行代码简单地向控制台打印一条消息,指示服务器正在运行并在特定端口上进行监听。

  3. 客户端连接处理循环:
    while (true) {
    Socket clientSocket = serverSocket.accept();
    new Thread(new ClientHandler(clientSocket)).start();
    }

    在代码块中,while循环无限地运行,不断等待并处理客户端连接。

  • serverSocket.accept(): 这是一个阻塞方法,它等待客户端连接到服务器。当检测到客户端连接时,它会返回代表客户端的Socket对象。
  • new Thread(new ClientHandler(clientSocket)).start(): 这一行代码创建一个新的Thread,并将一个ClientHandler实例设置为其目标。ClientHandler负责管理与连接的客户端的交互。当线程启动时,ClientHandler的run方法将独立执行,允许通过多线程同时为多个客户端提供服务。

2.2 用户Socket端核心代码

			//创建指定主机和端口的SocketSocket socket = new Socket(host, port);//获取Socket的输出流和输入流PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));//获取系统标准输入流BufferedReader sysReader = new BufferedReader(new InputStreamReader(System.in));

这段代码用于创建一个 socket 连接到指定的主机和端口,并为与服务器的通信建立输入和输出流。此外,它创建一个读取器,用于从标准输入流中捕获输入,允许用户与客户端程序进行交互。该代码的主要功能是为客户端设置通信基础设施,使其能够发送和接收与服务器的消息。

这段代码是用于创建一个客户端,通过 Socket 连接到服务器。具体步骤如下:

  1. Socket socket = new Socket(host, port)
    创建一个新的 Socket 对象,指定连接的主机和端口。这个 Socket 代表了客户端和服务器之间通信的端点。
  2. PrintWriter writer = new PrintWriter(socket.getOutputStream(), true)
    初始化一个 PrintWriter 对象,用于向 Socket 的输出流写入数据。PrintWriter 类提供了便捷的方法,用于向流中写入各种数据类型。
  3. BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))
    通过初始化 BufferedReader 对象,用于从 Socket 的输入流中读取数据。BufferedReader 类允许从字符输入流中高效地读取字符。
  4. BufferedReader sysReader = new BufferedReader(new InputStreamReader(System.in))
    创建一个 BufferedReader 对象,用于从标准输入流(键盘)中读取输入。这将用于捕获用户输入,并将其发送到服务器

2.3 小贴士之关于try-with-resources自动关闭资源的使用

代码中运用到许多try-with-resources的语法,try-with-resources 是 Java 7 中引入的一个语言特性,用于自动关闭资源。在 Java 中,程序常常需要操作文件、数据库连接、网络连接等资源,这些资源需要在不再需要时及时关闭,以释放系统资源并避免资源泄漏。传统的做法是在 finally 块中关闭资源,但这样的代码容易出错,而且代码量较多。

try-with-resources 语句简化了资源管理的代码,确保在代码块结束后自动关闭资源,无需手动编写 finally 块。其语法结构如下:

	try (ResourceType1 resource1 = new ResourceType1();ResourceType2 resource2 = new ResourceType2();// 可以有多个资源声明,用分号隔开) {// 使用资源的代码块// 资源会在代码块结束后自动关闭} catch (Exception e) {// 异常处理代码块}

即在try后使用小括号,将资源写进小括号中再接大括号,即 try( Resources ){ your code }
需要注意的是,在 try 关键字后面的括号内声明需要管理的资源,这些资源必须实现 java.lang.AutoCloseable 接口或其子接口(例如 java.io.Closeable)。在代码块结束后,Java 会自动调用资源的 close() 方法关闭资源,即使代码块中出现异常也会关闭资源。如果资源的 close() 方法抛出异常,这些异常会被抑制,并且可以通过 Throwable.getSuppressed() 方法获取。

3. 具体服务器端实现

MyServer.class

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;public class MyServer {public static void main(String[] args) {int port = 1234;try {//绑定监听端口ServerSocket serverSocket = new ServerSocket(port);System.out.println("Server is running, listening on port " + port);while (true) {// 等待并监听客户端的连接请求Socket clientSocket = serverSocket.accept();// 为客户端创建一个线程处理请求new Thread(new ClientHandler(clientSocket)).start();}} catch (IOException e) {e.printStackTrace();}}
}class ClientHandler implements Runnable {private Socket clientSocket;private BufferedReader reader;private PrintWriter writer;public ClientHandler(Socket clientSocket) {this.clientSocket = clientSocket;try {this.reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));this.writer = new PrintWriter(clientSocket.getOutputStream(), true);} catch (IOException e) {e.printStackTrace();}}@Overridepublic void run() {try {// 向客户端发送欢迎消息writer.println("Welcome to our Chat! Type anything pressing enter to send message.(Type 'exit' to end the chat)");BufferedReader sysReader = new BufferedReader(new InputStreamReader(System.in));String message;while (true) {if (reader.ready()) {  // 检查是否有消息从客户端发来message = reader.readLine();//监听客户端若输入exit则关闭客户端连接if ("exit".equalsIgnoreCase(message)) {System.out.println("Client exit, System shutdown...");//立即终止JVM,自动释放资源System.exit(0);}System.out.println("Client: " + message);}if (sysReader.ready()) {  // 检查是否有自定义消息要发送给客户端String messageToClient = sysReader.readLine();writer.println(messageToClient);}}} catch (IOException e) {e.printStackTrace();} finally {try {if (clientSocket != null) {clientSocket.close(); // 关闭Socket}} catch (IOException e) {e.printStackTrace();}}}
}

4. 具体客户端实现

MyClient.class

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;public class MyClient {public static void main(String[] args) {String host = "127.0.0.1";int port = 1234;// try-with-resources 语法块自动关闭资源try(//创建指定主机和端口的SocketSocket socket = new Socket(host, port);//获取Socket的输出流和输入流PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));//获取系统标准输入流BufferedReader sysReader = new BufferedReader(new InputStreamReader(System.in));){new Thread(() -> {try {String receivedMessage;while (true) {//立即返回,而不是通过readLine()等待,避免阻塞if (reader.ready()) {receivedMessage = reader.readLine();System.out.println("Server: " + receivedMessage);}}} catch (IOException e) {e.printStackTrace();}}).start();String messageToServer;while (true) {if (sysReader.ready()) {messageToServer = sysReader.readLine();writer.println(messageToServer);//exit命令退出系统if ("exit".equalsIgnoreCase(messageToServer)){System.out.println("You have exited the chat");System.exit(0);}}}} catch (IOException e) {e.printStackTrace();}}
}

5. 运行效果图

服务器运行后接受到客户端发来的消息并回发内容
服务器运行后接受到客户端发来的消息并回发内容
客户端向服务器发送消息并获取服务器返回的消息
客户端向服务器发送消息并获取服务器返回的消息

6. 总结

一开始想着用Java简单实现下TCP并不难,但实际动手后发现需要注意的点还挺多的,要考虑到双方数据的发送和获取,资源的关闭,对话的终结,真正做起来还是花了点时间,但至少强化了记忆也学习到了一些新方法,内心还是挺快乐的,总之还是要多动手多实践,相信你也可以做到一次编写一次过的,加油!
加油

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1421135.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

ONES 功能上新 | 近期产品新功能一览

支持在 ONES Project 中通过弹窗查看、编辑 ONES Wiki 页面。 应用场景: 当需要在 ONES Project 中查看 ONES Wiki 的页面内容时,可以直接点击工作项关联的 ONES Wiki 页面或项目文档组件中的页面,即可在 ONES Project 中通过弹窗查看 ONES W…

计算机毕业设计python+spark知识图谱音乐推荐系统 音乐数据分析可视化大屏 音乐爬虫 LSTM情感分析 大数据毕设 深度学习 机器学习

本科毕业设计(论文)开题报告 课题名称 基于Spark的音乐推荐与数据分析系统 的设计与实现 课题类型 系统设计 学院 大数据与人工智能学院 班级 专业 数据科学与大数据技术 指导教师 职称 学生姓名 学号 重庆工程学院教务处制 1.课…

机器学习中的精确度、召回率、F1分数

精确度与召回率 上图左边为混淆矩阵,四个区域分别为:真阳性(True positive),真阴性(True negative),假阳性(False positive),假阴性(F…

华为学习之旅,创建应用全流程

快速入门 开发准备 对于HarmonyOS应用开发的初学者。通过构建一个简单的具有页面跳转/返回功能的应用(如下) ,快速了解工程目录的主要文件,熟悉HarmonyOS应用开发流程。 基本概念 UI框架 HarmonyOS提供了一套UI开发框架,即方舟开发框架&am…

【python量化交易】qteasy使用教程07——创建更加复杂的自定义交易策略

创建更加复杂的自定义交易策略 使用交易策略类,创建更复杂的自定义策略开始前的准备工作本节的目标继承Strategy类,创建一个复杂的多因子选股策略策略和回测参数配置,并开始回测 本节回顾 使用交易策略类,创建更复杂的自定义策略 …

JCR一区 | Matlab实现1D-2D-GASF-CNN-BiLSTM-MATT的多通道输入数据分类预测

JCR一区 | Matlab实现1D-2D-GASF-CNN-BiLSTM-MATT的多通道输入数据分类预测 目录 JCR一区 | Matlab实现1D-2D-GASF-CNN-BiLSTM-MATT的多通道输入数据分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 Matlab实现1D-2D-GASF-CNN-BiLSTM-MATT的多通道输入数据分类预…

Maven 自动化构建

优质博文:IT-BLOG-CN 一、Maven:是一款服务于 Java平台的自动化构建工具 【1】Maven可以将一个项目按模块划分成不同的工程,利于分工协作; 【2】Maven可以将 jar包保存在自己的中央“仓库”中进行统一管理,有需要使用的工程引用这…

C语言 | Leetcode C语言题解之第85题最大矩形

题目&#xff1a; 题解&#xff1a; int maximalRectangle(char** matrix, int matrixSize, int* matrixColSize) {int m matrixSize;if (m 0) {return 0;}int n matrixColSize[0];int left[m][n];memset(left, 0, sizeof(left));for (int i 0; i < m; i) {for (int j …

重学JavaScript高阶知识点(三)—— 详解Js中的内存管理

详解Js中的内存管理 1. 简介2. 内存生命周期3. JavaScript 的内存分配4. 垃圾回收 1. 简介 很多底层语言一般都有底层的内存管理接口&#xff0c;比如 C语言&#xff0c;可以调用对应的API去创建和释放内存空间。意思是需要手动去创建和释放内存空间&#xff0c;很明显&#x…

mapreduce | 自定义Partition分区(案例2)

1.需求 统计每个手机号消费总金额&#xff0c;按照消费金额降序排序&#xff0c;最终联通、电信、移动分别写入不同的文件。 130、131、132&#xff08;联通&#xff09; 133&#xff08;电信&#xff09; 135、136、137、138、139 &#xff08;移动&#xff09; 手机号,消费记…

基于 Spring Boot 博客系统开发(八)

基于 Spring Boot 博客系统开发&#xff08;八&#xff09; 本系统是简易的个人博客系统开发&#xff0c;为了更加熟练地掌握 SprIng Boot 框架及相关技术的使用。&#x1f33f;&#x1f33f;&#x1f33f; 基于 Spring Boot 博客系统开发&#xff08;七&#xff09;&#x1f…

Vue3实战笔记(19)—封装菜单组件

文章目录 前言一、封装左侧菜单导航组件二、使用步骤三、小彩蛋总结 前言 在Vue 3中封装一个左侧导航菜单组件是一项提升项目结构清晰度和代码可复用性的关键任务。这个过程不仅涉及组件的设计与实现&#xff0c;还需考虑其灵活性、易用性以及与Vue 3新特性的紧密结合。以下是…

Docker:docker在项目中常用的一些命令

简介   Docker 是一个开源的容器化平台&#xff0c;它允许开发者将应用程序及其依赖项打包到一个可移植的容器中&#xff0c;并发布到任何安装了 Docker 引擎的机器上。这些容器是轻量级的&#xff0c;包含了应用程序运行所需的所有东西&#xff0c;如代码、系统库、系统工具…

数据结构与算法===回溯法

文章目录 原理使用场景括号生成代码 小结 原理 回溯法是采用试错的思想&#xff0c;它尝试分步骤的去解决一个问题。在分步骤解决问题的过程中&#xff0c;当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候&#xff0c;它将取消上一步甚至是上几步的计算&#x…

Android 10.0 Launcher3定制folder文件夹2x2布局之二foldericon的2x2的显示布局

1.前言 在10.0的系统rom产品定制化开发中,在对Launcher3的folder文件夹功能定制中,要求folder文件夹跨行显示,就是 2x2布局显示,默认的都是占1格的,现在要求占4格显示,系统默认是不支持显示4格的,所以接下来需要分析相关的 功能,然后来实现这个功能 2.Launcher3定制fo…

递归式--三种求解时间复杂度的方法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、代换法二、递归树法三.主方法总结 前言 学无止境&#xff0c;笔勤不辍。很久没有更新算法专栏了…笔者终于找到时间来更新了。今天&#xff0c;笔者给大家…

基于FPGA音视频矩阵-2K/4K分辨率解决方案

① 单板支持4进4出含4096x2160P30 及以下任意分辨率视频 ② 单板支持HDMI 接口、VGA接口、 DVI接口、光纤接口、SDI 接口、 HDBASET接口 ③ 接口输入分辨率自适应 ④ 接口输出分辨率任意配置 ⑤ 20ms广电级别切换速度以及延迟 ⑥ 图像纯RGB处理&#xff0c;色彩更准确 ⑦…

StarRocks 【新一代MPP数据库】

1、StarRocks 1.1、StarRocks 简介 StarRocks 是新一代极速全场景 MPP (Massively Parallel Processing&#xff0c;MPP数据库是一种基于大规模并行处理技术的数据库系统&#xff0c;旨在高效处理大量数据。) 数据库。StarRocks 的愿景是能够让用户的数据分析变得更加简单和敏…

易图讯三维电子沙盘-大数据处理服务

易图讯科技10名高级大数据工程师&#xff0c;高效、快速进行POI、DEM、高清卫星影像、地形地貌、路网、矢量地图等海量大数据处理服务。 免费专业提供POI、AOI、DEM、高清卫星影像、地形地貌、路网、矢量地图等海量大数据处理服务。 1年更新2次POI、高清卫星影像。

微软或将发布全新AI大模型,欲与GPT-4和Gemini一较高下

科技巨头微软正积极研发一款名为MAI-1的全新大型语言模型&#xff0c;该模型有望与谷歌Gemini、Anthropic的Claude以及OpenAI的GPT-4等顶尖模型展开竞争。 据The Information报道&#xff0c;这是微软自向OpenAI投资超过100亿美元获取其AI模型使用权以来&#xff0c;首次自主研…