【JavaEE初阶 — 网络编程】TCP流套接字编程

      c96f743646e841f8bb30b2d242197f2f.gif

ddb5ae16fc92401ea95b48766cb03d96.jpeg692a78aa0ec843629a817408c97a8b84.gif


    TCP流套接字编程    


    1. TCP & UDP 的区别    


TCP 的核心特点是面向字节流,读写数据的基本单位是字节 byte 


    2 API介绍    


    2.1 ServerSocket    


     定义     


ServerSocket 是创建 TCP 服务端 Socket 的API。


     构造方法     


方法签名
 
方法说明
 
ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口

所以服务器启动,需要绑定端口号


     方法      


方法签名
 
方法说明
 
 Socket accept()
 
开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待
 
 void close() 
 
关闭此套接字


    2.2  Socket    


  • Socket 是客户端 Socket ,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket。

  • 不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。

     构造方法     


方法签名方法说明
Socket(String host, int port)
 
创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接

这个方法的两个参数,都是服务器的IP & 端口,这个版本的构造方法,就是给客户端用的,服务器怎么通过这个类来构造对象呢?后续再来看;


     方法      


方法签名方法说明
InetAddress getlnetAddress()
 
返回套接字所连接的地址
 
InputStream getlnputStream()
 
返回此套接字的输入流
 
OutputStream getOutputStream()
 
返回此套接字的输出流

TCP没有 send(),receive() 这样的操作,但是 TCP 调用 getlnputStream() 会得到个 InputStream 对象;调用 getOutputStream(),得到一个 OutputStream 对象,这两个对象是字节流对象

虽然 Socket 自身没有读写操作,但是 Socket 可以拿到字节流对象,就可以通过字节流对象,来进行读写操作;


     3. 通过TCP实现回显服务器        


     TCP Echo Server    


    创建关联对象    


通过构造方法,绑定关联的端口号 (和 UDP类似,都是在构造对象的时候,绑定端口号)


    实现 start()    


    处理客户端发送的连接    


TCP 和 UDP 服务器 start() 的主循环的第一步有所区别:


  • UDP 进入主循环,就可以直接处理请求,根据请求计算响应,把响应返回客户端;
  • TCP服务器 ,进入主循环后,因为 TCP 是有连接的,所以第一步是先处理客户端发来的连接;
  • 这个连接就类似于打电话,在客户端打电话给服务器时,服务器要先接通电话,才可以进行后续的正常通信;

所以TCP服务器进入主循环的第一步,就是进行接通电话的操作,而拨号操作,是客户端来完成的;

调用 ServerSocket 对象底下的 accept() 方法,起到接听电话的作用;


还需要接收 accept() 方法的返回值:

  • 如果客户端和服务器确实已经建立连接了,那 accept() 是可以拿到这个请求连接的;
  • 如果客户端没有发起连接,那么 accept() 就会产生阻塞 ,和前面的 receive() 类似;

TCP 服务器后续通过对clientSocket 进行读写数据,来和客户端进行通信;


    进一步理解 ServerSocket 和 Socket 的职责划分    


    处理一个客户端的连接        

处理连接的过程比较复杂,因此我们把这个操作封装成一个方法;

可能会涉及到多个客户端的请求和响应,如果服务器接收到多个请求,就要返还给客户端多个响应;


    服务器与客户端成功连接,打印日志: 

这两个方法可以拿到对端(客户端)的 IP & 端口号;


    获取输入流对象&输出流对象     

打印出客户端的IP&端口号后,就需要进一步地处理客户端的请求和响应,需要借助 Socket 类内置的 InputStream & OutputStream 来处理这些请求和响应

这里获取到的是输入流对象,后续提供这个对象,来读取客户端的请求;


接下来获取输出流对象,并且处理异常:


拿到输入流对象&输出流对象后,后续读取请求,就使用输入流对象;返回响应,就把响应的内容写入输出流对象中; 


接下来,在 try 的代码块中,实现读取请求和返回响应的操作,这些操作分成三步

  1. 读取请求并解析 
  2. 根据请求计算响应
  3. 返回响应给客户端

