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

每个程序员都应该自己写一个的:socket包装类

        每个程序员都应该有自己的网络类。

        下面是我自己用的socket类,支持所有我自己常用的功能,支持windows和unix/linux。

目录

客户端

服务端

非阻塞

获取socket信息

完整代码


客户端

        作为socket客户端,只需要如下几个功能:

//连接到指定的域名/地址和端口
bool Connect(const string & host, unsigned short port);//发送数据
bool Send(const string & str);//发送文本
bool Send(char const * buf, long count);//发送二进制数据//接收数据
bool Recv(char * buf, int buflen, long * pReadCount);//断开连接
bool Close();

        比较复杂的是连接到服务器,因为要处理域名和端口,还要处理主机字节序和网络字节序的转换,但是写好以后就可以简单地用域名/地址和端口调用了。判断传入的参数是域名还是地址可以直接尝试转换成地址,如果失败再进行域名解析。

        Connect代码如下:

		bool Connect(const string & host, unsigned short port)//连接到指定的目标{if (isSTDOUT)return false;struct hostent *ph;T_SA_SIZE len_sa = sizeof(struct sockaddr_in);if (s >= 0){cout << "不能在已打开的socket上操作 " << s << endl;return false;}if (!CreateSocket()){cout << "socket创建失败 " << s << endl;return false;}peersa.sin_family = AF_INET;peersa.sin_port = htons(port);if (-1 == (long)(peersa.sin_addr.s_addr = inet_addr(host.c_str()))){if (NULL == (ph = gethostbyname(host.c_str())))return false;memcpy(&peersa.sin_addr.s_addr, ph->h_addr_list[0], ph->h_length);}if (connect(s, (sockaddr*)(void*)&peersa, sizeof(struct sockaddr_in)) < 0){Close();return false;}getsockname(s, (sockaddr*)(void*)&mysa, &len_sa);getpeername(s, (sockaddr*)(void*)&peersa, &len_sa);int iKeepAlive = 1;setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&iKeepAlive, sizeof(iKeepAlive));return true;}

服务端

        作为服务端,关键是这两个功能:

//在指定端口上监听
bool Listen(unsigned short portnum);//接受一个连接请求,返回一个新CmySocket对象
CmySocket Accept();

        Listen其实相当简单,只要先bind到端口即可,当然还可能需要指定使用的IP,不过我的程序没有用到,所以没有写:

bool Bind(unsigned short portnum)//绑定到端口
{if (s < 0 && !CreateSocket())return false;int on = 1;setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));mysa.sin_port = htons(portnum);if (bind(s, (sockaddr*)(void*)&mysa, sizeof(struct sockaddr_in)) < 0){Close();return false;}return true;
}bool Listen(unsigned short portnum)
{return (Bind(portnum)) && (!(listen(s, SOMAXCONN) < 0));
}

非阻塞

        一般用select查询socket是否可读就可以解决阻塞问题(写操作不会阻塞)。但是,有一个坑:

        如果并发执行select,会同时得到可读,从而所有进程或线程进入读操作(通常是服务端的accept操作),但是,只有其中一个能读到,其它进程或线程会被阻塞。这种现象叫做“惊群”。

        对select加锁可以解决问题,但是会降低性能。服务端应该通过别的方式来解决(一般要解决的只是让服务端能遵照指令退出而已)。

        通过select检查socket是否可读的代码如下:

		bool IsSocketReadReady(struct timeval & timeout, bool & ret){fd_set fd;int i;FD_ZERO(&fd);FD_SET(s, &fd);i = select(s + 1, &fd, NULL, NULL, &timeout);if (1 == i){ret = true;return true;}else if (0 == i){ret = false;return true;}else if (-1 == i){ret = false;return false;}return false;}

        还有一个能够快速退出的版本:

		//检查套接字是否可读,seconds为负不设超时,但仍可根据pfNeedBrek跳出bool IsSocketReadReady2(long seconds, bool & ret, bool(*pfNeedBrek)() = NULL){struct timeval timeout;timeout.tv_sec = (0 == seconds ? 0 : 1);timeout.tv_usec = 0;time_t t1 = time(NULL);do{//LOG<<seconds<<" "<<time(NULL) - t1<<ENDI;if (!IsSocketReadReady2(timeout, ret)){return false;}if (NULL != pfNeedBrek && pfNeedBrek()){if (isDebug)cout << "need break:" << s << endl;return false;}if (ret){return true;}} while (seconds < 0 || time(NULL) - t1 < seconds);return true;}

        每次阻塞一秒钟,然后检查是否设置了退出命令。当然我们知道UNIX/Linux的标准的机制是使用信号来中断,不过信号机制可能不同模块冲突,不如靠自己。

