【计算机网络】socket 网络套接字

网络套接字

  • 一、端口号
    • 1. 认识端口号
    • 2. socket
  • 二、认识TCP协议和UDP协议
    • 1. TCP协议
    • 2. UDP协议
  • 三、网络字节序
  • 四、socket 编程
    • 1. socket 常见API
    • 2. sockaddr 结构
    • 3. 编写 UDP 服务器
      • (1)socket()
      • (2)bind()
      • (3)recvfrom()
      • (4)sendto()
      • (5)udp 服务端和客户端
    • 4. 地址转换函数
      • (1)相关接口
      • (2)关于 inet_ntoa
    • 5. 编写 TCP 服务器
      • (1)listen()
      • (2)accept()
      • (3)con
      • (4)守护进程
      • (5)tcp 服务端和客户端

一、端口号

1. 认识端口号

实际上我们两台机器在进行通信时,是应用层在进行通信,应用层必定会推动下层和对方的上层进行通信。

其实网络协议栈中的下三层,主要解决的是数据安全可靠的送到远端机器。而用户使用应用层软件,完成数据发送和接收的。那么用户要使用软件,首先需要把这个软件启动起来!所以软件启动起来,本质就是进程!所以两台机器进行通信,本质是两台机器之上的应用层在通信,也就是两个进程之间在互相交换数据!所以网络通信的本质就是进程间通信!只不过在网络通信中的公共资源是网络,通过网络协议栈利用网络资源,让两个不同的进程看到了同一份资源!

在网络协议栈中,在传输层怎么把数据正确交给上层应用层呢?怎么知道交给哪一个应用呢?所以就要求上层应用层和传输层之间必须协商一种方案,让我们把数据准确交给上层,这个方案我们称为端口号。所以在传输层的报头中,必须要有原端口号目的端口号,也就是根据目的端口号就可以决定这个数据的有效载荷要交给上层应用的哪一个!所以对于端口号无论对于客户端和服务端,都能唯一的标识该主机上的一个网络应用层的进程!

我们可以这样理解,其实在传输层当中,操作系统会形成一张哈希表,哈希表中的类型是 task_struct*,每一个应用层都要和该哈希表绑定端口号,本质就是根据端口号在哈希表里做哈希运算,如果该位置已经被占用了,就不能被绑定了,因为一个端口号只能被一个进程绑定;如果该位置没有被使用,就把该进程的pcb地址放在该位置上。

2. socket

因为在公网上,IP地址 能表示唯一的一台主机,端口号 port,用来标识该主机上的唯一的一个进程,所以 IP + port 就可以标识全网唯一的一个进程!那么我们在网络通信时,只需要在对应的报头上填上原IP目的IP原port目的port,就可以将报文交给另一个主机的进程,这种基于 IP + port 的通信方式,我们称为 socket.

那么端口号和进程pid有什么区别呢?进程pid也能标识一台主机上的唯一进程啊?因为首先,不是所有的进程都要通信,但是所有的进程都要有pid!其次是为了使系统和网络功能解耦!

二、认识TCP协议和UDP协议

下面我们先认识一下两个传输层协议:

1. TCP协议

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

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

2. UDP协议

此处我们也是对UDP(User Datagram Protocol 用户数据报协议)有一个直观的认识;后面再详细讨论。

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

三、网络字节序

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,网络数据流同样有大端小端之分。那么如何定义网络数据流的地址呢?

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

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

在这里插入图片描述

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

四、socket 编程

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);

2. sockaddr 结构

socket API 是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的 UNIX Domain Socket;然而,各种网络协议的地址格式并不相同

在这里插入图片描述

  • IPv4IPv6 的地址格式定义在 netinet/in.h 中,IPv4地址用 sockaddr_in 结构体表示,包括16位地址类型, 16位端口号和32位IP地址.;
  • IPv4IPv6 地址类型分别定义为常数 AF_INETAF_INET6,这样,只要取得某种 sockaddr 结构体的首地址,不需要知道具体是哪种类型的 sockaddr 结构体,就可以根据地址类型字段确定结构体中的内容;
  • socket API 可以都用 struct sockaddr* 类型表示,在使用的时候需要强制转化成 sockaddr_in;这样的好处是程序的通用性,可以接收IPv4IPv6,以及 UNIX Domain Socket 各种类型的 sockaddr 结构体指针做为参数。