因为在一次连接中,这三个操作可能会解析多次,所以我们提供 while 循环来处理


    读取请求    

下列操作,读取到的请求是一个字节数组,还需要手动把字节数组再转成字符串,才方便后续的处理和打印:


我们可以借助 Scanner 来进行更简单直接的读取操作,既可以读请求,读出来的请求又已经是一个字符串;

 

  • 把刚刚从 Socket 中拿到的 InputStream 填入 Scanner 中,后续通过 Scanner 直接读取请求中的内容;
  • 如果 Scanner 没有再读取到数据,说明连接断开,就可以结束循环了

所以读取请求,可以直接借助 read(),也可以借助 Scanner 来辅助完成

    补充     

  • Scanner 可以控制处理台输入,又可以控制处理文件的输入,还可以控制处理网络的输入;
  • Scanner 的构造方法:

  • Scanner 的构造方法,填入的是一个InputStream 对象

    根据请求计算响应    

  • 当前编写的是一个回显服务器代码,所以可以直接在计算响应的逻辑返回请求即可


     返回响应给客户端    

下列写法,会直接拿到 response 中的字节数组,然后通过 outputStream 提供的 write(),来写入输出流对象即可;

这种计算响应的方法,是提供字节的方式填充输出流对象;


除了上述写法,我们还可以利用字符流的方式:


这里的 writer 和 System.out 起到的的效果类似,所以 println,printf 等等都可以通过调用


    打印返回响应的日志和连接断开的日志    


    服务器一次连接可处理多个请求的原理   

服务器 start() 的代码块中, process() 方法处理请求返回响应的逻辑,相当于嵌套了两层 while 循环:

因此,可以在服务器与客户端的一次连接中,服务器处理多个请求;

如果在一次连接中,客户端发送多次请求,服务器就返还多个响应(打一次电话可以说一句话或者很多句话);


    补充     

  • 一个连接一个请求(短连接),一个连接多个请求(长连接);
  • 因为连接过程的开销非常大,所以在日常开发中,更主流的是长连接,一个连接处理多个请求;
  • 就好比锁消除,针对要加锁的多个逻辑,每个逻辑都进行加锁,开销非常大,所以更科学的做法是把这些逻辑合在一起,只进行一次加锁;(和领导汇报工作成果,应该在一次电话中一次性汇总完毕,而不是打多次电话。每次电话只汇报一个成果)

    TCP Echo Client    


     创建 Socket 对象    

Socket 在客户端和服务器都可以使用,服务器的 Socket 通过调用accept()拿到,但是客户端的 Socket 就需要通过实例来创建对象;

     实现客户端构造方法    

在客户端的构造方法中,传入服务器的IP和端口号;

传到构造方法中的字符串IP地址(类似127.0.0.1这样的字符串),不需要任何转换;


对比UDP 的客户端,TCP客户端在构造方法在实例Socket对象后,就会在底层和对端建立TCP连接,连接好后,服务器会记录对端的信息(实例化Socket对象时传入的IP和端口号);

因此,服务器的IP和端口号,在TCP客户端中就不需要再创建变量来保存了;


    从控制台中读取请求,发送给服务器    


    从控制台中读取用户输入信息作为请求    

为了实现客户端能够和服务器在一次连接的情况下,发送多次请求,我们设置一个循环:

这步操作可以读取刚刚输入控制台的一行信息,读取到的信息作为客户端的请求;


    拿到输入流&输出流    

之后就把这个请求写入 Socket 对象中,写的时候也需要拿到Socket对象的 InputStream 输入流& OutputStream 输出流;


为了使用方便,可以对拿到的输入流和输出流再套一层壳


    完善循环逻辑     

所以在主循环中,第一步操作是从控制台中读取用户输入,把读取到的输入设置为请求:


第二步就是把请求发送给服务器


第三步,就是读取服务器返回的响应,并且把读取到的响应打印到控制台


    客户端与服务器交互过程    


    区分客户端与服务器的 Socket 对象    

下列服务器和客户端的两个 socket 对象,分别在不同进程中,甚至在不同主机中,因此绝对不是同一个对象;

