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

[网络编程]通过java用TCP实现网络编程

文章目录

  • 一. 通过java用TCP实现网络编程
    • api介绍
    • 代码实现
    • 上述代码存在的问题

一. 通过java用TCP实现网络编程

api介绍

1. ServerSocket
ServerSocket是专门给服务器用的api
构造方法:
在这里插入图片描述
方法:
在这里插入图片描述
2. Socket
不管是客⼾端还是服务端Socket,都是双⽅建⽴连接以后,保存的对端信息,及⽤来与对⽅收发数据

构造方法:
在这里插入图片描述
方法:
在这里插入图片描述

代码实现

服务器:
第一步: 创建对象
在这里插入图片描述

第二步: 实现start
2.1 首先要建立连接
在这里插入图片描述
这个ServerSocket的作用, 其实就是为了连接, 连接完成之后, 返回的是Socket对象, 接下来服务器进行的工作都是Socket完成的
调用start方法后, 如果没有客户端发送请求, 那么就是在accept这里阻塞等待

单独整理一个方法处理连接后的逻辑, 需要循环处理客户端的请求

第三步: 实现processConnection方法

3.1 打印一个日志, 告知服务器当前有客户端连上了
在这里插入图片描述
3.2 从socket获取流对象, 来进一步进行后续操作
在这里插入图片描述
因为TCP是字节流传输, 所以可以使用InputStream, OutputStream来接收客户端的数据, 从而进行读写操作
3.3 读取请求并解析
在这里插入图片描述

  1. 为什么不适用read , 而是使用scanner
    使用read返回的是字节数组, 那么为了后续方便打印, 还需要将字节数组转成String
    而InputStream本身就可以搭配Scanner使用, 此时scanner.next返回的直接是String
  2. if条件判断的含义
    如果客户端终止了, 那么scanner.hasNext返回的就是false, 取反就是true, 此时就表示客户端已经断开连接了, 就可以直接break, 无需执行后面的逻辑了
    如果客户端没有终止, 但是没有发送数据过来, 此时hasNext是阻塞的
    如果发送了数据过来, 那么hasNext返回true, 取反false, 不会进入if中, 就会继续执行后面的逻辑
  3. 但是使用scanner有个弊端, scanner.next这个读取方式, 只有读到"空白符"才会读取完毕, 不然就会一直阻塞, 直到有"空白符"请求为止, 所以就要求客户端在发送数据的时候, 务必要在每个请求的末尾加上空白符

空白符是一类字符的通称, 包括 换行, 回车, 空格, 制表符, 翻页符…

3.4 根据请求计算响应
在这里插入图片描述在这里插入图片描述
由于是回显程序, 直接返回即可
但是要明确给数据加上一个"空白符", 防止阻塞

3.5 把响应写回给客户端
在这里插入图片描述
3.6 打印日志
在这里插入图片描述

第四步: 实现main方法
在这里插入图片描述

完整代码:

public class TcpEchoServer {private ServerSocket serverSocket = null;public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);}public void start() throws IOException {while(true){Socket clientSocket = serverSocket.accept();processConnection(clientSocket);}}private void processConnection(Socket clientSocket) throws IOException {//1. 打印一个日志, 告知说当前有客户端连上了System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());//2. 从socket获取流对象, 来进一步进行后续操作try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()){//3. 读取请求并响应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();//4. 根据请求计算响应String response = process(request);//5. 把响应写回到客户端outputStream.write(response.getBytes(), 0, response.getBytes().length);//6. 服务器打印日志System.out.printf("[%s:%d] res=%s, resp=%s\n", clientSocket.getInetAddress(), clientSocket.getPort(),request,response);}}}private String process(String request) {return request + "\n";}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(9090);server.start();}
}

客户端:

第一步: 创建对象
在这里插入图片描述
这样的构造方式, 就完成了和服务器之间的连接

第二步: 完成start
2.1 准备工作
在这里插入图片描述
打印日志
从socket获取字节流对象, 为后续接收发送数据做准备
用scannerNetwork来接收服务器返回的结果, 不用再进行转字符操作

2.2从控制台读取数据
在这里插入图片描述
2.3 把请求发送给服务器
在这里插入图片描述
因为服务器那边只有接收到"\n"才会停止读取, 所以我们手动加上
使用outputStream.write来发送数据

2.4 从服务器读取响应
在这里插入图片描述
如果服务器断开或者没有连接上服务器, scannerNetwork返回false, 取反true, 就会break
如果连接上了服务器, 就会返回, 用response接收

2.5 把响应显示到控制台上
在这里插入图片描述

第三步: 完成main方法
在这里插入图片描述
完整代码:

public class TcpEchoClient {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(InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()){Scanner scannerNetwork = new Scanner(inputStream);while(true){//1. 从控制台读取数据System.out.println("请输入要发送的数据:");String request = scanner.next();//2. 把请求发送给服务器request += "\n";outputStream.write(request.getBytes());//3. 从服务器读取响应if(!scannerNetwork.hasNext()){break;}String response = scannerNetwork.next();//4. 把响应显示到控制台上System.out.println(response);}}catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);client.start();}}

运行结果:
在这里插入图片描述
在这里插入图片描述

上述代码存在的问题

1. 服务器中, 对于accept创建的socket对象, 没有进行关闭操作
服务器serverSocket是不必关闭的, 因为他的声明周期是跟随整个服务器进程的, 他要一直等待连接
客户端的socket, 也是不必关闭的, 它跟随客户端的生命周期, 客户端结束它才要结束在这里插入图片描述

但是服务器的clientSocket就不可以不关闭了, 因为每个客户端都有对应的clientSocket, 如果用完了不关闭, 就会使当前的clientSocket对应的文件描述附表得不到释放, 引进文件资源泄露

解决办法:
我们可以在processConnection中加入finally或者将clientSocket方法try()中
在这里插入图片描述
在这里插入图片描述
**2. 当前这个代码, 服务器是无法同时给多个客户端提供服务的
在这里插入图片描述
启动多个客户端, 服务器是感知不到的, 只能当上一个客户端终止, 下一个客户端才能连接上
原因:
在这里插入图片描述
我们这个代码, 当一个客户端正在连接时, 此时进入到processConnection方法中, 进行while循环, 如果第二个客户端来了, 是没法执行到accept的
解决办法:
可以使用多线程, 让连接客户端和处理客户端的响应可以一起进行
在这里插入图片描述
注意: 此时就只能在processConnection中close掉clientSocket

服务器完整代码:

public class TcpEchoServer {private ServerSocket serverSocket = null;public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);}public void start() throws IOException {while(true){Socket clientSocket = serverSocket.accept();Thread thread = new Thread(() -> {try {processConnection(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}});thread.start();}}private void processConnection(Socket clientSocket) throws IOException {//1. 打印一个日志, 告知说当前有客户端连上了System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());//2. 从socket获取流对象, 来进一步进行后续操作try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()){//3. 读取请求并响应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();//4. 根据请求计算响应String response = process(request);//5. 把响应写回到客户端outputStream.write(response.getBytes(), 0, response.getBytes().length);//6. 服务器打印日志System.out.printf("[%s:%d] res=%s, resp=%s\n", clientSocket.getInetAddress(), clientSocket.getPort(),request,response);}}finally{clientSocket.close();}}private String process(String request) {return request + "\n";}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(9090);server.start();}
}

相关文章:

[网络编程]通过java用TCP实现网络编程

文章目录 一. 通过java用TCP实现网络编程api介绍代码实现上述代码存在的问题 一. 通过java用TCP实现网络编程 api介绍 1. ServerSocket ServerSocket是专门给服务器用的api 构造方法: 方法: 2. Socket 不管是客⼾端还是服务端Socket,都是双⽅建⽴连接以后&#…...

Python(TensorFlow)和Java及C++受激发射损耗导图

🎯要点 神经网络监督去噪预测算法聚焦荧光团和检测模拟平台伪影消除算法性能优化方法自动化多尺度囊泡动力学成像生物研究多维分析统计物距粒子概率算法 Python和MATLAB图像降噪算法 消除噪声的一种方法是将原始图像与表示低通滤波器或平滑操作的掩模进行卷积。…...

IEEE投稿模板翻译

>将这一行替换为您的稿件id号(双击此处编辑)< IEEE 期刊和会议论文的撰写准备&#xff08;2022&#xff09; 第一作者 A. 作者&#xff0c;IEEE成员&#xff0c;第二作者 B. 作者&#xff0c;第三作者 C. 作者 Jr.&#xff0c;IEEE成员 摘要—本文档为IEEE会刊、期刊和…...

log4j 1.x 日志输出线程以唯一ID的形式配置

在 Log4j 1.x 中&#xff0c;直接以线程ID&#xff08;如Java中的Thread.currentThread().getId()返回的ID&#xff09;的形式记录日志是可行的&#xff0c;但 Log4j 1.x 本身并不直接提供一个内建的、自动将每个线程ID转换为“同一时间段内唯一ID”的机制。线程ID本身在JVM的上…...

宏观学习笔记:GDP分析(二)

GDP分析&#xff08;一&#xff09;主要是介绍GDP相关的定义以及核算逻辑&#xff0c;本节主要介绍GDP的分析思路。GDP分析主要是2种方法&#xff1a;总量分析和结构分析。 1. 总量分析 1.1 数值选择 一般情况下&#xff0c;分析的对象都是 官方公布的GDP当季值。 1.2 趋势规…...

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位&#xff1a;为1时表示在内存期间被访问过&#xff0c;为0时表示未被访问&#xff1b;修改位&#xff1a;为1时表示该页面自从被装入内存后被修改过&#xff0c;为0时表示未修改过。 置换页面时&#xff0c;最先置换访问位和修改位为…...

C高级编程 第十六天(树 二叉树)

1.树 1.1结构特点 非线性结构&#xff0c;有一个直接前驱&#xff0c;但可能有多个直接后继有递归性&#xff0c;树中还有树可以为空&#xff0c;即节点个数为零 1.2相关术语 根&#xff1a;即根结点&#xff0c;没有前驱叶子&#xff1a;即终端结点&#xff0c;没有后继森…...

OpenCV结构分析与形状描述符(11)椭圆拟合函数fitEllipse()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆&#xff0c;该椭圆在最小二乘意义上最好地拟合一组2D点。它返回一个内切椭圆的旋转矩形。使…...

904.水果成篮

题目 链接&#xff1a;leetcode链接 思路分析&#xff08;滑动窗口&#xff09; 读完题目&#xff0c;很明显&#xff0c;这个题目需要我们寻找一个最长子数组&#xff0c;使得这个子数组里面最多存在两种不同的数字&#xff0c;很容易联想到使用滑动窗口。 另外&#xff…...

【网络安全】漏洞挖掘之 2FA 恢复代码安全措施不当

未经许可,不得转载。 文章目录 正文正文 目标:example.com 2024年6月,我在HackerOne上参与一个私人项目时发现了一个与2FA(双因素身份验证)恢复代码管理相关的安全漏洞。该漏洞发生在用户禁用并重新启用2FA的过程中。问题在于,系统在2FA重新启用后,仍然接受此前生成的…...

指令微调与参数微调的代码实践与分析

文章目录 指令微调的实验性分析LoRA 代码实践与分析指令微调的示例代码与预训练的代码高度一致,区别主要在于指令微调数据集的构建(SFTDataset)和序列到序列损失的计算(DataCollatorForSupervisedDataset)。以下代码展示了 LLMBox 和 YuLan-Chat 中指令微调的整体训练流程…...

Android14音频进阶之高通Elite架构指定通道播放(八十四)

简介: CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布:《Android系统多媒体进阶实战》🚀 优质专栏: Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏: 多媒体系统工程师系列【原创干货持续更新中……】🚀 优质视频课程:AAOS车载系统+…...

常见的正则化方法以及L1,L2正则化的简单描述

深度学习中的正则化是通过在模型训练过程中引入某些技术来防止模型过拟合的一种策略。过拟合是指模型在训练数据上表现非常好&#xff0c;但在新的、未见过的数据上表现不佳。正则化通过限制模型的复杂度或对模型参数施加约束&#xff0c;从而提高模型的泛化能力。 常见的正则…...

深入理解 Milvus:新一代向量数据库的基础技术与实战指南

一、什么是 Milvus&#xff1f; Milvus 是一个开源的向量数据库&#xff0c;专门设计用于存储和检索大规模的高维向量数据。无论是图像、视频、音频还是文本&#xff0c;通过将这些数据转换为向量&#xff0c;Milvus 都能通过近似最近邻搜索&#xff08;Approximate Nearest N…...

Maven教程——从入门到入坑

第1章 为什么要使用Maven 1.1 获取第三方jar包   开发中需要使用到的jar包种类繁多&#xff0c;获取jar包的方式都不尽相同。为了查找一个jar包找遍互联网&#xff0c;身心俱疲。不仅如此&#xff0c;费劲心血找到的jar包里有的时候并没有你需要的那个类&#xff0c;又或者有…...

研究生深度学习入门的十天学习计划------第九天

第9天&#xff1a;深度学习中的迁移学习与模型微调 目标&#xff1a; 理解迁移学习的核心概念&#xff0c;学习如何在实际应用中对预训练模型进行迁移和微调&#xff0c;以应对不同领域的任务。 9.1 什么是迁移学习&#xff1f; 迁移学习&#xff08;Transfer Learning&#…...

perl的学习记录——仿真regression

1 记录的背景 之前只知道有这个强大语言的存在&#xff0c;但一直侥幸自己应该不会用到它&#xff0c;所以一直没有开始学习。然而人生这么长&#xff0c;怎就确定自己不会用到呢&#xff1f; 这次要搭建一个可以自动跑完所有case并且打印每个case的pass信息到指定的文件中。…...

【Go】go连接clickhouse使用TCP协议

离开你是傻是对是错 是看破是软弱 这结果是爱是恨或者是什么 如果是种解脱 怎么会还有眷恋在我心窝 那么爱你为什么 &#x1f3b5; 黄品源/莫文蔚《那么爱你为什么》 package mainimport ("context""fmt""log""time&q…...

Emlog-Pro访问网站时需要密码验证插件

插件介绍 EmlogPro访问网站密码验证插件&#xff0c;为你的网站添加输入密码访问网站功能&#xff0c;在应用中的场景往往运用在为内部或是个人使用的页面里面&#xff0c;在访问的时候可以提示输入密码&#xff0c;做隐私保护。 下载地址&#xff1a; Emlog-Pro访问网站时需…...

Apache ShardingSphere数据分片弹性伸缩加解密中间件

Apache ShardingSphere Apache ShardingSphere 是一款分布式 SQL 事务和查询引擎,可通过数据分片、弹性伸缩、加密等能力对任意数据库进行增强。 软件背景 ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...