3. 编写 UDP 服务器

(1)socket()

下面我们编写一个 UDP 服务器。首先需要做的是创建套接字,使用到的接口是 socket()

在这里插入图片描述

第一个参数是我们创建的套接字的域,即使用 IPv4 的网络协议还是 IPv6 的网络协议,目前我们只需要关注这两个即可,如下图:

在这里插入图片描述

第二个参数表示当前 socket 对应的类型,也就是相当于这个套接字未来给我们提供什么服务,是面向字节流的还是面向用户数据报的,如下:

在这里插入图片描述

第三个参数表示的是协议类型,目前我们不需要传这个参数。

而返回值相当于是一个文件描述符,所以创建一个套接字的本质,在底层就相当于是打开一个文件,只不过以前的 struct file 指向的是键盘、显示器这样的设备;而现在指向的是网卡设备。

在这里插入图片描述

(2)bind()

创建套接字成功之后,接下来就要绑定端口号,使用到的接口是 bind(),如下:

在这里插入图片描述

其中第一个参数就是创建套接字时的返回值;第二个参数是一个结构体;第三个参数是结构体的长度。但是我们在网络套接字编程的时候不用第二个参数类型的结构体,这个结构体它只是设计接口用,我们实际用的是 sockaddr_in 类型的结构体,只需要在传参的时候进行强转即可。我们可以使用 bzero() 接口将该结构体清0;

我们是要使用 bind 来让套接字和我们往该结构体中填充的网络信息要关联起来,所以我们需要想该结构体中填充对应的字段。该结构体中有如下字段:

在这里插入图片描述

对应下图:

在这里插入图片描述

其中 sin_zero 为该结构体的填充字段,也就是这些字段不用填充,当作占位符即可;sin_addr 代表 ip 地址;sin_port 代表服务器所使用的端口号;sin_family 代表该结构体对应的网络协议类型,IPv4 或者 IPv6.

因为我们在给对方发送数据的时候,我们也一定需要让对方知道我们是谁,所以我们需要将端口号携带上,发送给对方,这样对方把数据处理完,就可以给我们响应回来。所以端口号是要在网络里来回发送的,也就是需要保证我们的端口号是网路字节序列,因为该端口号是要给对方发送的。所以这里我们就需要用到主机序列转网络序列的接口,由于端口号是两个字节,所以用到的接口为 htons()

在这里插入图片描述

由于我们用户一般用的都是点分十进制字符串风格的 IP 地址,也就是 0.0.0.0 这种风格,每个点分的范围是 0~255,每个字符一个字节,远远超过结构体中要求的 32 位 ip 地址,也就是四字节。所以我们需要将该字符串类型转换为 uint32_t 的类型,那么用到的接口是 inet_addr(),它的作用就是将字符串风格的 ip 地址转化为网络风格的 uint32_t 类型,如下图:

在这里插入图片描述

同端口号一样,IP 地址也需要保证是网络字节序列。那么它的返回值类型 in_addr_t 其实就是符合网路字节序列的 uint32_t 的类型。

上面我们已经把准备工作做好了,接下来我们就需要使用 bind() 接口进行绑定,本质就是把我们定义的 struct 结构体设置进内核,设置进指定的套接字内部。

(3)recvfrom()

接下来我们就需要在指定的一个套接字里获取数据内容,使用到的接口是 recvfrom(),如下图:

在这里插入图片描述

第一个参数就是网络文件描述符;第二个参数和第三个参数分别表示我们提供的缓冲区和它的长度,读到的数据就会放在缓冲区中;第三个参数设为0就是默认使用阻塞方式;最后两个参数又是熟悉的结构体,由于我们需要知道这些数据是谁给我们发的,因为我们有可能也要将数据给对方返回。所以最后两个参数其实是输出型参数。

返回值成功就是对应的长度,否则就是-1,如下:

在这里插入图片描述

(4)sendto()

将数据发送回给对方使用到的接口为 sendto(),如下:

在这里插入图片描述

参数和 recvfrom() 的参数类似,这里不再介绍了。而最后两个参数是输入型参数,我们要将数据发回给对方,首先需要知道对方是谁,而我们上面已经通过 recvfrom() 获取到了对方的结构体信息,所以直接使用该结构体信息即可。