这两个对象存在密切的关联关系,可以把这两个 socket 对象理解为两部电话:

  • 接通这两部电话后,从A听筒说话,B可以听见;从B听筒说话,A可以听见(从一边对Socket对象写数据,另一边的 Socket 对象就可以读到);
  • 但是这两个对象绝对不是同一部电话;

    处理细节问题    

    问题一:冲刷缓冲区     

完善 main 方法


程序运行结果


关掉客户端:


再启动一次客户端,并且发送一个数据,并且一敲回车,发现没有反应:


为什么没有反应呢?因为其实刚刚客户端代码,并没有真的把请求发送出去:

这个操作只是把数据放到 “发送缓冲区” 中,还没有真正写入网卡里;


  •  发送缓冲区其实就是一块内存空间,对网络/硬盘写数据是一个非常低效的操作,如果频繁地调用这些比较低效的操作,程序运行是非常缓慢的;
  • 为了提高效率,就引入一个内存缓冲区,把要写入的数据都放入缓冲区中,再统一进行发送,这样可以减小写硬盘和写网络的次数;
  • 但是提高效率的同时,也会产生副作用,就是调用 writer.println 这样的操作,并没有真正地触发发送数据操作,而只是把数据写入缓冲区;
  • 当然,把数据写入缓冲区,而不是直接发送这样的行为,是 PrintWriter的行为,如果不套壳,是可以直接发送的;
  • 但是在实际开发中,广泛使用了缓冲区这样的概念,调用flush()来刷新缓冲区这个操作是非常关键的;

如何真正地把数据发送出去呢?我们要使用刷新操作(调用 flush() 方法来冲刷缓冲区),把缓冲区的数据强制写入 IO 设备中:


客户端服务器交互结果


    问题二:针对 hasNext 对一个完整请求/响应设置标识符    

 println 的操作,会自动加上一个 \n : 


但是如果在这个代码中不加这个 \n,直接使用 print行不行呢?


我们重新启动一下客户端,并且发送内容,发现客户端又没有反应了,并且服务器也没有读到信息:


造成上述原因,是因为 next() 的问题,修改成 print 后,客户端输入的数据也是发送到服务器上了,并且服务器也收到了,但是服务器并没有真正处理,因为服务器有一个hasNext()判断:


    补充    

  • hasNext() 的行为是,判断当前收到的数据是否包含“空白符”,什么是空白符呢?
  • 换行,回车,空格,制表符,翻页符......都是空白符;
  • 遇到空白符,hasNext()才会认为是一个完整的 next,否则在遇到空白符之前,hasNext() 都会阻塞。

所以刚刚在修改成 print 之后,发送的内容是不包含空白符的内容,hasNext() 就会阻塞而无法进入下面读取请求的逻辑;


    总结    


  • 使用 println,是在约定一个请求/响应,是在使用 \n 作为结束标记,对端在读取数据的时候,也会在读取到 \n 时,判断读取到一个完整的请求/响应;
  • 这是我们在使用TCP时,特别需要注意的事项,并且和UDP不一样;
  • UDP是以 DatagramPacket 作为单位的,但是TCP则是以字节为单位,但是实际上一个请求,往往是以多个字节构成的;
  • 到底多少个字节为一个完整的请求/响应,就需要程序员想办法标记出来,引入分割符是标记一次完整请求/响应的典型方式,不一定是换行,也可以是其他分割符; 

    问题三:根据不同Socket的生命周期判断是否需要手动关闭    

在TCP服务器刚刚编写的代码中,涉及两种Socket:

  • ServerSocket的生命周期,贯彻整个服务区进程,不需要手动关闭


  • clientSocket 的生命周期是一次连接,而不是整个服务器进程;

  • 所以每个客户端连接,都会创建一个新的 clientSocket,每个客户端断开,这个对象就应该 close() 了,但是当前代码并没有对 clientSocket 进行 close() 释放;
  • 没有在每次连接结束后,对 clientSocket 进行关闭,就会造成文件资源泄漏的问题(文件一直在打开,而不进行关闭,在打开到一定程度,会把文件描述表耗尽,就无法继续打开新文件)

  • 在 poccessConnection() 方法的逻辑执行完毕之后,我们就可以对 clientSocket 进行关闭

    总结    

  • 对于是否需要手动关闭 Socket ,需要我们分析请求它的生命周期是跟随整个进程,还是跟随某个环节;
  • 如果是跟随整个进程,那么可以不手动关闭 Socket;如果是每个请求都会创建应该 Socket,或者每一次连接都会创建一个 Socket,或者某一个环节的执行周期,会创建一个Socket,这样的情况,就需要我们手动关闭 Socket;
    问题四:服务器无法同时等待 accept() &等待已连接的客户端发送响应    

