[Java EE] 网络编程与通信原理(三):网络编程Socket套接字(TCP协议)
🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀Java EE(94平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
目录
- 1. TCP套接字编程
- 1.1 API介绍
- 1.1.1 ServerSocket
- 1.1.2 Socket
- 1.2 代码实例
- 1.2.1 回显服务器
- 1.2.2 回显客户端
- 1.3 为服务器引入多线程
- 2. 拓展了解:长连接与短连接
1. TCP套接字编程
前面的UDP套接字是基于数据报为基本单位进行传输的,而今天我们要叙述的TCP套接字是基于字节为单位进行传输的.
1.1 API介绍
1.1.1 ServerSocket
本类是创建在服务器端的Socket的API.
- 构造方法:
方法签名 | 方法说明 |
---|---|
ServerSocket(int port) | 创建⼀个服务端流套接字Socket,并绑定到指定端⼝ |
- 常用成员方法
方法签名 | 方法说明 |
---|---|
Socket accept() | 开始接听指定端⼝(创建时绑定的端⼝),有客户端连接后,返回⼀个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待(没有客户端与之进行连接),注意这里返回的数据类型是Socket类型 |
void close() | 关闭此套接字 |
举例说明:买房
有一天一位老哥想要买一套房子,他在马马路牙子上见到了一位西装革履的销售小哥,这时候这位销售小哥就问老哥:“哥,要买房吗?”.这位老哥正好想买套房子,于是跟着小哥去了售楼部.到了售楼部之后,这位小哥一挥手,来了一个Leader风格穿着的小姐姐,哥老哥一桶踢里哐啷地介绍房子.
在这个过程中,ServerSocket的构造方法的作用就好像是这个销售小哥一样,用来揽客.(用来和客户端用端口号建立连接),而这个售楼部的小姐姐就好像ServerSocket中的accept方法一样,在销售小哥揽客之后,接待客户(在与客户端建立连接之后,接通客户端的请求).
1.1.2 Socket
Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服
务端Socket,也就是Socket类在客户端和服务器中都要用.
不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据
的。
- Socket的构造方法
方法签名 | 方法说明 |
---|---|
Socket(String host,int port) | 创建⼀个客户端流套接字Socket,并与对应IP的主机上,对应端⼝的进程建⽴连接 |
[注意] 客户端与服务器进行连接的时候,构造方法本身就可以与指定的服务器进行连接,也就是拨号的过程.
- Socket的常用成员方法
方法签名 | 方法说明 |
---|---|
InetAddress getInetAddress() | 返回套接字所连接的地址 |
InputStream getInputStream() | 返回此套接字的输⼊流 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
这里我们看到,在使用这些成员方法的时候,可以获取到Socket套接字的输入流和输出流,之后我们在客户端这边向着服务器发送请求的时候,使用的是输出流,反之从服务器获取响应的时候,我们用的是输入流,反之,在服务器这边,向着客户端返回响应的时候,使用输出流,接收请求的时候,使用输入流,这里的输入和输出的参照是以自身为参照.后续我们使用read()或者Scanner从InputStream读取数据之后,就可以对数据进行操作,处理好之后,在通过write()操作写回OutputStream.
[注意] 这里使用到的输入流和输出流,我们在之前的文件IO章节提到过.
https://blog.csdn.net/2301_80050796/article/details/138851572?spm=1001.2014.3001.5501
1.2 代码实例
在代码中,我们整体会涉及到三个Socket套接字:
- 服务器ServerSocket
- 服务器Socket,通过这个Socket和客户端提供交换能力
- 客户端Socket,通过这个Socket和服务器提供交换能力
1.2.1 回显服务器
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;public class TCP_Echo_Server {private ServerSocket server = null;//服务器用于操作网卡的Socket//构造方法指定端口号public TCP_Echo_Server(int port) throws IOException {server = new ServerSocket(port);//传入端口号,创建服务器Socket}public void start() throws IOException {System.out.println("服务器启动");while (true){Socket clientSocket = server.accept();//接收客户端请求,返回Socket对象processConnection(clientSocket);//传入accept的请求,处理请求}}private void processConnection(Socket cilentSocket) throws IOException {//打印登录日志System.out.printf("[%s:%d] 客户端上线\n",cilentSocket.getInetAddress(),cilentSocket.getPort());//从获取到的请求中获取到输入流和输出流,由于它们两个需要关闭,所以写在try中try (InputStream inputStream = cilentSocket.getInputStream();OutputStream outputStream = cilentSocket.getOutputStream()){while (true){Scanner scanner = new Scanner(inputStream);//使用Scanner的方式读取输入流中的数据if (!scanner.hasNext()){//如果输入流中没有数据了,就结束循环System.out.printf("[%s:%d] 客户端下线\n",cilentSocket.getInetAddress(),cilentSocket.getPort());break;}String request = scanner.next();//读取字符串String response = process(request);//处理请求,返回响应outputStream.write(response.getBytes());//把响应写回输出流System.out.printf("[%s:%d] req = %s,resp = %s\n",cilentSocket.getInetAddress(),cilentSocket.getPort(),request,response);//打印请求响应日志}}finally {cilentSocket.close();//关闭Socket.每一个客户端请求都有一个Socket,用完就没用了,所以要关闭}}private String process(String request){return request + '\n';//之所以要加上一个换行符,是因为响应写回到客户端要把一大串字节分为若干数据报}public static void main(String[] args) throws IOException {TCP_Echo_Server tcpEchoServer = new TCP_Echo_Server(9090);tcpEchoServer.start();}
}
[注意的几点]
- 注意在accept接听请求的时候,在外层加上while死循环.
- 服务器在通过ServerSocket接听客户端发送来的请求并读取数据之后,需要关闭
cilentSocket
对象,而且为了防止程序在处理请求的过程中出现异常导致程序以外终止,我们还需要把它放入finally中.不关闭就会导致文件资源泄露,是当前的cilentSocket
对象的文件描述符表得不到释放.
我们还是那上面的买房的例子来说明
销售小哥不可能只揽你一个人,他需要不停地从外面揽客,就需要不停有小姐姐来接待,所以需要while死循环.而售楼部的小姐姐也不可能光给你自己提供服务,而是在接待完你之后,还会在接待其他人,所以在接待你之后,就需要close.
- 就像我们前面说的,使用InputStream和OutputStream之后需要关闭,所以我们就需要把输入流和输出流的创建写入try的括号中.
- 这里不使用
read()
读取,读取出来的是字节byte[]
,使用Scanner进行读取的时候,会对字节进行转换,自动转换为字符串. scanner.next()
读取数据的时候,由于我们后边写到的客户端会使用"空白符"来将一大串数据进行分割,每次分割都对应的是一个应用层数据报,所以我们这里在使用next读取的时候,读取到的就是一个个完整的"字节流请求",而不是没有分隔符的"一大坨数据".- 与第5条相同的道理,在返回响应的时候,我们同样要在数据的末尾加上"空白符",以便客户端进行读取.
return request + '\n';
1.2.2 回显客户端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class TCP_Echo_Client {private Socket socket = null;//客户端用于操作网卡的Socketpublic TCP_Echo_Client(String IP,int Port) throws IOException {socket = new Socket(IP,Port);//通过构造方法传入的服务器的IP地址和端口号构造Socket}public void start() throws IOException {System.out.println("客户端启动");Scanner scanner = new Scanner(System.in);//客户端通过控制台输入请求try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()){while (true){System.out.println(">>");String request = scanner.next();//通过Scanner获取到要发送的请求request += '\n';//为请求加上一个空白符,以便服务器通过Scanner.next读取outputStream.write(request.getBytes());//写入输出流Scanner scanner1 = new Scanner(inputStream);//从输入流读取服务器响应String response = scanner1.next();System.out.println(response);}}}public static void main(String[] args) throws IOException {TCP_Echo_Client tcpEchoClient = new TCP_Echo_Client("127.0.0.1",9090);tcpEchoClient.start();}
}
[需要注意的几点]
request += '\n';
这里要在请求的后面加上\n
,这样在服务器读取响应的时候,就是一个个完整的"字节流请求".scanner1.next();
读取"字节流响应"的时候,由于前面服务器使用\n
对数据进行过分割,所以我们在这里就可以使用这样的方式进行读取.
1.3 为服务器引入多线程
上面的代码出现的最大的问题就是,不可以同时给多个客户端提供服务.这其实不是API的问题,是我们代码结构的问题,就是由于我们在上述的代码中出现了死循环嵌套的代码方式:
第一个客户端在被服务器accept()
接听之后,进入processConnection(clientSocket);
方法内部会有while (true)
循环,这就使得服务器在没有等到第一个客户端结束运行之后,上层start()
方法中的死循环无法进行下一个循环接听下一个客户端.所以会出现不支持同时给多个客户端提供服务的现象.
先启动的客户端有响应.
后启动的客户端无响应.
所以我们在服务器的代码中引入了多线程.每accept()
接听一个客户端,就给这个客户端的Socket单独创建一个线程,这样在接听下一个客户端的时候,就不会受上一个客户端的影响.
- 引入多线程
public void start() throws IOException {System.out.println("服务器启动");while (true){Socket clientSocket = server.accept();//接收客户端请求,返回Socket对象Thread thread = new Thread(()->{//使用多线程同时处理多个客户端发来的请求try {processConnection(clientSocket);//传入accept的请求,处理请求} catch (IOException e) {throw new RuntimeException(e);}});thread.start();}}
其中每个创建的新线程为每个客户端的Socket提供服务,而main线程用来循环处理接听操作.
当然我们也可以引入线程池,我们在前面提到过线程池的概念.
https://blog.csdn.net/2301_80050796/article/details/138388735?spm=1001.2014.3001.5501
从下图可以看到,同时启动的两个客户端都有反应:
服务器也有两个客户端的登录日志:
- 引入线程池
public void start() throws IOException {System.out.println("服务器启动");ExecutorService executorService = Executors.newCachedThreadPool();//使用线程池同时处理多个客户端发来的请求while (true){Socket clientSocket = server.accept();//接收客户端请求,返回Socket对象executorService.submit(new Runnable() {@Overridepublic void run() {try {processConnection(clientSocket);//传入accept的请求,处理请求} catch (IOException e) {throw new RuntimeException(e);}}});}}
[注意]
- 这里我们使用线程数量动态变化的线程池创建方式.
Executors.newCachedThreadPool();
,而使用创建固定线程的线程池创建方式不合乎逻辑,我们不可能只给固定几个客户端提供服务. - 这里我们使用线程池的方式,比循环创建线程的方式更加高效,创建线程会涉及到线程的创建和销毁,会有一定的开销,而线程池在用完一个线程之后,就会将这个线程回收回线程池,使得每一个线程更加充分地利用起来.
2. 拓展了解:长连接与短连接
TCP发送数据时,需要先建⽴连接,什么时候关闭连接就决定是短连接还是⻓连接:
短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能⼀次收发
数据。
长连接:不关闭连接,⼀直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可
以多次收发数据。
基于BIO(同步阻塞IO)的长连接会⼀直占⽤系统资源。对于并发要求很⾼的服务端系统来说,这样的消耗是不能承受的。
由于每个连接都需要不停的阻塞等待接收数据,所以每个连接都会在⼀个线程中运⾏。
⼀次阻塞等待对应着⼀次请求、响应,不停处理也就是⻓连接的特性:⼀直不关闭连接,不停的处理
请求。
实际应⽤时,服务端⼀般是基于NIO(即同步非阻塞IO或IO多路复用)来实现长连接,性能可以极大的提升.一个线程在同时管理多个clientSocket.
举例说明:下楼上小摊买饭
一家三口,爸爸妈妈和孩子.三个人要吃不同的饭,第一种方案就是,三个人都下楼,各买各的.就是同步阻塞IO.
第二种方案就是只让爸爸一个人下去买,爸爸就可以在买完一份饭之后,在等待这个饭做出来的途中,再去买另一个饭,这样就会大大提高效率.就是IO多路复用.
相关文章:

[Java EE] 网络编程与通信原理(三):网络编程Socket套接字(TCP协议)
🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏:🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 🧀Java …...
MyBatis懒加载数据(大批量数据处理)
使用范例 Cursor约定使用Iterator去懒加载数据,以时间换空间,非常适合处理通常无法容纳在内存中的数百万个项目查询。如果在 resultMap 中使用集合,则必须使用 resultMap 的 id 列对游标 SQL 查询进行排序(resultOrdered“true”)。 //为了避…...

MySQL--联合索引应用细节应用规范
目录 一、索引覆盖 1.完全覆盖 2.部分覆盖 3.不覆盖索引-where条件不包含联合索引的最左则不覆盖 二、MySQL8.0在索引中的新特性 1.不可见索引 2.倒序索引 三、索引自优化--索引的索引 四、Change Buffer 五、优化器算法 1.查询优化器算法 2.设置算法 3.索引下推 …...

【spring boot+Lazy ORM+mysql】开发一个数据库管理系统实现对应数据库数据查看和修改
【spring bootLazy ORMmysql】开发一个数据库管理系统实现对应数据库数据查看和修改 演示项目地址:http://124.222.48.62:30193/wu-smart-acw-ui/index.html#/login (admin/admin) 功能 用户登录注册新增、编辑数实例新增、编辑数据库信息…...

知识分享:隔多久查询一次网贷大数据信用报告比较好?
随着互联网金融的快速发展,越来越多的人开始接触和使用网络贷款。而在这个过程中,网贷大数据信用报告成为了评估借款人信用状况的重要依据。那么,隔多久查询一次网贷大数据信用报告比较好呢?接下来随小易大数据平台小编去看看吧。 首先&…...

【Day8:JAVA字符串的学习】
目录 1、常用API2、String类2.1 String类的特点2.2 String类的常见构造方法2.3 String类的常见面试题:2.3.1 面试题一:2.3.2 面试题二:2.3.3 面试题三:2.3.4 面试题四: 2.4 String类字符串用于比较的方法2.5 String类字…...

jetcache缓存
1 介绍 是阿里的双极缓存,jvm-->redis-->数据库 文档:jetcache/docs/CN at master alibaba/jetcache GitHub 2 注意事项 使用的实体类一定实现序列化接口定时刷新注解,慎用 它会为每一个key创建一个定时器 :场景为&…...
SQLSyntaxErrorException: FUNCTION dbname.to_timestamp does not exist
由于MySQL数据库高版本(如8.x)中有to_timestamp()函数,低版本中(如5.7.x)没有这个函数,服务运行报错。 自己创建函数实现功能,创建语句如下; DELIMITER // CREATE FUN…...
Borel-Cantelli 引理
翻译自大佬 https://huarui1998.com/Notes/math/borel-cantelli.html 1. 集序列的 lim inf \lim\inf liminf 和 lim sup \lim\sup limsup 类似于定义实数序列 { a k } \{a_k\} {ak} 的 lim inf \lim\inf liminf 和 lim sup \lim\sup limsup, …...
算法训练营第四十一天 | LeetCode 509 斐波那契数列、LeetCode 70 爬楼梯、LeetCode 746 使用最小花费爬楼梯
LeetCode 509 斐波那契数列 这题动规五部曲都定义得比较明确。首先是dp数组下标,题目中给定F(0) 0说明从0开始,dp[i]直接表示F(i)的值即可。递推公式也直接给出了,也给了开头两个作为递推基础的数值作为初始化依据。遍历顺序也指明是从前往…...

网络其他重要协议(DNS、ICMP、NAT)
1.DNS DNS是一整套从域名映射到IP的系统 1.1 DNS背景 TCP/IP中使用IP地址和端口号来确定网络上的一台主机的一个程序,但是IP地址不方便记忆,例如我们想访问百度就会在浏览器中输入baidu.com而不是百度的IP地址。于是人们发明了一种叫主机名的东西, 是…...
利用PyCSP3库(含大量全局约束)进行组合约束建模
文章目录 1. 什么是 PyCSP3 ?2. 安装方法(Windows)2.1 通过 Google_colab 直接运行2.2 通过 pip 进行安装3. 快速入门3.1 声明变量3.2 更新约束3.3 定义目标3.4 常用的全局约束1. 什么是 PyCSP3 ? PyCSP3 是 Python 中的一个库,用于对组合约束问题进行建模,包括 约束满足…...

解决updateByExample时属性值异常的问题(部分属性值没有使用占位符?进行占位,而是变成了属性的名称)
目录 场景简介代码片断实体类 报错信息排查原因解决测试过程解决方案 场景简介 1、程序将mybatis框架升级为3.5.9版本后执行updateByExample方法时报错 代码片断 Condition condition new Condition(MbCcsSessionConfig.class); condition.createCriteria().andEqualTo(&quo…...
[C++][algorithm][Eigen] 基于Eigen实现Softmax函数
1 简介 Softmax函数是机器学习和深度学习中一个非常重要的激活函数,它在多分类问题中尤其关键。Softmax函数能够将一个向量或一组实数转换成概率分布,使得每个元素的值都在0到1之间,并且所有元素的和为1。本博客文章《【Eigen】基于Eigen实现…...

一招教大家,如何移除受保护的excel工作表的编辑权限限制?
有时候,我们打开工作表发现只有部分单元格可以编辑,点击其他单元格都显示“您试图更改的单元格或图标受保护”,既没法正常编辑或下拉填充,也没有办法快捷筛选。这时候我们可以通过输入密码解除保护,就可以正常编辑了。…...

Python 全栈体系【四阶】(五十三)
第五章 深度学习 十二、光学字符识别(OCR) 2. 文字检测技术 2.3 DB(2020) DB全称是Differentiable Binarization(可微分二值化),是近年提出的利用图像分割方法进行文字检测的模型。前文所提…...

民国漫画杂志《时代漫画》第27期.PDF
时代漫画27.PDF: https://url03.ctfile.com/f/1779803-1248635258-b6a842?p9586 (访问密码: 9586) 《时代漫画》的杂志在1934年诞生了,截止1937年6月战争来临被迫停刊共发行了39期。 ps: 资源来源网络!...

图论(四)—最短路问题(Dijkstra)
一、最短路 概念:从某个点 A 到另一个点B的最短距离(或路径)。从点 A 到 B 可能有多条路线,多种距离,求其中最短的距离和相应路径。 最短路径分类: 单源最短路:图中的一个点到其余各点的最短路径…...

用友NC linkVoucher SQL注入漏洞复现
0x01 产品简介 用友NC是由用友公司开发的一套面向大型企业和集团型企业的管理软件产品系列。这一系列产品基于全球最新的互联网技术、云计算技术和移动应用技术,旨在帮助企业创新管理模式、引领商业变革。 0x02 漏洞概述 用友NC /portal/pt/yercommon/linkVoucher 接口存在…...

部署Prometheus + Grafana实现监控数据指标
1.1 Prometheus安装部署 Prometheus监控服务 主机名IP地址系统配置作用Prometheus192.168.110.27/24CentOS 7.94颗CPU 8G内存 100G硬盘Prometheus服务器grafana192.168.110.28/24CentOS 7.94颗CPU 8G内存 100G硬盘grafana服务器 监控机器 主机名IP地址系统配置k8s-master-0…...

React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...

Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...