【Java】TCP网络编程:从可靠传输到Socket实战

【Java】TCP网络编程:从可靠传输到Socket实战

活动发起人@小虚竹 想对你说:

这是一个以写作博客为目的的创作活动,旨在鼓励大学生博主们挖掘自己的创作潜能,展现自己的写作才华。如果你是一位热爱写作的、想要展现自己创作才华的小伙伴,那么,快来参加吧!我们一起发掘写作的魅力,书写出属于我们的故事。我们诚挚邀请你参加为期14天的创作挑战赛!

提醒:在发布作品前,请将不需要的内容删除。

 各位看官,大家早安午安晚安呀~~~

如果您觉得这篇文章对您有帮助的话

欢迎您一键三连,小编尽全力做到更好
欢迎您分享给更多人哦


今天我们来学习【Java】TCP网络编程:从可靠传输到Socket实战

目录

1.首先我们再说一下TCP和UDP的区别和相同点

2.连接:通信双方都会记录对方的信息

3.主要是两个api ServerSocket和Socket

4.TCP服务端实战代码演示

5.TCP客户端实战代码演示


TCP的socket的api的差异很大,但是和前面的IO有很大的关联

1.首先我们再说一下TCP和UDP的区别和相同点

1.TCP是有连接的,UDP无连接(这一点可以在代码中体现)

2.TCP是面向字节流流的,UDP是面向数据报的

3.TCP是可靠传输的,UDP是不可靠传输的(这一点在代码中体现不出来)

4.TCP和UDP都是全双工的

2.连接:通信双方都会记录对方的信息

UDP:每次发送数据报都要指定对方的地址(UDP没有存储这个信息)

一张图



TCP:不需要(不过需要内核自动和客户端建连接(TCP的三次握手,后面我会进行讲解)这个过程是系统内核自动完成的)

对于应用程序来说,客户端是发起“建立连接”



服务器这边:把内核中建立好的连接拿到应用程序里面()

这个ServerSocket只负责绑定端口号,然后通过accept方法 把建立好的连接拿过来(但是一瞬间有很多连接的话,就像生产者消费者模型里面只能进行阻塞等待)

3.主要是两个api ServerSocket和Socket

ServerSocket是给服务器用的类,使用这个类用来绑定端口号(这个类负责把系统内核里面已经建立好的连接从队列里面拿过来,)

Socket:既会给服务器使用的类,也会给客户端使用,通过socket这个对象和客户端进行交互

(这两个类都是用来表示socket文件的,抽象了网卡这样的硬件设备)

4.TCP服务端实战代码演示