(5)udp 服务端和客户端

其中通过使用上面的接口编写的一个简单的接收客户端的字符串信息,并进行简单的加工的 udp 服务器代码链接为:UDP.

其中 udp server 的代码如下:

				#pragma once#include <iostream>#include <string>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <strings.h>#include <unistd.h>#include <cstring>#include <functional>#include "log.hpp"using func_t = std::function<std::string(const std::string&)>;//typedef std::function<std::string(const std::string&)> func_t;std::string default_ip = "0.0.0.0";uint16_t default_port = 8080;log lg;class UdpServer{public:UdpServer(const uint16_t &port = default_port, const std::string &ip = default_ip): _port(port), _ip(ip), _isrunning(false), _sockfd(0){}void Init(){// 1.创建 udp 套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0); // AF_INET == PF_INETif (_sockfd < 0){lg(Fatal, "socket create faild, sockfd: %d", _sockfd);exit(1);}lg(Info, "socket create success, sockfd: %d", _sockfd);// 2.绑定端口号// 2.1 准备数据struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port); // 主机序列转网络序列// local.sin_addr.s_addr = inet_addr(_ip.c_str());   // 1.string -> uint32_t  2.保证uint32_t是网络序列local.sin_addr.s_addr = htonl(INADDR_ANY);// 2.2 开始bindint n = bind(_sockfd, (const sockaddr *)&local, sizeof(local));if (n < 0){lg(Fatal, "bind faild, errno: %d, err message: %s", errno, strerror(errno));exit(2);}lg(Info, "bind success, errno: %d, err message: %s", errno, strerror(errno));}void Run(func_t func){_isrunning = true;char buffer[1024];while (_isrunning){// 记录客户端发来时的结构体信息struct sockaddr_in client;socklen_t len = sizeof(client);ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&client, &len);if (n < 0){lg(Warning, "recvfrom error, errno: %d, err message: %s", errno, strerror(errno));continue;}buffer[n] = 0;// 对数据进行简单的加工std::string info = buffer;std::string echo_string = func(info);// 发送回给对方sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (const sockaddr *)&client, len);}}~UdpServer(){if (_sockfd > 0)close(_sockfd);}private:int _sockfd;uint16_t _port;std::string _ip;bool _isrunning;};

udp client 的代码如下:

				#include <iostream>#include <cstdlib>#include <unistd.h>#include <strings.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>using namespace std;void Usage(string proc){cout << "\n\rUsage: " << proc << " serverip serverport\n" << endl;}int main(int argc, char* argv[]){if(argc != 3){Usage(argv[0]);exit(0);}string server_ip = argv[1];uint16_t server_port = stoi(argv[2]);sockaddr_in server;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(server_ip.c_str());server.sin_port = htons(server_port);socklen_t len = sizeof(server);// client 也需要 bind,只不过不需要用户显示 bind,一般由OS自由随机选择// 系统会在首次发送数据的时候给我们bindint sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd < 0){cout << "socker error" << endl;return 1;}string message;char buffer[1024];while(true){cout << "Plase Enter@ ";getline(cin, message);// 发送数据sendto(sockfd, message.c_str(), message.size(), 0, (sockaddr*)&server, len);// 当服务器进行简单的加工处理后会发送回来,此时客户端再次获取sockaddr_in temp;socklen_t size = sizeof(temp);ssize_t s = recvfrom(sockfd, buffer, 1023, 0, (sockaddr*)&temp, &len);if(s > 0){buffer[s] = 0;cout << buffer << endl;}}close(sockfd);return 0;}

main 函数:

				#include <iostream>#include <vector>#include <memory>#include <cstdio>#include "UdpServer.hpp"using namespace std;void Usage(string proc){cout << "\n\rUsage: " << proc << " port[1024+]\n" << endl;}// 处理字符串的方法string Handler(const std::string& str){string res = "Server get a message: ";res += str;cout << res << endl;return res;}// 远程执行指令的方法string ExcuteCommand(const string& cmd){FILE* fp = popen(cmd.c_str(), "r");if(nullptr == fp){perror("popen");return "error";}string result;char buffer[4096];while(true){char* tmp = fgets(buffer, sizeof(buffer), fp);if(tmp == nullptr) break;result = buffer;}pclose(fp);return result;}int main(int argc, char* argv[]){if(argc != 2){Usage(argv[0]);exit(0);}uint16_t port = stoi(argv[1]);unique_ptr<UdpServer> svr(new UdpServer(port, "127.0.0.1"));svr->Init();svr->Run(ExcuteCommand);return 0;}