一个服务器能同时给多个客户端提供服务,那么刚刚编写的TCP服务器也可以处理多个请求吗?我们关掉客户端,再重新启动多个客户端:


    补充:修改同时启动多个客户端的IDEA设置    



IDEA 会默认只启动一个客户端,再启动别的客户端,会先关闭上一个启动的客户端;我们通过这里的设置,就可以让 IDEA 启动多个客户端;

设置好后,我们再来重新启动两个客户端, 并且先后发送请求:

结果在第二个客户端发送请求时,又卡住了,并且第一个创建的客户端多次发送请求的操作是没问题的,服务器都会有一个正常响应,但是第二个客户端无论怎么发送请求,都不会有响应:

并且我们通过服务器日志,可以发现,在第二个客户端上线时,并没有再次打印日志;

如果我们关掉第一个客户端,第二个客户端发送的请求,会马上被服务器接收,并且返回响应

所以当前代码,TCP服务器在同一时刻,只能处理一个客户端发送的请求;


针对上面出现的问题,我们对服务器的处理请求,返回响应这一块代码的关键逻辑进行分析:

如果同一时刻,有多个客户端对一个服务器进行连接,那么第一个和服务器连接的客户端1:

所以当前这个服务器代码,如果已经在处理一个客户端的请求,就没办法处理另一个客户端的请求,服务器代码会卡在循环中,无法重新调用 accept()连接新的服务器,直到连接的客户端退出,导致循环终止;


    总结    


  • 当前服务器代码,无法同时等待 accept 和 等待用户请求;在等待客户端发送请求的时候,没办法等待 accept() ,这个时候,如果有新的客户端连接,也无法接听电话(一个专业销售没办法在给先来的顾客讲解产品的时候,又去接待低级销售后面揽入店的顾客);
  • 因此,服务器的代码,导致服务器一次只能处理一个客户端发送的请求,这个代码是不合理的; 

    4. 服务器引入多线程    


如果只是单个线程,无法同时响应多个客户端;为了解决这个问题,此处给每个客户端都分配一个线程;


在服务器主线程中,就只是进行 accept(),每次有新的客户端 accept() 连接成功,就创建一个新的线程,又新线程负责完成,后续对客户端 Socket 对象的引用 clientSocket 的读写操作;

引入线程之后,重新启动服务器和两个客户端,可以发现服务器打印了两个带着不同端口号的日志;


因此,在引入线程之后,服务器可以一边等待请求,一边等待 accept() 连接新的客户端;



    5. 服务器引入线程池    


客户端连接,服务器就会创建新线程,客户端断开连接,客户端就会销毁线程,为了避免频繁创建销毁线程,也可以引入线程池;

线程池


对于在服务器引入线程池,一般不会使用 newFixedThreadPool,因为会创建一个固定线程数的线程池,意味着同时处理的客户端连接数目就固定了;

把任务都交到线程池中,线程池已经预先创建好了一些线程,提前创建好的线程就可以立刻投入工作,从而减少再去创建线程的开销;


线程不是越多越好,如果线程数量过多,CPU的利用率无法再被提高,还会导致系统调度速度下降;并且创建线程也是需要系统资源的,系统总体资源是有限的(一个主机差不多只能创建几千个线程);

无论是多线程,还是线程池,一个线程都对应一个客户端,并且一个主机创建的线程数目是有上限的,那如果有成千上万个客户端,同时访问一个服务器(一台主机),该怎么办呢?

IO多路复用/IO多路连接(这里不重点讲解,因为JVM中没有原生的 IO 多路复用 API,而是把API重新封装,已经不仅仅是多路复用了,后续会详细讲解针对 Java 的 IO多路复用所使用的 NIO,Netty 等知名网络框架,当前讲的IO是 BIO/Blocking IO);


    扩展    