获取socket信息

        程序调试经常要知道本地的端口和对方的地址端口,这是通过调用getsockname和getpeername来实现的:

//成员变量public:bool isDebug;//调试输出private:bool isSTDOUT;//输出到标准输出而不是socketint s;//socket -1表示无效unsigned long sendcount;//发送计数unsigned long recvcount;//接收计数struct sockaddr_in mysa;//本地半相关struct sockaddr_in peersa;//远程半相关//服务端接受连接或客户端建立连接后执行:
getsockname(cs.s, (sockaddr*)(void*)&cs.mysa, &len_sa);
getpeername(cs.s, (sockaddr*)(void*)&cs.peersa, &len_sa);//输出内部信息string debuginfo()//输出内部数据结构{string str;char buf[256];str = "";if (isSTDOUT)str += "STDOUT\n";if (-1 != s){sprintf(buf, "%d", s);str += buf;}else str += "未连接";str += "\n";sprintf(buf, "send: %ld\nrecv: %ld\n", sendcount, recvcount);str += buf;if (AF_INET == mysa.sin_family)str += "AF_INET";else{sprintf(buf, "%d", mysa.sin_family);str += buf;}str += "\n";str += inet_ntoa(mysa.sin_addr);str += "\n";sprintf(buf, "%d", ntohs(mysa.sin_port));str += buf;str += "\n";if (AF_INET == peersa.sin_family)str += "AF_INET";else{sprintf(buf, "%d", peersa.sin_family);str += buf;}str += "\n";str += inet_ntoa(peersa.sin_addr);str += "\n";sprintf(buf, "%d", ntohs(peersa.sin_port));str += buf;str += "\n";return str;}

完整代码