有关代码中的细节:

  • 有关 IP 地址

云服务器禁止直接bind公网ipbind ip 地址为0,表示的含义是任意地址绑定,这种是比较推荐的做法。当 IP 地址为 127.0.0.1 时,表示进行的是本地传输测试,不会进行跨网传输。

  • 有关 port

其中 0~1023 的端口号是系统内定的端口号,一般都要有固定的应用层协议使用,例如 http:80,https:443;所以我们一般绑端口号,一般绑1024以上的。

  • popen() 系统调用

popen() 是一个被封装起来的管道和子进程执行命令的应用。

在这里插入图片描述

它的第一个参数就是需要执行的命令,在底层它会帮我们进行 fork() 创建子进程,并让父子进程建立管道,然后让子进程把它的运行结果通过管道再返回给调用方。如果调用方想得到 command 指令的运行结果,可以通过文件指针的方式读取。第二个参数相当于是打开这个命令的方式,我们使用 “r” 即可。使用完毕后使用 pclose() 关闭该文件指针即可。

其中,我们可以使用 netstat -nlup 查看系统中所有的 udp 信息,并且把进程信息也显示出来。

我们还可以将以上代码修改成为多线程代码,链接为:多线程UDP.

4. 地址转换函数

(1)相关接口

我们只介绍基于 IPv4socket 网络编程,sockaddr_in 中的成员 struct in_addr sin_addr 表示32位 的 IP 地址,但是我们通常用点分十进制的字符串表示 IP 地址,以下函数可以在字符串表示和 in_addr 表示之间转换。我们在上面的 bind() 中也使用了地址转换函数 inet_addr().

  • 字符串转 in_addr 的函数:

      			#include <arpa/inet.h>int inet_aton(const char* strptr, struct in_addr* addrptr);in_addr_t inet_addr(const char* strptr);int inet_pton(int family, const char* strptr, void* addrptr);
    
  • in_addr 转字符串的函数:

      			char* inet_ntoa(struct in_addr inaddr);const char* inet_ntop(int family, const void* addrptr, char* strptr, size_t len);
    

其中 inet_ptoninet_ntop 不仅可以转换 IPv4in_addr,还可以转换 IPv6in6_addr,因此函数接口是 void* addrptr.

(2)关于 inet_ntoa

inet_ntoa 这个函数返回了一个 char*,很显然是这个函数自己在内部为我们申请了一块内存来保存 ip 的结果,那么是否需要调用者手动释放呢?

在这里插入图片描述

man 手册上说,inet_ntoa 函数,是把这个返回结果放到了静态存储区。这个时候不需要我们手动进行释放。

5. 编写 TCP 服务器

(1)listen()

TCP 是面向连接的,服务器一般是比较被动的,所以服务器一直处于一种等待连接到来的状态,这个工作叫做监听状态,使用到的接口是 listen(),如下:

在这里插入图片描述

第一个参数为指定的套接字,通过该套接字等待新连接的到来。第二个参数我们后面再介绍,暂时设为10左右即可。返回值,成功返回0,失败返回-1.

(2)accept()

因为 TCP 是面向连接的,所以在正式通信之前,先要把连接建立起来,使用到的接口为 accept(),该接口的作用是获取一个新的连接,如下:

在这里插入图片描述

第一个参数为我们刚刚设置为监听状态的套接字;后两个参数和 recvfrom() 的后两个参数一样,都是输出型参数,也就是谁给我们发的 TCP 报文,那么对应的套接字信息就会通过这两个参数返回出来。

而返回值成功返回一个文件描述符;否则返回-1;那么返回值也是一个文件描述符,我们原本也有一个文件描述符,为什么会有两个 sockfd 呢?我们该用哪个呢?其实它们分工是明确的,我们原本定义的 sockfd,即被创建的,被 bind 的,被监听的套接字,它的工作是从底层获取新的连接;而未来真正提供通信服务的,是 accept() 返回的套接字!

