【计算机网络】网络编程套接字UDP服务器客户端的简单模拟

需要云服务器等云产品来学习Linux的同学可以移步/–>腾讯云<–/官网,轻量型云服务器低至112元/年,优惠多多。(联系我有折扣哦)

文章目录

  • 1. 前置知识
    • 1.1 源IP和目的IP
    • 1.2 端口号
    • 1.3 TCP协议和UDP协议初识
    • 1.4 网络字节序
    • 1.5 socket编程接口
      • 1.5.1 socket常见API
      • 1.5.2 sockaddr结构
  • 2. 简单的UDP服务端和客户端代码实现
    • 2.1 makefile文件的编写
    • 2.2 server端的编写
      • 2.2.1 需要调用的函数
      • 2.2.2 代码
      • 2.2.3 测试
    • 2.3 client端的编写
      • 2.3.1 需要调用的函数
      • 2.3.2 代码
      • 2.3.3 测试

1. 前置知识

1.1 源IP和目的IP

每台主机都有自己的IP地址,当数据在进行通信的时候,除了要发送的数据外,在报头里面还要包含发送方的IP和接收方的IP,这里发送方的IP就被称为源IP,接收方的IP就是目的IP

现在有了源IP和目的IP之后,是不是就能够通信了呢?

源IP和目的IP的出现只能标识两台主机的唯一性,但是在主机上还存在很多个进程,在发送数据的时候肯定是由一个进程发送给另一个进程的,所以还需要标识两台主机上进程的唯一性

为了更好的表示一台主机上服务进程的唯一性,用端口号port标识服务进程、客户端进程的唯一性。

1.2 端口号

端口号(port)是传输层协议的内容

  • 端口号是一个2字节16位的整数
  • 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理
  • IP地址 + 端口号能够标识网络上的某一台主机的某一个进程
  • 一个端口号只能被一个进程占用,一个进程可以占用多个端口号

由上面可知:IP地址(主机的全网唯一性)+该主机上的端口号(主机上进程唯一性)可以标识一个唯一的进程。那么最终网络通信的本质就是进程间通信

进程已经有pid标识唯一性了,为什么还要有端口号呢?

  • 系统是系统,网络是网络,单独设置为了让网络和系统解耦
  • 客户端需要每次都能够找到服务端进程,这就要求了服务端的唯一性不能发生任何改变,pid在每次进程创建的时候都有可能改变
  • 不是所有进程都需要进行网络通信的(需要端口号),但是所有进程都需要pid。

1.3 TCP协议和UDP协议初识

1. TCP协议

此处我们先对TCP(Transmission Control Protocol 传输控制协议)有一个直观的认识; 后面我们再详细讨论TCP的一些细节问题.

  • 传输层协议
  • 有连接
  • 可靠传输
  • 面向字节流

2. UDP协议