基于BIO(同步阻塞IO)的长连接会一直占用系统资源。对于并发要求很高的服务端系统来说,这样的消耗是不能承受的。

  • 由于每个连接都需要不停的阻塞等待接收数据,所以每个连接都会在一个线程中运行。
  • 一次阻塞等待对应着一次请求、响应,不停处理也就是长连接的特性:一直不关闭连接,不停的处理请求。

实际应用时,服务端一般是基于NIO(即同步非阻塞IO)来实现长连接,性能可以极大的提升。 


    6. 长短连接    


TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接;


    长连接和短连接的概念    


  • 短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据。
  • 长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以多次收发数据。

    长连接和短连接区别    


  • 建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。
  • 主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。
  • 两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于客户端与服务端通信频繁的场景,如聊天室,实时游戏等。

   

   c96f743646e841f8bb30b2d242197f2f.gif

692a78aa0ec843629a817408c97a8b84.gif

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Scala入门基础(20)数据集复习拓展

一.Stack栈二.Queue 队列 一.Stack栈 Stack:栈,特殊的结构。它对元素的操作是在头部:栈顶 先进后出的队列。pop表示取出,push表示在栈中添加元素 二.Queue 队列 Queue 队列;先进先出.enqueue入队,dequeue出队。

ThinkPHP Nginx 重写配置

目录 NGINX 重写 Admin项目隐藏入口文件,且禁用Admin模块&Admin.php 1️⃣配置仅用模块 2️⃣新增admin_xyz.php文件(自定义入口文件名),并绑定admin模块 3️⃣配置nginx 重写规则 NGINX 重写 在Nginx低版本中&#xff0…

深度学习基础3

目录 1.过拟合与欠拟合 1.1 过拟合 1.2 欠拟合 1.2 解决欠拟合 1.2.1 L2正则化 1.2.2 L1正则化 1.2.3 Dropout 1.2.4 简化模型 1.2.5 数据增强 1.2.6 早停 1.2.7 模型集成 1.2.8 交叉验证 2.批量标准化 2.1 实现过程 2.1.1 计算均值和方差 2.1.2 标准化 2.1.3…

Scala习题

姓名,语文,数学,英语 张伟,87,92,88 李娜,90,85,95 王强,78,90,82 赵敏,92,88,91 孙涛&#xff0c…

【赵渝强老师】PostgreSQL的数据库

PostgreSQL的逻辑存储结构主要是指数据库中的各种数据库对象,包括:数据库集群、数据库、表、索引、视图等等。所有数据库对象都有各自的对象标识符oid(object identifiers),它是一个无符号的四字节整数,相关对象的oid都…

(C语言) 8大翻译阶段

(C语言) 8大翻译阶段 文章目录 (C语言) 8大翻译阶段⭐前言🗃️8大阶段🗂️1. 字符映射🗂️2. 行分割🗂️3. 标记化🗂️4. 预处理🗂️5. 字符集映射🗂️6. 字符串拼接🗂️7. 翻译&…

安全基线检查

一、安全基线检测基础知识 安全基线的定义 安全基线检查的内容 安全基线检查的操作 二、MySQL的安全基线检查 版本加固 弱口令 不存在匿名账户 合理设置权限 合理设置文件权限 日志审核 运行账号 可信ip地址控制 连接数限制 更严格的基线要求 1、禁止远程连接数据库 2、修改…

玩转 uni-app 静态资源 static 目录的条件编译

一. 前言 老生常谈,了解 uni-app 的开发都知道,uni-app 可以同时支持编译到多个平台,如小程序、H5、移动端 App 等。它的多端编译能力是 uni-app 的一大特点,让开发者可以使用同一套代码基于 Vue.js 的语法编写程序,然…

[2024年3月10日]第15届蓝桥杯青少组stema选拔赛C++中高级(第二子卷、编程题(2))

方法一&#xff08;string&#xff09;&#xff1a; #include <iostream> #include <string> using namespace std;// 检查是否为回文数 bool isPalindrome(int n) {string str to_string(n);int left 0, right str.size() - 1;while (left < right) {if (s…