至此,我们可以使用 telnet 进行指定服务的一个远程连接,后面跟上 IP 地址和端口号即可;它在底层默认使用的就是 TCP.

(3)con

由于在 TCP 中,客户端是要连接服务器的,所以服务端需要有一个能够向服务器发起连接的接口,该接口为 connect(),如下:

在这里插入图片描述

该接口的作用是通过指定的套接字,向指定的网络目标地址发起连接。后两个参数和 sendto() 的后两个参数一样。返回值成功返回0,失败返回-1.

TCP 客户端也需要 bind,但是和 UDP 一样,不需要显示的 bind,系统会在客户端发起 connect 的时候,进行自动随机 bind.

我们可以使用 netstat -nltp 查看系统中所有 TCP 的信息,并把进程信息显示出来。

(4)守护进程

在我们登录 Linux 的时候,Linux 系统会给我们形成一个会话,而且会为每个会话创建一个 bash 进程,这个 bash 就可以为用户提供命令行服务。每个会话中只能存在一个前台进程,但是可以存在多个后台进程,而键盘信号只能发送给前台进程。前台和后台进程的区别就是是否拥有键盘文件,它们都可以向显示器打印,而只有前台进程才能从键盘,即标准输入获取数据!

如果我们不想后台进程向显示器打印的数据影响我们,我们可以将它的打印数据重定向到文件中,例如:

在这里插入图片描述

其中 [1] 表示后台任务号,后面数字表示进程 PID.

而查看后台任务的指令为:jobs,如下:

在这里插入图片描述

如果我们想把后台进程提到前台,可以使用 fg 任务号,如下:

在这里插入图片描述

如果想把它重新放回后台,我们可以使用 ctrl + z 将该进程暂停。然后使用 bg 任务号 将该进程重新启动,如下:

在这里插入图片描述

接下来我们再运行几个后台进程,例如使用 sleep,方便观察 Linux 中的进程间关系,使用 ps axj | head -1 && ps axj | grep -Ei 'a.out|sleep' 查看它们的进程信息:

在这里插入图片描述

其中 PPID、PID 我们都认识,而 PGID 表示的是进程组IDSID 表示 session id,即会话 id.

而系统中可能会存在多个 session,所以系统需要管理多个 session.

我们可以看到,./a.out 进程的 PIDPGID 是一样的,所以它就是自成进程组的。而三个 sleep 分别是三个不同的进程,但是它们的 PGID 却是同一个,而且是用管道建立的进程的第一个进程的 PID,所以它们三个自成一组,而组长是多个进程中的第一个。那么进程组和任务有什么关系呢?任务是要指派给进程组的!所以我们需要校正一下以前的说法,我们把前台进程称为前台任务,后台进程称为后台任务,因为可能某一个后台任务里面,可能会包含多个进程。但是无论有几个进程组完成对应的任务,在同一个会话内启动的,SID 是一样的!那么上面中的 SID 到底是谁呢?我们可以查看一下:

在这里插入图片描述

如上图,我们可以看到,它是 bash!所以就是以 bashpid 去构建了一个 session

这种后台进程会收到用户登录和退出的影响,如果我们不想受到任何用户登录和注销的影响,我们可以将进程守护进程化。什么是守护进程呢?我们把自成进程组自成会话的进程称为守护进程!那么我们该如何做到呢?下面我们认识一个接口:setsid(),如下:

在这里插入图片描述

该接口的作用就是,哪个进程调用该接口,就把该进程的组ID设置为会话ID,也就是让进程独立成会话。

在这里插入图片描述

返回值成功返回进程的ID,否则返回-1.

注意,该接口不能由进程组的组长直接调用,那么怎么才能保证不是组长调用呢?所以我们可以使用 fork() 创建子进程调用!所以守护进程的本质,也是孤儿进程!

(5)tcp 服务端和客户端

接下来我们结合上面所学的知识,编写一个 TCP 服务器,并将它守护进程化,代码链接:

其中在守护进程中,我们的代码中是充满大量的打印的,而这些打印默认是向标准输出打的,也就是向显示器上打了,而对于守护进程来说,就不应该向显示器上打了,所以我们需要一个解决方案。而 Linux 中存在一种字符文件,叫做 /dev/null,只要我们向该文件写入,都会被该文件丢弃掉,如果我们向该文件读取,什么也读取不到。所以我们只需要将所有的输出向该文件写入即可。我们也可以将打印信息写入文件中。