#ifndef MYSTD_MYSOCKET_H
#define MYSTD_MYSOCKET_H#ifndef _MS_VC
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>
#else
#include "winsock.h"
#endifnamespace ns_my_std_2
{#define T_SA_SIZE int#ifdef _HPOS
#undef T_SA_SIZE
#define T_SA_SIZE int
#endif#ifdef _IBMOS
#undef T_SA_SIZE
#define T_SA_SIZE unsigned int
#endif#ifdef _LINUXOS
#undef T_SA_SIZE
#define T_SA_SIZE socklen_t
#endifclass CmySocket{public:bool isDebug;//调试输出private:bool isSTDOUT;//输出到标准输出而不是socketint s;//socket -1表示无效unsigned long sendcount;//发送计数unsigned long recvcount;//接收计数struct sockaddr_in mysa;//本地半相关struct sockaddr_in peersa;//远程半相关bool Init()//初始化,s被设置为-1,计数清零,半相关清零{char myname[256];struct hostent *ph;s = -1;isDebug = false;isSTDOUT = false;sendcount = 0;recvcount = 0;memset(&mysa, 0, sizeof(struct sockaddr_in));memset(&peersa, 0, sizeof(struct sockaddr_in));if (0 != gethostname(myname, 256))return false;myname[255] = '\0';if (NULL == (ph = gethostbyname(myname)))return false;mysa.sin_family = ph->h_addrtype;return true;}bool CreateSocket()//初始化并建立一个socket{if (Init() && (s = socket(AF_INET, SOCK_STREAM, 0)) > 0)return true;else return false;}int CloseSocket(int _s){
#ifndef _MS_VCreturn close(_s);
#elsereturn closesocket(_s);
#endif}bool Bind(unsigned short portnum)//绑定到端口{if (s < 0 && !CreateSocket())return false;int on = 1;setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));mysa.sin_port = htons(portnum);if (bind(s, (sockaddr*)(void*)&mysa, sizeof(struct sockaddr_in)) < 0){Close();return false;}return true;}public:CmySocket(int _s = -1)//构造函数,s默认被设置为-1{T_SA_SIZE len_sa = sizeof(struct sockaddr_in);Init();s = _s;if (-1 != s){getsockname(s, (sockaddr*)(void*)&mysa, &len_sa);getpeername(s, (sockaddr*)(void*)&peersa, &len_sa);}}void SetSTDOUT() { isSTDOUT = true; }//设置为标准输出bool Listen(unsigned short portnum)//在指定端口上监听,如果s为-1会先建立socket然后bind{if (isSTDOUT)return false;return (Bind(portnum)) && (!(listen(s, SOMAXCONN) < 0));}bool Accept(int * pNewSocket)//接受一个连接请求{if (isSTDOUT)return false;return -1 != ((*pNewSocket) = accept(s, NULL, NULL));}CmySocket Accept()//接受一个连接请求,返回一个新CmySocket对象{if (isSTDOUT)return false;CmySocket cs;T_SA_SIZE len_sa = sizeof(struct sockaddr_in);cs.Init();cs.s = accept(s, (sockaddr*)(void*)&cs.mysa, &len_sa);if (-1 != cs.s){getsockname(cs.s, (sockaddr*)(void*)&cs.mysa, &len_sa);getpeername(cs.s, (sockaddr*)(void*)&cs.peersa, &len_sa);int iKeepAlive = 1;setsockopt(cs.s, SOL_SOCKET, SO_KEEPALIVE, (void *)&iKeepAlive, sizeof(iKeepAlive));}return cs;}bool Send(const string & str)//发送文本{return Send(str.c_str(), str.size());}bool Send(char const * buf, long count)//发送二进制数据{if (isSTDOUT){std::cout << buf << std::flush;sendcount += count;return true;}long i = 0;while (i < count){int n = send(s, buf + i, count - i, 0);if (isDebug)cout << "socket " << s << " send " << count - i << " return " << n << endl;if (n != count){cout << "socket " << s << " send " << count - i << " return " << n << endl;}if (n < 0){return false;}i += n;sendcount += n;}return true;}bool Recv(char * buf, int buflen, long * pReadCount)//接收数据{if (isSTDOUT)return false;if ((*pReadCount = recv(s, buf, buflen, 0)) < 0){if (isDebug)cout << "socket " << s << " recv  return " << *pReadCount << endl;return false;}if (isDebug)cout << "socket " << s << " recv  return " << *pReadCount << endl;recvcount += (*pReadCount);return true;}bool Close()//close socket 设置s为-1,但其它数据会保持到下一次用这个对象建立新socket时才清除{if (isSTDOUT)return true;if (isDebug)cout << "socket 关闭:" << s << endl;shutdown(s, 2);if (0 == CloseSocket(s)){s = -1;return true;}else return false;}bool Connect(const string & host, unsigned short port)//连接到指定的目标{if (isSTDOUT)return false;struct hostent *ph;T_SA_SIZE len_sa = sizeof(struct sockaddr_in);if (s >= 0){cout << "不能在已打开的socket上操作 " << s << endl;return false;}if (!CreateSocket()){cout << "socket创建失败 " << s << endl;return false;}peersa.sin_family = AF_INET;peersa.sin_port = htons(port);if (-1 == (long)(peersa.sin_addr.s_addr = inet_addr(host.c_str()))){if (NULL == (ph = gethostbyname(host.c_str())))return false;memcpy(&peersa.sin_addr.s_addr, ph->h_addr_list[0], ph->h_length);}if (connect(s, (sockaddr*)(void*)&peersa, sizeof(struct sockaddr_in)) < 0){Close();return false;}getsockname(s, (sockaddr*)(void*)&mysa, &len_sa);getpeername(s, (sockaddr*)(void*)&peersa, &len_sa);int iKeepAlive = 1;setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&iKeepAlive, sizeof(iKeepAlive));return true;}bool IsConnected() { if (isSTDOUT)return true; else return -1 != s; }//是否处于连接状态,只对客户socket有意义sockaddr_in const * GetPeersa()const { return &this->peersa; }//检查套接字是否可读bool IsSocketReadReady(long seconds, bool & ret){struct timeval timeout;timeout.tv_sec = seconds;timeout.tv_usec = 0;return IsSocketReadReady(timeout, ret);}bool IsSocketReadReady(struct timeval & timeout, bool & ret){fd_set fd;int i;FD_ZERO(&fd);FD_SET(s, &fd);i = select(s + 1, &fd, NULL, NULL, &timeout);if (1 == i){ret = true;return true;}else if (0 == i){ret = false;return true;}else if (-1 == i){ret = false;return false;}return false;}//检查套接字是否可读,seconds为负不设超时,但仍可根据pfNeedBrek跳出bool IsSocketReadReady2(long seconds, bool & ret, bool(*pfNeedBrek)() = NULL){struct timeval timeout;timeout.tv_sec = (0 == seconds ? 0 : 1);timeout.tv_usec = 0;time_t t1 = time(NULL);do{//LOG<<seconds<<" "<<time(NULL) - t1<<ENDI;if (!IsSocketReadReady2(timeout, ret)){return false;}if (NULL != pfNeedBrek && pfNeedBrek()){if (isDebug)cout << "need break:" << s << endl;return false;}if (ret){return true;}} while (seconds < 0 || time(NULL) - t1 < seconds);return true;}bool IsSocketReadReady2(struct timeval & timeout, bool & ret){ret = false;fd_set fd;int i;FD_ZERO(&fd);FD_SET(s, &fd);//LOG<<"timeout.tv_sec "<<timeout.tv_sec<<ENDI;
#ifdef _HPOSi = select(s + 1, (int *)&fd, NULL, NULL, &timeout);
#elsei = select(s + 1, &fd, NULL, NULL, &timeout);
#endif//LOG<<"timeout.tv_sec "<<timeout.tv_sec<<" select "<<i<<ENDI;if (1 == i){ret = true;return true;}else if (0 == i){ret = false;return true;}else if (-1 == i){if (EINTR == errno){ret = false;return true;//被信号中断不是错误}else{ret = false;return false;}}return false;}//接收数据,可以设置函数来终止bool Recv2(char * buf, int buflen, long * pReadCount, bool(*pfNeedBrek)()){bool isReady = false;if (!IsSocketReadReady2(-1, isReady, pfNeedBrek)){if (isDebug)cout << "IsSocketReadReady2 error:" << s << endl;return false;}if (!isReady){if (isDebug)cout << "not ready:" << s << endl;return false;}return Recv(buf, buflen, pReadCount);}int GetMyPort()const{return ntohs(mysa.sin_port);}string GetPeerInfo()const{string str;if (-1 != s){str += inet_ntoa(peersa.sin_addr);str += ":";char buf[32];sprintf(buf, "%d", ntohs(peersa.sin_port));str += buf;}return str;}string debuginfo()//输出内部数据结构{string str;char buf[256];str = "";if (isSTDOUT)str += "STDOUT\n";if (-1 != s){sprintf(buf, "%d", s);str += buf;}else str += "未连接";str += "\n";sprintf(buf, "send: %ld\nrecv: %ld\n", sendcount, recvcount);str += buf;if (AF_INET == mysa.sin_family)str += "AF_INET";else{sprintf(buf, "%d", mysa.sin_family);str += buf;}str += "\n";str += inet_ntoa(mysa.sin_addr);str += "\n";sprintf(buf, "%d", ntohs(mysa.sin_port));str += buf;str += "\n";if (AF_INET == peersa.sin_family)str += "AF_INET";else{sprintf(buf, "%d", peersa.sin_family);str += buf;}str += "\n";str += inet_ntoa(peersa.sin_addr);str += "\n";sprintf(buf, "%d", ntohs(peersa.sin_port));str += buf;str += "\n";return str;}};
}#endif