快速排序hoare版本和挖坑法(代码注释版)

hoare版本 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h>// 交换函数 void Swap(int* p1, int* p2) {int tmp *p1;*p1 *p2;*p2 tmp; }// 打印数组 void _printf(int* a, int n) {for (int i 0; i < n; i) {printf("%d ", a[i]);}printf("…

C5.【C++ Cont】getchar,putchar和scanf

目录 1.回顾C语言文章24.【C语言】getcha和putchar的使用 2.C中和C语言不同的地方 3.关键点 4.scanf 5.练习1 题目描述 输入描述: 输出描述: 输入 输出 6.练习2 题目描述 输入格式 输出格式 输入输出样例 说明/提示 1.回顾C语言文章24.【C语言】getcha和putchar…

深入理解 AI 产品的核心价值——《AI产品经理手册》

现在&#xff0c;人们对AI 充满了兴趣和看法。这些年&#xff0c;我亲身经历了对AI 的感受和认识的此起彼伏。我还是学生时&#xff0c;就对AI 以及伴随而来的第四次工业革命感到无比激动和期待。然而&#xff0c;当我开始组织读书会&#xff0c;每月阅读有关AI 的书籍&#xf…

Spring Boot拦截器(Interceptor)详解

拦截器Interceptor 拦截器我们主要分为三个方面进行讲解&#xff1a; 介绍下什么是拦截器&#xff0c;并通过快速入门程序上手拦截器拦截器的使用细节通过拦截器Interceptor完成登录校验功能 1. 快速入门 什么是拦截器&#xff1f; 是一种动态拦截方法调用的机制&#xff…

python代码示例(读取excel文件,自动播放音频)

目录 python 操作excel 表结构 安装第三方库 代码 自动播放音频 介绍 安装第三方库 代码 python 操作excel 表结构 求出100班同学的平均分 安装第三方库 因为这里的表结构是.xlsx文件,需要使用openpyxl库 如果是.xls格式文件,需要使用xlrd库 pip install openpyxl /…

构建 LLM (大型语言模型)应用程序——从入门到精通(第七部分:开源 RAG)

通过检索增强生成 (RAG) 应用程序的视角学习大型语言模型 (LLM)。 本系列博文 简介数据准备句子转换器矢量数据库搜索与检索大语言模型开源 RAG&#xff08;本帖&#xff09;评估服务LLM高级 RAG 1. 简介 我们之前的博客文章广泛探讨了大型语言模型 (LLM)&#xff0c;涵盖了其…

2024健康大数据与智能医疗(ICHIH 2024)

大会官网&#xff1a;www.ic-ichih.net 大会时间&#xff1a;2024年12月13-15日 大会地点&#xff1a;中国珠海 收录检索&#xff1a;IEEE Xplore&#xff0c;EI Compendex&#xff0c;Scopus

从0开始学PHP面向对象内容之常用设计模式(适配器,桥接,装饰器)

二&#xff0c;结构型设计模式 上两期咱们讲了创建型设计模式&#xff0c;都有 单例模式&#xff0c;工厂模式&#xff0c;抽象工厂模式&#xff0c;建造者模式&#xff0c;原型模式五个设计模式。 这期咱们讲结构型设计模式 1、适配器模式&#xff08;Adapter&#xff09; …

原生微信小程序画表格

wxml部分&#xff1a; <view class"table__scroll__view"><view class"table__header"><view class"table__header__item" wx:for"{{TableHeadtitle}}" wx:key"index">{{item.title}}</view></…

TDengine 签约深圳综合粒子,赋能粒子研究新突破

在高能物理和粒子研究领域&#xff0c;实验装置的不断升级伴随着海量数据的产生与处理。尤其是随着大湾区综合性国家科学中心的建设步伐加快&#xff0c;深圳综合粒子设施研究院&#xff08;以下简称“研究院”&#xff09;作为承载“双区驱动”战略的重要科研机构&#xff0c;…

SpringMVC——SSM整合

SSM整合 创建工程 在pom.xml中导入坐标 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_…