另外,TCP 在通信时是全双工的,也就是可以同时读写的。在底层操作系统给 TCP 提供两个缓冲区,一个发送缓冲区,一个接收缓冲区,我们在用 TCP 的同时,别人也在用,所以别人也会有上面两个缓冲区,所以当我们发送数据,是先把我们的数据拷贝到我们的 TCP 的发送缓冲区,然后通过网络会发送到对方的接收缓冲区,反过来也同理,如下图:

在这里插入图片描述

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

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

相关文章

【Unity编辑器扩展】Unity编辑器主题颜色设置工具

可以用来应用和自定义你的Unity编辑器。14个主题可供选择。轻松创建自己的主题。 主题展示:

明御运维审计与风险控制系统漏洞复现

简介 明御运维审计与风险控制系统是安恒信息在多年运维安全管理的理论和实践经验积累的基础上,采用B/S架构,集“身份认证、账户管理、控制权限、日志审计”于一体,支持多种字符终端协议、文件传输协议、图形终端协议、远程应用协议的安全监控与历史查询,具备全方位运维风险…

ChatGPT丨“成像光谱遥感技术中的AI革命:ChatGPT应用指南“

遥感技术主要通过卫星和飞机从远处观察和测量我们的环境&#xff0c;是理解和监测地球物理、化学和生物系统的基石。ChatGPT是由OpenAI开发的最先进的语言模型&#xff0c;在理解和生成人类语言方面表现出了非凡的能力。本文重点介绍ChatGPT在遥感中的应用&#xff0c;人工智能…

【医学大模型】临床推理 + 大模型,推理感知型诊断框架

临床推理 大模型&#xff0c;推理感知型诊断框架 提出背景Module II-1: 少数样本CoT推理Module II-2: 单模态学生蒸馏Module II-3: 多模态学生蒸馏 提出背景 论文&#xff1a;https://arxiv.org/pdf/2312.07399.pdf 这篇文章提出了一种新的诊断框架&#xff0c;专注于利用大型…

Android T 远程动画显示流程其二——动画的添加流程(更新中)

前言 接着上篇文章分析 Android T 远程动画显示流程其一 切入点——处理应用的显示过渡 下面&#xff0c;我们以从桌面点击一个应用启动的场景来分析远程动画的流程&#xff0c;窗口添加的流程见Android T WMS窗口相关流程 这里我们从AppTransitionController.handleAppTran…

HAL STM32 HW I2C DMA + SSD1306/SH1106驱动示例

HAL STM32 HW I2C DMA SSD1306/SH1106驱动示例 &#x1f4cd;硬件I2C DMA驱动参考&#xff1a;https://blog.csdn.net/weixin_45065888/article/details/118225993 &#x1f4cc;github上的相关项目&#xff1a;https://github.com/taburyak/STM32_OLED_SSD1306_HAL_DMA &am…

五种多目标优化算法(MOGWO、MOJS、NSWOA、MOPSO、MOAHA)性能对比,包含6种评价指标,9个测试函数(提供MATLAB代码)

一、5种多目标优化算法简介 1.1MOGWO 1.2MOJS 1.3NSWOA 1.4MOPSO 1.5MOAHA 二、5种多目标优化算法性能对比 为了测试5种算法的性能将其求解9个多目标测试函数&#xff08;zdt1、zdt2 、zdt3、 zdt4、 zdt6 、Schaffer、 Kursawe 、Viennet2、 Viennet3&#xff09;&#xff0…

Linux服务器安装MySQL8

进入安装目录 /usr/local下载 wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.20-linux-glibc2.12-x86_64.tar.xz解压,重命名 tar -Jxvf mysql-8.0.20-linux-glibc2.12-x86_64.tar.xzmv mysql-8.0.20-linux-glibc2.12-x86_64 mysql8创建用户组、用户 # 需要…

fastApi笔记01-路径参数

路径参数 使用与 Python 格式化字符串相同的语法来声明路径"参数"或"变量" from fastapi import FastAPIapp FastAPI()app.get("/items/{item_id}") def read_item(item_id):return {"item_id": item_id} http://127.0.0.1:8000/i…