(这里是结束)

相关文章:

每个程序员都应该自己写一个的:socket包装类

每个程序员都应该有自己的网络类。 下面是我自己用的socket类&#xff0c;支持所有我自己常用的功能&#xff0c;支持windows和unix/linux。 目录 客户端 服务端 非阻塞 获取socket信息 完整代码 客户端 作为socket客户端&#xff0c;只需要如下几个功能&#xff1a; //…...

JMeter:断言之响应断言

一、断言的定义 断言用于验证取样器请求或对应的响应数据是否返回了期望的结果。可以是看成验证测试是否预期的方法。 对于接口测试来说&#xff0c;就是测试Request/Response&#xff0c;断言即可以针对Request进行&#xff0c;也可以针对Response进行。但大部分是对Respons…...

RLHF的替代算法之DPO原理解析:从Zephyr的DPO到Claude的RAILF

前言 本文的成就是一个点顺着一个点而来的&#xff0c;成文过程颇有意思 首先&#xff0c;如上文所说&#xff0c;我司正在做三大LLM项目&#xff0c;其中一个是论文审稿GPT第二版&#xff0c;在模型选型的时候&#xff0c;关注到了Mistral 7B(其背后的公司Mistral AI号称欧洲…...

U盘显示无媒体怎么办?方法很简单