import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class TcpEchoServer { // 读取用Scanner ,发送用PrintWriter // 1.首先serverSocket调用系统API把连接拿过来,然后交给socket // 我们就有了socket这个对象,这个时候我们就可以宣布这个客户端成功和我们服务器建立了联系并且我们拿到了 // 2.然后我们通过字节流把数据从socket抽象的文件里面读取到,用try包起来,用scanner从流里面读取 // 3. 我们拿到字符串进行响应 // 4. 把返回的字符串通过字节流(我们的字符串通过字符流转换成字节流)写回去 // 5. 把信息打印出来 private ServerSocket serverSocket = null; public TcpEchoServer(int serverPort) throws IOException { // 利用这个系统API从内核中取到已经建立好的连接 // 这个和客户端构造的socket完全不一样,客户端的那个是我们已经拿到的连接 serverSocket = new ServerSocket(serverPort); // 服务器自己分配端口号, } public void start() throws IOException { System.out.println("服务器启动"); // 把队列里面建立好的连接拿过来 while(true){ // 要一直不断地从那个内核里面不断地拿我们已经建立好的连接!!! Socket Clientsocket = serverSocket.accept(); /* //创建一个新的线程把这个请求进行响应 Thread t = new Thread(() ->{ possessCollection(Clientsocket); }); t.start();*/ // 但是线程池是更好一点点的选择 ExecutorService service = Executors.newCachedThreadPool(); service.submit(() ->{ possessCollection(Clientsocket); }); } } public void possessCollection(Socket Clientsocket){ // System.out.printf("[%s,%d] 客户端上线\n" , Clientsocket.getInetAddress(),Clientsocket.getPort()); // 把端口号和IP拿到 try( InputStream inputStream = Clientsocket.getInputStream(); // 这里要用 " ; " OutputStream outputStream = Clientsocket.getOutputStream()){ // 客户可能等会还会继续发送请求(我们循环处理) Scanner scanner = new Scanner(inputStream);//每一次读一次缓冲区,缓冲区里面的东西就少一次,都被我读出来了嘛 while(true){ if(!scanner.hasNext()){// 用户不再输入的时候,就直接跳出循环!!!,一直等待用户输入 System.out.printf("[%s,%d] 客户端下线\n" , Clientsocket.getInetAddress(),Clientsocket.getPort()); break; } String request = scanner.next(); // 拿到字符串进行响应 String response = process(request);// 拿到响应 //4. 拿到字符串的响应,然后我们通过字符流转字节流传递出去 PrintWriter printWriter = new PrintWriter(outputStream); printWriter.println(response); // 此处的println不是写到控制台了,而是写到outputStream的流对象了,也就是写入到ClientSocket里面了 // 自然这个数据也是通过网卡发出去了 printWriter.flush();// 再刷新一下缓存,防止没有发出去, // 总结: 发送和接收数据都是通过socket文件的字节流输入输出来实现,用scanner读字节流,用printWriter写字符串 // 5.打印一下交互过程 System.out.printf("[%s,%d] req=%s resp=%s\n",Clientsocket.getInetAddress(),Clientsocket.getPort(), request,response); } }catch(IOException e){ e.printStackTrace(); }finally{ try { Clientsocket.close(); } catch (IOException e) { throw new RuntimeException(e); } } } private String process(String request) { // 回显服务器 return request; } public static void main(String[] args) throws IOException { TcpEchoServer tcpEchoServer = new TcpEchoServer(9090); tcpEchoServer.start(); } } 

但是这里面会出现两个问题

问题一:为什么我们的ServerSocket对象没有进行close操作,但是Socket对象却需要close操作呢?这样不会出现文件资源泄露吗?

首先我们需要知道什么时候回造成文件资源泄露?

直频繁申请但是一直不释放就会(什么文件的表项啥的)

但是ServerSocket对象从头到尾只创建过一次对象,而且一直在把内核中建立的连接拿到。所以说ServerSocket这个对象的生命周期是是伴随着进程消失的(因此不需要特地的进行回收,等到进程解释JVM会把这个进程里面的东西一起回收了)


但是?

问题二:等待我们写完客户端的代码之后进行讲述

如果启动多个客户端和服务器进行连接(就不行了,这个服务器一直在等待客户端进行输入,我们就需要多个线程并发拿到这个连接)

但是频繁地创建和销毁线程也会有很大开销,线程池是更好一点点选择

5.TCP客户端实战代码演示

import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class TcpEchoClient { /** * 1.随机分配一个端口的发出信息(我们要把服务器的IP地址和端口号给搞进去) * 2.我们循环输入一个字符串,把这个字符串用字符流转换成字节流写到socket抽象的文件里面 * 3.然后我们接收响应(不像服务器,我们又不需要进行处理) * 4.我们直接通过Scanner把字节流里面的内容读出来就好了 */ private Socket socket = null; public TcpEchoClient(String serverIP,int serverPort) throws IOException { socket = new Socket(serverIP,serverPort); } public void start(){ System.out.println(" -> "); Scanner scanner = new Scanner(System.in); try(OutputStream outputStream = socket.getOutputStream(); InputStream inputStream = socket.getInputStream()){ while(true){ /* if(!scanner.hasNext()){ // 用户不想输入了,就直接退出了 break; 不需要这个 }*/ // * 2.我们循环输入一个字符串,把这个字符串用字符流转换成字节流写到socket抽象的文件里面 String request = scanner.next(); PrintWriter printWriter = new PrintWriter(outputStream); // 这些流尽量都放到try()里面 printWriter.println(request); printWriter.flush();// 一定不要忘记刷新缓存区' // * 4.我们直接通过Scanner把字节流里面的内容读出来就好了 Scanner scannerNetwork = new Scanner(inputStream); // 这个Scanner尽量也是放到try()里面 String response = scannerNetwork.next(); System.out.println(response); } }catch(IOException e){ e.printStackTrace(); } } public static void main(String[] args) throws IOException { TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1",9090); tcpEchoClient.start(); } } 
上述就是【Java】TCp网络编程:TCP网络编程:从可靠传输到Socket实战的全部内容啦

能看到这里相信您一定对小编的文章有了一定的认可。

有什么问题欢迎各位大佬指出
欢迎各位大佬评论区留言修正~~


您的支持就是我最大的动力​​​!!!

Read more

Flutter 组件 reaxdb_dart 适配鸿蒙 HarmonyOS 实战:响应式 NoSQL 数据库,构建高性能本地持久化与分布式状态同步架构

Flutter 组件 reaxdb_dart 适配鸿蒙 HarmonyOS 实战:响应式 NoSQL 数据库,构建高性能本地持久化与分布式状态同步架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 reaxdb_dart 适配鸿蒙 HarmonyOS 实战:响应式 NoSQL 数据库,构建高性能本地持久化与分布式状态同步架构 前言 在鸿蒙(OpenHarmony)生态迈向全场景协同、涉及极高频率的端侧数据持久化、实时 UI 数据绑定及分布式节点状态同步的背景下,如何实现一套既能保障数据“强一致性”、又能提供毫秒级检索性能且具备天然“响应式(Reactive)”特性的本地存储引擎,已成为决定应用交互流畅度与底层架构灵活性。在鸿蒙设备这类强调 AOT 极致效能与沙箱存储严格隔离的环境下,如果应用依然依赖臃肿的传统 SQL 或非响应式的 Key-Value,由于由于由于由于 UI 与数据库间的频繁轮询,极易由于由于“数据 IO 阻塞”导致鸿蒙应用在高并发读写时发生明显的帧率抖动。 我们需要一种能够解耦存储逻辑、支持 Stream 级变更监听且具备高性能二进制序列化架构的嵌入式

By Ne0inhk
深入解析nanobot的原理与架构

深入解析nanobot的原理与架构

一、nanobot 是什么?一句话概括 nanobot 是一个超轻量级 AI Agent(智能体)框架: 用约 4000 行 Python 代码,实现了一个能接入多平台聊天软件、支持多 LLM、带记忆和工具系统的“个人 AI 助手”,代码量只有同类项目 Clawdbot / OpenClaw 的 1% 左右。 二、整体架构:从聊天窗口到 LLM 的“神经中枢” 先看一张整体架构示意图: 外部世界 LLM Providers Agent Core 核心引擎 消息总线 Channels 通道层 用户侧 Telegram 飞书 Feishu Discord WhatsApp

By Ne0inhk
Spring Boot 后端分层开发实战:从 MVC 到三层架构详解

Spring Boot 后端分层开发实战:从 MVC 到三层架构详解

应用分层 通过上面的练习,我们学习了 Spring MVC 简单功能的开发,但是我们也发现了一些问题。目前我们程序的代码有点 “杂乱”,然而当前只是 “一点点功能” 的开发。如果我们把整个项目功能完成呢?代码会更加的 “杂乱无章”(文件乱,代码内容乱)。 也基于此,咱们接下来学习应用分层。类似公司的组织架构:公司初创阶段,一个人身兼数职,既做财务,又做人事,还有行政。随着公司的逐渐壮大,会把岗位进行细分,划分为财务部门,人事部门,行政部门等。各个部门内部还会再进行细分。 项目开发也是类似,最开始功能简单时,我们前后端放在一起开发,随着项目功能的复杂,我们分为前端和后端不同的团队,甚至更细粒度的团队。后端开发也会根据功能再进行细分。MVC 就是其中的一种拆分方式。但是随着后端人员不再涉及前端,后端开发又有了新的分层方式。 4.1 介绍 阿里开发手册中,关于工程结构部分,定义了常见工程的应用分层结构: 那么什么是应用分层呢?应用分层是一种软件开发设计思想,

By Ne0inhk
Flutter 组件 whitecodel_auto_link 适配鸿蒙 HarmonyOS 实战:交互式文本探针,构建信息流自动链接识别与极速预览架构

Flutter 组件 whitecodel_auto_link 适配鸿蒙 HarmonyOS 实战:交互式文本探针,构建信息流自动链接识别与极速预览架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 whitecodel_auto_link 适配鸿蒙 HarmonyOS 实战:交互式文本探针,构建信息流自动链接识别与极速预览架构 前言 在鸿蒙(OpenHarmony)生态迈向深度社交、企业办公及即时通讯全场景覆盖的背景下,如何将枯燥的长文本转化为具备可交互能力的“信息枢纽”,已成为提升用户操作效率的关键。在鸿蒙设备这类强调分布式协同与智慧感知的移动终端上,如果应用仅能显示纯文本,而无法识别其中的网址(URL)、邮箱(Email)或电话(Phone),用户就必须通过复杂的“长按、复制、切换应用、粘贴”链路来处理信息,这极大地割裂了鸿蒙系统的流转体验。 我们需要一种能够自动扫描文本特征、支持多维热点识别且具备高性能渲染能力的富文本处理引擎。 whitecodel_auto_link 为 Flutter 开发者引入了极其简便的长文本自动链接方案。它通过内置的高精度正则匹配矩阵,自动将文本中的特定识别域转化为可点击的高亮区域。在适配到鸿蒙

By Ne0inhk