【JavaEE初阶 — 网络编程】TCP流套接字编程
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 的代码块中,实现读取请求和返回响应的操作,这些操作分成三步
- 读取请求并解析
- 根据请求计算响应
- 返回响应给客户端
因为在一次连接中,这三个操作可能会解析多次,所以我们提供 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发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接;
长连接和短连接的概念
- 短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据。
- 长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以多次收发数据。
长连接和短连接区别
- 建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。
- 主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。
- 两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于客户端与服务端通信频繁的场景,如聊天室,实时游戏等。
相关文章:

【JavaEE初阶 — 网络编程】TCP流套接字编程
TCP流套接字编程 1. TCP & UDP 的区别 TCP 的核心特点是面向字节流,读写数据的基本单位是字节 byte 2 API介绍 2.1 ServerSocket 定义 ServerSocket 是创建 TCP 服务端 Socket 的API。 构造方法 方法签名 方法说明 ServerS…...
《气候变化研究进展》
《气候变化研究进展》设有气候系统变化、气候变化影响、气候变化适应、温室气体排放、对策论坛、简讯等栏目,其内容包括:国内外气候变化研究的最新成果与进展,以及与气候变化有关的交叉学科,如地球科学、生态与环境科学、人文与社…...

D2545电动工具调速专用控制电路芯片介绍【青牛科技】
概述: D2545 是一块频率、占空比可调的脉冲控制电路。可通过调节外接的电阻和电容大小来控制输出频率和占空比,达到控制电机转速的作用。 主要特点: ● 电源电压范围宽 ● 占空比可调 ● 静态功耗小 ● 抗干扰能力强 应用: ● …...

Unity 2020、2021、2022、2023、6000下载安装
Unity 2020、2021、2022、2023、6000 下载安装 以Unity 6000.0.24fc1下载安装为例: 打开 https://unity.cn/ 优三缔 官方网站; 点击【产品列表】→点击【查看更多】→选择自己需要的版本→点【开始使用】 点击【从Unity Hub下载】 以Windows为例&am…...

33 基于单片机的智能窗帘控制系统
目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机,采用DHT11温湿度传感器检测温湿度,滑动变阻器连接ADC0832数模转换器转换模拟,光敏传感器,采用GP2D12红外传感器,通过LCD1602显示屏显示…...

【CSS in Depth 2 精译_063】10.2 深入理解 CSS 容器查询中的容器
当前内容所在位置(可进入专栏查看其他译好的章节内容) 【第十章 CSS 容器查询】 ✔️ 10.1 容器查询的一个简单示例 10.1.1 容器尺寸查询的用法 10.2 深入理解容器 ✔️ 10.2.1 容器的类型 ✔️10.2.2 容器的名称 ✔️10.2.3 容器与模块化 CSS ✔️ 10.3…...

记录一次 k8s 节点内存不足的排查过程
背景:前端服务一直报404,查看k8s日志,没发现报错,但是发现pods多次重启。 排查过程: 查看pods日志,发现日志进不去。 kubectrl logs -f -n weave pod-name --tail 100查看pod describe kubectl describ…...

探索天空中的“名字”——用Landsat影像记录你的名字形状!
大家好!今天我发现了一个特别有趣的工具——NASA官网上有一个功能,允许你输入自己的名字,然后它会根据Landsat卫星影像显示出与你名字形状相符的地形图。是不是很酷?🎉 🌍 Landsat影像的神奇之处Landsat是N…...

QT6学习第四天 感受QT的文件编译
QT6学习第四天 感受QT的文件编译 使用纯代码编写程序新建工程 使用其他编辑器纯代码编写程序并在命令行运行使用 .ui 表单文件生成界面使用自定义 C 窗口类使用现成的QT Designer界面类 使用纯代码编写程序 我们知道QT Creator中可以用拖拽的方式在 .ui 文件上布局,…...

透视投影(Perspective projection)与等距圆柱投影(Equirectangular projection)
一、透视投影 1.方法概述 Perspective projection(透视投影)是一种模拟人眼观察三维空间物体时的视觉效果的投影方法。它通过模拟观察者从一个特定视点观察三维场景的方式来创建二维图像。在透视投影中,远处的物体看起来比近处的物体小&…...

5 Java字符串操作
字符串操作 1、String类1.1 声明字符串1.2 创建字符串 1.3 字符串连接 /连接字符串连接其他数据类型 1.4 提取字符串信息获取字符串长度length()获取指定位置的字符 charAt()获取子字符串索引位置 indexOf()判断字符串首尾内容 startsWith()/endsWith()获取字符数组 toCharArra…...

【C++习题】17.二分查找算法_二分查找
文章目录 二分查找算法简介题目链接:题目描述:解法C 算法代码:图解总结朴素二分模板 二分查找算法简介 特点: 二分查找算法,是最恶心,细节最多,最容易写出死循环的算法。(而且非常难…...

Spring Boot英语知识网站:架构与开发
4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式,是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示: 图4-1系统工作原理…...

Unity ShaderLab 实现网格爆炸
实现思路: 沿着3D物体每个面的法线,将面偏移一定的位置。 Shader Graph实现如下: Shader Lab 实现如下: Shader "Unlit/MeshExplode" {Properties{_MainTex ("Texture", 2D) "white" {}_Distan…...
2024/11/28学习日志
为了更好地记录并反思自己的学习状况,将每日学习的内容、时长、心得等记录于此日志。 于9月3日开始记录,计划每日记录,希望至少能够坚持一个学期。 学习内容: 数据结构: 拓扑排序。关键路径。 马原: 马…...
在shardingsphere执行存储过程
环境: springboot:2.5.2 数据库:Kingbase金仓V8R6 依赖: <dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId></depende…...
1.文件目录操作
目录 🍌 ls - 列出目录内容 🍉cp - 复制文件或目录 🍇mv - 移动或重命名文件 🍓 cd - 切换目录 🍈 pwd - 打印工作目录 🍒mkdir - 创建目录 🍑 rmdir - 删除空目录 🥭 touc…...
Vue单页面应用和多页面应用
在 Vue.js 中,“单页面”(SPA,Single Page Application)和"多页面"(MPA,Multi Page Application)是两种不同的应用结构,它们的差异主要体现在页面的加载方式、路由的使用、…...
Lombok :简化 Java 编程的得力工具
在 Java 开发过程中,常常需要编写大量的样板代码,例如构造函数、Getter 和 Setter 方法、equals 和 hashCode 方法等。这些代码虽然逻辑相对固定,但编写起来却较为繁琐且容易出错,并且会使代码显得冗长。Lombok 应运而生ÿ…...

AIGC引领金融大模型革命:未来已来
文章目录 金融大模型的应用场景1. **金融风险管理**2. **量化交易**3. **个性化投资建议**4. **金融欺诈检测和预防**5. **智能客户服务** 金融大模型开发面临的挑战应对策略《金融大模型开发基础与实践》亮点内容简介作者简介获取方式 在AIGC(Artificial Intellige…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...

SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...