UDP(User Datagram Protocol 用户数据报协议

  • 传输层协议
  • 无连接
  • 不可靠传输
  • 面向数据报

1.4 网络字节序

在之前的文章中数据在内存中的存储,我们提到了数据内存中存储的时候有大端小端之分,磁盘文件中的多字节数据相对于文件中的偏
移地址也有大端小端之分。

大端机器和和小端机器也有可能通信,那么这个时候如何定义网络通信的数据流呢?

发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

实际上这些函数名是很好记的,按照作用来组合即可,其中h表示host,是当前主机的意思;n表示network,是网络的意思;l表示32位长整数,s表示16位短整数

例如:htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回

1.5 socket编程接口

1.5.1 socket常见API

// 创建 socket 文件描述符(TCP/UDP,客户端+服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号(TCP/UDP,服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

1.5.2 sockaddr结构

套接字不仅支持跨网络通信,还支持本地的进程间通信(域间套接字)。对于不同的通信方式,需要使用的接口在细节上是有一些不同的。所以套接字提供了两个结构体sockaddr_insockaddr_un结构体,其中前者是用于跨网络通信的,后者用于本地进程间通信。但是除此之外,通信的方法基本都是相同的,所以为了让两种通信方式能使用同一套函数接口,所以就定义了sockaddr结构体,该结构体与sockaddr_insockaddr_un的结构都不相同,但这三个结构体头部的16个比特位都是一样的,这个字段叫做协议家族。

三个结构体的内容如下:

image-20240214220403653

所以当我们在传参的时候,就直接传sockaddr结构体,在函数内部做识别,执行对应的操作。

注意: 实际我们在进行网络通信时,定义的还是sockaddr_in这样的结构体,只不过在传参时需要将该结构体的地址类型进行强转为sockaddr*罢了。

既然传递的类型是指针类型,那么为什么不用C语言的void *类型,而进行了这么复杂的设计?

在网络通信的结构进行设计的时候,C语言的标准还没有出来。所以进行了设计,在之后为了向前兼容,就沿用了这套接口

2. 简单的UDP服务端和客户端代码实现

这里我们采用C和C++混编的方式,就封装成类来实现,通过makefile进行自动化编译。

2.1 makefile文件的编写

# 这是构建本项目的makefile文件cc=g++ # 这里cc是一个变量,表示构建项目使用的编译器.PHONY:all # 构建一个伪对象,表示我们要同时构建两个目标文件
all: udpServer udpClient# 这里是两个目标文件的依赖和构建方法
udpServer:udpServer.cc $(cc) -o $@ $^ -std=c++11
udpClient:udpClient.cc $(cc) -o $@ $^ -std=c++11# 删除所有产生的目标文件
.PHONY:clean
clean:	rm -f udpServer udpClient 

2.2 server端的编写

2.2.1 需要调用的函数

1. socket

头文件:#include <sys/types.h>#include <sys/socket.h>
函数原型:int socket(int domain, int type, int protocol);
参数解释:domain:表示通信类型(本质是一个宏),socket支持多种通信方式,一般来说有本地和网络两种。AF_INET表示使用IPv4进行网络通信;AF_UNIX表示本地通信。详情见man手册type:表示套接字提供服务的类型,如SOCK_STREAM:流式服务(TCP策略);SOCK_DGRAM:数据报服务(UDP策略)protocol:表示对应的协议,实际上可以由前两个参数确定,所以这里设计成0没有问题
函数描述:创建一个用于网络通信的文件描述符
返回值:调用成功返回文件描述符,否则返回-1同时设置错误码

2. bind

头文件:#include <sys/types.h>#include <sys/socket.h>
函数原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数解释:sockfd:需要设置的sockfdaddr:将需要设置的内容对应的结构体指针强转为struct sockaddr *再传入(为了统一接口)addrlen:传入的addr对应的变量大小
函数描述:将local设置到内核中。
返回值:调用成功返回0,否则返回-1,同时设置错误码

为什么要使用bind,不能直接通信吗?

通信是要使用网络的,最终也是要通过OS进行通信。所以相关信息需要让OS来维护,我们没有办法直接操作OS内核数据结构,因此需要通过系统调用bind来将相关内容设置进内核

sockaddr相关结构的定义:

  1. sockaddr的定义:
typedef unsigned short int sa_family_t;#define	__SOCKADDR_COMMON(sa_prefix) \sa_family_t sa_prefix##familystruct sockaddr{__SOCKADDR_COMMON (sa_);char sa_data[14];};

所以可以看出来sockaddr是一个结构体,内部有两个成员变量;其中第一个变量是16位的地址类型,一个14位的地址数据。__SOCKADDR_COMMON是一个宏,作用是将括号内的内容拼接到family前面,最终形成一个sa_family_t类型的变量

  1. sockaddr_in的定义
struct sockaddr_in{__SOCKADDR_COMMON (sin_); // 定义sin_family变量,16位的数据类型in_port_t sin_port;	// 16端口号struct in_addr sin_addr; // 32位IP地址/* 数据填充(无意义)  */unsigned char sin_zero[sizeof (struct sockaddr) -__SOCKADDR_COMMON_SIZE -sizeof (in_port_t) -sizeof (struct in_addr)];};

3. bzero

头文件:#include <strings.h>
函数原型:void bzero(void *s, size_t n);
参数解释:s:需要设置的变量的地址n:变量的长度
函数描述:从s开始的n个字节设置为0,类似于memset

4. inet_aton

头文件:#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>
函数原型与描述:int inet_aton(const char *cp, struct in_addr *inp); // 将点分十进制的IP地址转换为符合网络字节序的二进制形式存放到in_addr结构体中
参数解释:cp:点分十进制形式的C式ip字符串inp:存放网络字节序的二进制i形式IP
返回值:如果地址有效,则返回非0,如果地址无效则返回0

5. recvfrom

头文件:#include <sys/types.h>#include <sys/socket.h>
函数原型:ssize_t recvfrom(int sockfd, void *buf, size_t len, int falgs, struct sockaddr *src_addr, socklen_t *addrlen);
函数描述:接收信息
参数解释:sockfd:用于接收的sockfdbuf:len:flags:读取的方式,默认为0,阻塞读取src_addr:收到消息除了本身,还得知道是谁发的,输入输出型参数,返回对应的消息内容是从哪一个client发来的addrlen:结构体大小
返回值:返回接收到的字节数,出错返回-1同时设置错误码。如果发送方正常关闭,返回0

2.2.2 代码

/*udpServer.hpp*/
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <string.h>
#include <cerrno>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>namespace Server
{using func_t = std::function<void(std::string, uint16_t, std::string)>;static void Usage(){std::cout << "\nUsage:\n\t./udpServer local_port\n\n";}enum // 枚举出错类型{USAGE_ERR = 1,SOCKET_ERR,BIND_ERR};const static std::string defaultIP = "0.0.0.0";const int gnum = 1024; // 处理任务的缓冲区大小class udpServer{public:udpServer(const func_t &cb, const uint16_t &port, const std::string &ip = defaultIP): _port(port), _ip(ip) {}// 这里初始化要做的事情有两件: 1. 创建sockfd 2.bind端口号和ipvoid initServer(){_sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 参数1:这里使用AF_INET表示使用IPv4进行网络通信;参数2:我们这里使用UDP策略;参数3:这里使用0表示默认if (_sockfd == -1)                        // 差错处理{std::cerr << "socket error " << errno << strerror(errno) << std::endl;exit(SOCKET_ERR);}std::cout << "socket sucess : " << _sockfd << std::endl;// 在当前函数的栈帧上创建一个local对象,设置相关属性,然后将相关属性bind到系统内核中struct sockaddr_in local;                       // 这里struct sockaddr_in类型需要头文件arpa/inet.hbzero(&local, sizeof(local));                   // 在填充数据之前首先将对象内部元素清空,这里使用bzerolocal.sin_family = AF_INET;                     // 设定协议家族local.sin_port = htons(_port);                  // 设置端口号,这里端口号需要首先转换成网络端口号local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 设置ip,这里的ip是string类型,但是实际在传输的时候使用的是整型,所以需要转换,这里使用inet_addr// inet_addr的作用有两个: 1.string -> uint32_t; 2. htonl()int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local)); // 将local设置到内核中,即bindif (n == -1){std::cerr << "bind error " << errno << strerror(errno) << std::endl;exit(BIND_ERR);}// 至此初始化的操作完成}void start() // 让服务器开始跑起来{// 服务器的本质是一个死循环,在循环内部处理收到的任务char buffer[gnum];while (true){// 1. 读取数据struct sockaddr_in peer; // 定义一个变量用于接收数据socklen_t len = sizeof(peer);ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);// a. 数据是什么 b. 谁发的if (n > 0){buffer[n] = 0;std::string clientIp = inet_ntoa(peer.sin_addr); // 转换网络字节序, 点分十进制uint16_t clientPort = ntohs(peer.sin_port);std::string message = buffer;std::cout << clientIp << "[" << clientPort << "]# " << message << std::endl;// 2. 处理任务_callback(clientIp, clientPort, message);}}}private:// 成员变量分析:作为一个服务端进程,我们首先需要一个端口号port和一个本地ip// 还需要有一个文件描述符sockfd,用于进行通信(网络通信是基于文件的,所以使用的都是文件的一套内容,包括fd)int _sockfd;     // socket文件描述符std::string _ip; // 本地ipuint16_t _port;  // 服务进程端口号func_t _callback;};
}

2.2.3 测试

image-20240219193511547

查看网络情况就可以用指令netstat:

  • -a:显示所有连线中的Socket;
  • -e:显示网络其他相关信息;
  • -i:显示网络界面信息表单;
  • -l:显示监控中的服务器的Socket;
  • -n:直接使用ip地址(数字),而不通过域名服务器;
  • -p:显示正在使用Socket的程序识别码和程序名称;
  • -t:显示TCP传输协议的连线状况;
  • -u:显示UDP传输协议的连线状况;

image-20240219193733492

那么本地测试没有问题,这里使用的是云服务器,能不能bind公网IP呢?

./udpServer 8080 公网IP

image-20240219193937013

云服务器是虚拟化的服务器,不能直接bind你的公网IP,可以绑定内网IP(ifconfig显示的);如果是虚拟机或者独立真实的Linux环境,你可以bind你的IP;

  • 如何保证云服务器能够被别人访问?

    实际上,一款网络服务器不建议指明一个IP,也就是不要显示地绑定IP。服务器IP可能不止一个,如果只绑定一个明确的IP,最终的数据可能用别的IP来访问端口号,访问不出来,所以真实的服务器IP一般采用INADDR_ANY(全0,任意地址)代表任意地址bind

最终代码:

/*udpServer.hpp*/
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <string.h>
#include <cerrno>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>namespace Server
{using func_t = std::function<void(std::string, uint16_t, std::string)>;static void Usage(){std::cout << "\nUsage:\n\t./udpServer local_port\n\n";}enum // 枚举出错类型{USAGE_ERR = 1,SOCKET_ERR,BIND_ERR};const static std::string defaultIP = "0.0.0.0";const int gnum = 1024; // 处理任务的缓冲区大小class udpServer{public:udpServer(const func_t &cb, const uint16_t &port, const std::string &ip = defaultIP): _port(port), _ip(ip) {}// 这里初始化要做的事情有两件: 1. 创建sockfd 2.bind端口号和ipvoid initServer(){_sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 参数1:这里使用AF_INET表示使用IPv4进行网络通信;参数2:我们这里使用UDP策略;参数3:这里使用0表示默认if (_sockfd == -1)                        // 差错处理{std::cerr << "socket error " << errno << strerror(errno) << std::endl;exit(SOCKET_ERR);}std::cout << "socket sucess : " << _sockfd << std::endl;// 在当前函数的栈帧上创建一个local对象,设置相关属性,然后将相关属性bind到系统内核中struct sockaddr_in local;                       // 这里struct sockaddr_in类型需要头文件arpa/inet.hbzero(&local, sizeof(local));                   // 在填充数据之前首先将对象内部元素清空,这里使用bzerolocal.sin_family = AF_INET;                     // 设定协议家族local.sin_port = htons(_port);                  // 设置端口号,这里端口号需要首先转换成网络端口号// local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 设置ip,这里的ip是string类型,但是实际在传输的时候使用的是整型,所以需要转换,这里使用inet_addrlocal.sin_addr.s_addr = INADDR_ANY;// inet_addr的作用有两个: 1.string -> uint32_t; 2. htonl()int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local)); // 将local设置到内核中,即bindif (n == -1){std::cerr << "bind error " << errno << strerror(errno) << std::endl;exit(BIND_ERR);}// 至此初始化的操作完成}void start() // 让服务器开始跑起来{// 服务器的本质是一个死循环,在循环内部处理收到的任务char buffer[gnum];while (true){// 1. 读取数据struct sockaddr_in peer; // 定义一个变量用于接收数据socklen_t len = sizeof(peer);ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);// a. 数据是什么 b. 谁发的if (n > 0){buffer[n] = 0;std::string clientIp = inet_ntoa(peer.sin_addr); // 转换网络字节序, 点分十进制uint16_t clientPort = ntohs(peer.sin_port);std::string message = buffer;std::cout << clientIp << "[" << clientPort << "]# " << message << std::endl;// 2. 处理任务_callback(clientIp, clientPort, message);}}}private:// 成员变量分析:作为一个服务端进程,我们首先需要一个端口号port和一个本地ip// 还需要有一个文件描述符sockfd,用于进行通信(网络通信是基于文件的,所以使用的都是文件的一套内容,包括fd)int _sockfd;     // socket文件描述符std::string _ip; // 本地ipuint16_t _port;  // 服务进程端口号func_t _callback;};
}
/*udpServer.cc*/
#include "udpServer.hpp"
#include <memory>using namespace Server;void handleMessage(std::string clientIp, uint16_t clientPort, std::string message)
{std::cout << "我是一个回调函数执行,收到的消息是:" << message << std::endl;
}//  调用的指令 :./udpServer port
int main(int argc, char *argv[])
{// 解析指令if (argc != 2){Usage();exit(USAGE_ERR);}uint16_t port = atoi(argv[1]);// 创建对象,进行通信std::unique_ptr<udpServer> usvr(new udpServer(handleMessage, port));usvr->initServer(); // 初始化服务进程usvr->start();      // 开始监听return 0;
}

image-20240219194336093

2.3 client端的编写

2.3.1 需要调用的函数

头文件:#include <sys/types.h>#include <sys/socket.h>
函数原型:ssize_t sendto(int sockfd, void *buf, size_t len, int falgs, struct sockaddr *dest_addr, socklen_t *addrlen);
函数描述:向指定IP和端口号的进程发送信息
参数解释:sockfd:用于发送的sockfdbuf:要发送的数据的地址len:要发送的数据长度flags:发送的方式,默认为0,阻塞发送dest_addr:接收方的IP和端口号addrlen:结构体大小
返回值:调用成功返回接发送的字节数,出错返回-1同时设置错误码

2.3.2 代码

/*udpCliet.hpp*/
#pragma once
#include <iostream>
#include <string>
#include <string.h>
#include <cerrno>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>namespace Client
{enum // 枚举出错类型{USAGE_ERR = 1,SOCKET_ERR,BIND_ERR,};class udpClient{public:udpClient(const std::string &serverIp, const uint16_t &serverPort): _serverIp(serverIp), _serverPort(serverPort), _sockfd(-1), _quit(false) {}void initClient(){// 1.创建套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd == -1){std::cerr << "socket error: " << errno << " : " << strerror(errno) << std::endl;exit(SOCKET_ERR);}std::cout << "socket success: " << _sockfd << std::endl;// 2. bind// a. Client要不要bind   -- 当然要// b. Client要不要程序员显示bind   -- 不用,由OS自动形成端口进行bind。因为Client是在客户机上运行的,客户机上同时会有很多其他的Client在跑,不能确定哪些端口已经被使用// c. OS在什么时候,如何bind    -- 在Client发起网络连接时自动执行bind操作}void run(){struct sockaddr_in server;memset(&server, 0, sizeof server);server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(_serverIp.c_str());server.sin_port = htons(_serverPort);std::string message;while(!_quit){std::cout << "Please Enter# ";std::cin >> message;sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));}}private:int _sockfd;           // 套接字std::string _serverIp; // 服务端IPuint16_t _serverPort;  // 服务端端口号bool _quit;            // 客户端退出标志};
} // namespace Client
/*udpCliet.cc*/
#include "udpClient.hpp"
#include <string>
#include <memory>using namespace Client;
static void Usage(std::string proc)
{std::cout << "\nUsage:\n\t" << proc << " server_ip server_port\n\n";
}// ./udpClient server_ip server_port
int main(int argc, char *argv[])
{// 解析指令if (argc != 3){Usage(argv[0]);exit(USAGE_ERR);}std::string serverIp = argv[1];uint16_t serverPort = atoi(argv[2]);std::unique_ptr<udpClient> ucli(new udpClient(serverIp, serverPort));ucli->initClient();ucli->run();return 0;
}

2.3.3 测试

./udpClient 127.0.0.1 18989 # 客户端启动
./udpServer 18989 # 服务端启动

image-20240219202846231


本节完…

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# OpenCvSharp DNN Low Light image Enhancement

目录 介绍 效果 模型信息 项目 代码 下载 C# OpenCvSharp DNN Low Light image Enhancement 介绍 github地址&#xff1a;https://github.com/zhenqifu/PairLIE 效果 模型信息 Model Properties ------------------------- ------------------------------------------…

文献学习-1-Continuum Robots for Medical Interventions

Chapt 5. 连续体机构分析 5.1 文献学习 5.1.1 Continuum Robots for Medical Interventions Authors: PIERRE E. DUPONT , Fellow IEEE, NABIL SIMAAN , Fellow IEEE, HOWIE CHOSET , Fellow IEEE, AND CALEB RUCKER , Member IEEE 连续体机器人在医学上得到了广泛的应用&a…

卷积神经网络的基本结构

卷积神经网络的基本结构 与传统的全连接神经网络一样&#xff0c;卷积神经网络依然是一个层级网络&#xff0c;只不过层的功能和形式发生了变化。 典型的CNN结构包括&#xff1a; 数据输入层&#xff08;Input Layer&#xff09;卷积层&#xff08;Convolutional Layer&#x…

Avalonia学习(二十四)-系统界面

目前项目式练习&#xff0c;界面内容偏多&#xff0c;所以不给大家贴代码了&#xff0c;可以留言交流。此次为大家展示的是物联项目的例子&#xff0c;仅仅是学习&#xff0c;我把一些重点列举一下。 界面无边框 以前的样例主要是通过实现控件来完成的&#xff0c;前面已经有窗…

新算法UoT助力AI提问——主动寻求信息,任务完成率提高57.8%

引言&#xff1a;信息寻求在不确定性环境中的重要性 在不确定性环境中&#xff0c;信息寻求的能力至关重要。在许多实际应用中&#xff0c;如医学诊断和故障排除&#xff0c;解决任务所需的信息并非一开始就给出&#xff0c;而需要通过提问后续问题来主动寻求&#xff08;例如…

使用AndroidStudio调试Framework

1.前言 最近在工作过程中&#xff0c;涉及到FW的一些修改&#xff0c;比如PhoneWindowManager&#xff0c;只能通过加日志看打印的方式查看一些内容&#xff0c;比较低效&#xff0c;所以想了解一下FW的调试方式&#xff0c;后来发现AS就可以调试FW.我平时都是在Docker服务器编…

网站管理新利器:免费在线生成 robots.txt 文件!

&#x1f916; 探索网站管理新利器&#xff1a;免费在线生成 robots.txt 文件&#xff01; 你是否曾为搜索引擎爬虫而烦恼&#xff1f;现在&#xff0c;我们推出全新的在线 robots.txt 文件生成工具&#xff0c;让你轻松管理网站爬虫访问权限&#xff0c;提升网站的可搜索性和…

Redis(十四)双写一致性工程案例

文章目录 问题概述canal功能安装部署mysql配置canal服务端canal客户端&#xff08;Java程序&#xff09; 问题概述 canal https://github.com/alibaba/canal 功能 数据库镜像数据库实时备份索引构建和实时维护(拆分异构索引、倒排索引等)业务 cache 刷新带业务逻辑的增量数据…

OpenCV 4基础篇| 色彩空间类型转换

目录 1. 色彩空间基础2. 色彩空间类型2.1 GRAY 色彩空间2.2 BGR 色彩空间2.3 CMY(K) 色彩空间2.4 XYZ 色彩空间2.5 HSV 色彩空间2.6 HLS 色彩空间2.7 CIEL*a*b* 色彩空间2.8 CIEL*u*v* 色彩空间2.9 YCrCb 色彩空间 3. 类型转换函数3.1 cv2.cvtColor3.2 cv2.inRange 1. 色彩空间…

安达发|APS生产排程软件6大核心技术

APS生产排程软件是一种先进的生产计划和调度工具&#xff0c;它通过整合企业内外部资源&#xff0c;实现生产计划的优化和生产过程的自动化控制。APS生产排程软件的核心技术包括产品工艺数据管理&#xff08;PDM&#xff09;、客户需求管理&#xff08;CRM&#xff09;、高级计…

生成自己的rola模型简单版四步完成

工具准备&#xff1a;秋叶整合包&#xff0c;lora 训练器 秋叶整合包地址&#xff1a;https://pan.quark.cn/s/2c832199b09b#/list/share lora训练器地址&#xff1a;lora训练器_免费高速下载|百度网盘-分享无限制 (baidu.com) 第一章 图像预处理 根据自己需要准备一个图片…

软件工具安装遇到bug、报错不知道怎么解决?看这里!

前言 本文举例了几个常见的软件工具使用问题&#xff0c;文末会提供一些我自己整理和使用的工具资料 。 "在追逐零 Bug 的路上&#xff0c;我们不断学习、改进&#xff0c;更加坚定自己的技术信念。让我们相信&#xff0c;每一个 Bug 都是我们成长的机会。" 一、VM…

LeetCode 热题 100 | 二叉树(中下)

目录 1 基础知识 1.1 队列 queue 1.2 栈 stack 1.3 常用数据结构 1.4 排序 2 98. 验证二叉搜索树 3 230. 二叉搜索树中第 K 小的元素 4 199. 二叉树的右视图 菜鸟做题忘了第几周&#xff0c;躺平过了个年TT 1 基础知识 1.1 队列 queue queue<type> q…

【办公类-16-07-04】合并版“2023下学期 中班户外游戏(有场地和无场地版,一周一次)”(python 排班表系列)

背景需求&#xff1a; 把 无场地版&#xff08;贴周计划用&#xff09; 和 有场地版&#xff08;贴教室墙壁上用&#xff09; 组合在一起&#xff0c;一个代码生成两套。 【办公类-16-07-02】“2023下学期 周计划-户外游戏 每班1周五天相同场地&#xff0c;6周一次循环”&…

论文阅读——ONE-PEACE

ONE-PEACE: EXPLORING ONE GENERAL REPRESENTATION MODEL TOWARD UNLIMITED MODALITIES 适应不同模态并且支持多模态交互。 预训练任务不仅能提取单模态信息&#xff0c;还能模态间对齐。 预训练任务通用且直接&#xff0c;使得他们可以应用到不同模态。 各个模态独立编码&am…

Maxwell安装部署

1 Maxwell输出格式 database&#xff1a;变更数据所属的数据库table&#xff1a;变更数据所属的表type&#xff1a;数据变更类型ts&#xff1a;数据变更发生的时间xid&#xff1a;事务idcommit&#xff1a;事务提交标志&#xff0c;可用于重新组装事务data&#xff1a;对于inse…

无人机数据链技术,无人机数据链路系统技术详解,无人机数传技术

早期的无人机更多的为军事应用服务&#xff0c;如军事任务侦查等&#xff0c;随着技术和社会的发展&#xff0c;工业级无人机和民用无人机得到快速的发展&#xff0c;工业级无人机用于农业植保、地理测绘、电力巡检、救灾援助等&#xff1b;民用无人机用于航拍、物流等等领域。…

美国Mercari煤炉注册教程,还不快来Get!

想要掘金全球电商市场&#xff0c;美国的Mercari平台绝对值得关注。Mercari&#xff0c;也被称作煤炉&#xff0c;类似于我们国内的闲鱼二手交易平台&#xff0c;它同时拥有美国和日本两个市场。其中&#xff0c;美国市场的消费需求稳定且持续增长&#xff0c;成为了许多跨境电…

医卫医学试题及答案,分享几个实用搜题和学习工具 #笔记#笔记#微信

收录了大量考试类型的题库&#xff0c;大到考公&#xff0c;小到知识竞赛&#xff0c;题库资源算是比较丰富的了。操作起来也不难&#xff0c;我们只需输入题目内容&#xff0c;即可快速搜索出答案&#xff0c;而且它在给出答案的同时还会附带解析。最重要的是&#xff0c;搜题…

​LeetCode解法汇总105. 从前序与中序遍历序列构造二叉树

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 描述&#xff1a; 给定两个整…