当出现U盘无媒体情况时&#xff0c;您可以在磁盘管理工具中看到一个空白的磁盘框&#xff0c;并且在文件资源管理器中不会显示出来。那么&#xff0c;导致这种问题的原因是什么呢&#xff1f;我们又该怎么解决呢&#xff1f; 导致U盘无媒体的原因是什么&#xff1f; 当您遇到上…...

进销存管理系统如何提高供应链效率?

供应链和进销存系统之间有着密切的联系。进销存系统是供应链管理的一部分&#xff0c;用于跟踪和管理产品的采购、库存和销售。进销存管理是供应链管理的核心流程之一&#xff0c;它有助于提高效率、降低成本、增加盈利&#xff0c;同时确保客户满意度&#xff0c;这对于企业的…...

用AI魔法打败AI魔法

全文均为AI创作。 此为内容创作模板&#xff0c;在发布之前请将不必要的内容删除当前&#xff0c;AI技术的广泛应用为社会公众提供了个性化智能化的信息服务&#xff0c;也给网络诈骗带来可乘之机&#xff0c;如不法分子通过面部替换语音合成等方式制作虚假图像、音频、视频仿…...

Java 中的final:不可变性的魔法之旅

&#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 Java 中的final&#xff1a;不可变性的魔法之旅 前言第一&#xff1a;了解final变量第二&#xff1a;final方法第三&#xff1a;final类第四&#xff1a;final参数第五&#…...

Alfred 5 for mac(最好用的苹果mac效率软件)中文最新版

Alfred 5 Mac是一款非常实用的工具&#xff0c;它可以帮助用户更加高效地使用Mac电脑。用户可以学会使用快捷键、全局搜索、快速启动应用程序、使用系统维护工具、快速复制粘贴文本以及自定义设置等功能&#xff0c;以提高工作效率。 Alfred for Mac 的一些主要功能包括&#…...

常见的Python解释器,你了解多少?

Python&#xff0c;作为一种解释型编程语言&#xff0c;它的运行过程也遵循“程序源码—>解释器&#xff08;字节码&#xff09;—>虚拟机&#xff08;可执行文件&#xff09;”的流程。 在编写Python程序时&#xff0c;是在扩展名为.py的文件中进行编写&#xff0c;.py…...

在 Python 中使用 Selenium 按文本查找元素

我们将通过示例介绍在Python中使用selenium通过文本查找元素的方法。 在 Python 中使用 Selenium 按文本查找元素 软件测试是检查应用程序是否满足用户需求的技术。 该技术有助于使应用程序成为无错误的应用程序。 软件测试可以手动完成&#xff0c;也可以通过某些软件完成。…...