SpringSecurity安全框架

我们使用这个springSecurity安全框架,作用是认证,授权,将用户的权限和对应的资源进行绑定,默认的是在内存中保存的,实际开发中,是需要根据项目业务的需求对某些方法进行重写,使数据库中权限对应的资源进行绑定,就是查看当前登录的用户所扮演的角色,该角色有哪些权限 授权 1内…

【操作系统】

计算机操作系统 计算机是如何让用户得到好的体验什么是操作系统&#xff08;OS&#xff09;操作系统如何管理 计算机是如何让用户得到好的体验 计算机系统是由计算机硬件和软件组成的。用户使用计算机&#xff0c;比如在文本文件填写内容&#xff0c;通过邮箱发送邮件&#xf…

Aloudata StarRocks 直播预告:指标平台的物化加速实践

数据指标的管理、研发和应用一直存在着诸多痛点&#xff0c;这些挑战促使了对指标平台解决方案的需求不断增长。2月29日&#xff08;星期四&#xff09;19:00&#xff0c;Aloudata 将与 StarRocks 携手举办线上直播&#xff0c;深入揭秘第三代指标平台物化加速的强大能力&#…

大蟒蛇(Python)笔记(总结,摘要,概括)——第10章 文件和异常

目录 10.1 读取文件 10.1.1 读取文件的全部内容 10.1.2 相对文件路径和绝对文件路径 10.1.3 访问文件中的各行 10.1.4 使用文件的内容 10.1.5 包含100万位的大型文件 10.1.6 圆周率中包含你的生日吗 10.2 写入文件 10.2.1 写入一行 10.2.2 写入多行 10.3 异常 10.3.1 处理Ze…

基于springboot+vue的课程答疑系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

外站群服务器的特性及使用优势

随着互联网的快速发展&#xff0c;站群服务器在网站运营中扮演着越来越重要的角色。相较于国内站群服务器&#xff0c;国外站群服务器因其独特的特性和使用优势&#xff0c;受到了众多网站管理员的青睐。本文将对国外站群服务器的特性及使用优势进行科普介绍。 一、国外站群服务…

【Spring Cloud】实现微服务调用的负载均衡

文章目录 什么是负载均衡自定义实现负载均衡启动shop-product微服务通过nacos查看微服务的启动情况自定义实现负载均衡 基于Ribbon实现负载均衡添加注解修改服务调用的方法Ribbon支持的负载均衡策略通过修改配置来调整 Ribbon 的负载均衡策略通过注入Bean来调整 Ribbon 的负载均…

JS进阶——解构赋值

数组解构 基本&#xff1a; let [a, b, c] [1, 2, 3]; // a 1 // b 2 // c 3 可嵌套 let [a, [[b], c]] [1, [[2], 3]]; // a 1 // b 2 // c 3 可忽略 let [a, , b] [1, 2, 3]; // a 1 // b 3 不完全解构 let [a 1, b] []; // a 1, b undefined 剩余运…

如何创造价值写给自己的笔记

人工智能统领全文 在深入探讨这篇概述之前&#xff0c;我们首先需要理解一个核心观点&#xff1a;生产力的进步与生产关系的变革是相辅相成的。这一点在历史的长河中不断得到验证&#xff0c;从工业时代到信息时代&#xff0c;再到如今由人工智能引领的新时代&#xff0c;每一…

探索D咖智能饮品机器人的工作原理:科技、材料与设计的相互融合

智能饮品机器人是近年来随着人工智能和自动化技术的发展而崭露头角的一种创新产品。它将科技、材料和设计相互融合&#xff0c;为消费者带来了全新的饮品体验。下面D咖来探索智能饮品机器人的工作原理&#xff0c;以及科技、材料和设计在其中的作用。 首先&#xff0c;智能饮品…

Observability:使用 OpenTelemetry 和 Elastic 监控 OpenAI API 和 GPT 模型

作者&#xff1a; 来自 Elastic David Hope ChatGPT 现在非常火爆&#xff0c;甚至席卷了整个互联网。 作为 ChatGPT 的狂热用户和 ChatGPT 应用程序的开发人员&#xff0c;我对这项技术的可能性感到非常兴奋。 我看到的情况是&#xff0c;基于 ChatGPT 的解决方案将会呈指数级…