【Notepad++】搜索返回窗口(find result)被隐藏或遮挡如何恢复?

Notepad 搜索返回窗口被隐藏或遮挡如何恢复 1&#xff1a;F72&#xff1a;F12恢复之后可以多看一些Notepad中快捷键的使用&#xff0c;以备不时之需。 1&#xff1a;F7 打开任意文件,搜索任意内容,按F7,焦点切换到Find result。 按AltSpace&#xff0c;出现小窗口点击"移动…...

应用软件安全编程--05预防 XML 注入

如果用户有能力使用结构化XML 文档作为输入&#xff0c;那么他能够通过在数据字段中插入 XML 标签来 重写这个 XML 文档的内容。 XML 解析器会将这些标签按照正常标签进行解析。下面是一段在线商 店的 XML 代码&#xff0c;主要用于查询后台数据库。 <item)<descri…...

JavaEE-博客系统3(功能设计)

本部分内容为&#xff1a;实现登录功能&#xff1b;强制要求用户登录&#xff1b;实现显示用户信息&#xff1b;退出登录&#xff1b;发布博客 该部分的后端代码如下&#xff1a; Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws Ser…...

椭圆滤波器

之前的文章 信号去噪 中列出了7种常用的信号去噪算法&#xff0c;对于后两种算法——深度学习和奇异值分解(SVD)&#xff0c;我现在也不太理解&#xff0c;就先不写了。 很多朋友留言又提了一些算法&#xff0c;今天一起来聊聊椭圆滤波器。 椭圆滤波器&#xff08;Elliptic F…...

Mac 下安装golang环境

一、下载安装包 安装包下载地址 下载完成&#xff0c;直接继续----->下一步到结束即可安装成功&#xff1b; 安装成功之后&#xff0c;验证一下&#xff1b; go version二、配置环境变量 终端输入vim ~/.zshrc进入配置文件&#xff0c;输入i进行编辑 打开的不管是空文本…...

前端面试大纲

一、CSS 1.说一下CSS的盒模型。 在HTML页面中的所有元素都可以看成是一个盒子 盒子的组成&#xff1a;内容content、内边距padding、边框border、外边距margin 盒模型的类型&#xff1a; 标准盒模型 margin border padding content IE盒模型 margin content(border padd…...

CAN(Controller Area Network)是一种用于在汽车和工业领域中进行通信的串行总线系统(附加案例)

CAN&#xff08;Controller Area Network&#xff09;是一种用于在汽车和工业领域中进行通信的串行总线系统。它是一种高可靠性、多主机、多节点通信协议&#xff0c;主要用于实时控制和数据传输。 CAN数据是指在CAN总线上通过CAN协议进行通信传输的数据。CAN总线上的数据被分…...

代码随想录day53|1143.最长公共子序列、 1035.不相交的线、 53. 最大子序和

1143.最长公共子序列 dp[i][j]&#xff1a;长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j] 因此是if(nums1[i-1] nums2[j-1]) 1035.不相交的线 和上一题一样 53. 最大子序和 int result dp[0]; 不是0&#xff0c;因为dp[i]有…...

xilinx fpga ddr mig axi

硬件 参考&#xff1a; https://zhuanlan.zhihu.com/p/97491454 https://blog.csdn.net/qq_22222449/article/details/106492469 https://zhuanlan.zhihu.com/p/26327347 https://zhuanlan.zhihu.com/p/582524766 包括野火、正点原子的资料 一片内存是 1Gbit 128MByte 16bit …...

《golang设计模式》第三部分·行为型模式-04-迭代器模式(Iterator)

文章目录 1. 概念1.1 角色1.2 类图 2. 代码示例2.1 需求2.2 代码2.3 类图 1. 概念 迭代器&#xff08;Iterator&#xff09;能够在不暴露聚合体内部表示的情况下&#xff0c;向客户端提供遍历聚合元素的方法。 1.1 角色 InterfaceAggregate&#xff08;抽象聚合&#xff09;…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

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

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

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践

前言&#xff1a;本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中&#xff0c;跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南&#xff0c;你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案&#xff0c;并结合内网…...