Linux —— Socket编程(一)

news2024/11/16 11:33:29

一、本篇重点

1. 认识IP地址、端口号、网络字节序等网络编程中的基本概念

2. 学习Socket api的基本用法

3. 能够实现一个简单的udp客户端/服务器

二、基本概念

1. 理解源IP地址和目的IP地址

简单的理解,IP地址是用于标识一台机器的,我们通过IP地址去找到我们需要通信的机器,源IP地址就是一个数据包的源头机器的IP地址,目的IP地址就是该数据包要传送到的机器IP地址。

2. 端口号

2.1 什么是端口号?

我们通过IP地址去标识一台具体的机器,但是一个机器中有着许多的进程,我们最终是要将信息交给某个对应的目标进程的,例如我微信发的消息,最终也是要在对方机器中找到对方微信的进程,并将信息交给该进程,所以在网络通信中,还需要对进程进行标识,端口号就是标识唯一进程的

2.2 为什么不能用进程ID去代替端口号呢?

进程ID也可以作为标识一个进程的唯一标识符,但为了达到解耦合的设计,我们的网络系统不能去以来进程管理系统,而是要单独独立的去设计,这是为了方便维护,所以单独设计了端口号去标识唯一的进程。

2.3 端口号的性质

a. 端口号是一个2字节16位的整数

b. 一个端口号只能被一个进程占用,但一个进程可以有多个端口号

c. 在网络通信中,一个进程根据IP地址和端口号,可以找到唯一的一个目的主机中的一个唯一进程,所以网络通信的本质,也可以认为就是进程间的通信

3. TCP协议和UDP协议

3.1 什么是TCP协议和UDP协议?

TCP协议和UDP协议是传输层的协议,它们的任务是为了在不同主机上的应用程序之间提供可靠或高效的数据传输服务。

两种协议各有其应用场景,这里的可靠和不可靠只认为是一种特性,而不分好坏!

3.2 TCP协议(传输控制协议)

a. TCP协议具有可靠的传输特性,存在着确认机制,发送方发送数据后,接收方会返回确认信息,确保数据被正确的接收,如果接受方一定时间内没有确认收到,则会重新传送。

b. 还会通过对数据进行编号,确保数据按顺序到达接收方,并且接收方可以识别丢失或者重复的数据

c. 流量控制,防止发送方发送数据过快,导致接收方来不及处理而出现数据丢失或者缓冲区溢出,接收方通过通知发送方自己接收窗口的大小,控制发送方的发送速度

d. 拥塞控制,当网络拥塞时,TCP会自动调整发送数据的速度,避免加重网络负担,保证网络的稳定性

e. 有连接、面向字节流的服务

3.3 UDP协议

a. 提供高效的数据传输,无连接、不可靠、面向数据报的服务

b. 相比于TCP协议,UDP协议注重的是高效性,由于没有复杂的确认和重传机制,UDP可以快速的发送数据,减少传输延迟

4. 网络字节序

4.1 什么是网络序?

网络字节序是一种计算机网络中规定的字节顺序标准

计算机储存数据时分大端和小端,所谓小端就是低权位的数据放在对应低地址中,反之则是大端,在网络通信中,不同的机器大小端是不确定的,也没办法将源机器是什么端这个信息放在报头去告诉目的机器,因为报头本身也是数据信息。

所以在网络通讯中,我们对大小端进行了统一,但凡发送到网络中的信息,都以大端的方式去发送和读取,这就是网络字节序定义的标准

4.2 网络字节序的相关接口

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);//将源主机中32位长整形转换成网络字节序要求的大端格式
uint16_t htons(uint16_t hostshort);//将源主机中16位短整形转换成网络字节序要求的大端格式
uint32_t ntohl(uint32_t netlong);//将网络中32位长整形(大端)转换成当前主机的格式
uint16_t ntohs(uint16_t netshort);//将网络中16位短整形(大端)转换成当前主机的格式

三、Socket编程

1. 什么是Socket编程?

Socket编程,即套接字编程是一种实现网络通信的编程方式,根据网络协议分层来看,Socket编程是程序员在利用系统调用接口去访问传输层的部分去进行网络通信,其中创建出来的Socket变量可以认为它是一个能够去沟通网络的文件,我们将主机ip和端口号通过它提供的接口与该网络文件进行关联,然后利用接口可以从中去读取或者写入信息,并且利用它去向指定的网络中其他主机的某个进程发消息,只要知道IP地址和端口号就可以,当然还有各种处理连接错误、数据丢失、网络阻塞等问题以及解决方案,这里我们先简单通过一个UDP的小demo去一个个认识相关的接口和参数。

2. 简单实现UDP

关于Socket的接口,我打算通过一个简单的udp模拟实现的小demo去边写边学习和整理,最后再进行一个整合和总结,这也是我在学习和熟悉这个接口的一种思路和方式。

2.1 大体的框架和功能

我们希望模拟实现一个基于UDP协议的一个服务器,模拟client端(客户端)和server端(服务端)相互跨网络通信的功能,为了便于测试,我们先简单的要求,client端向server端去发送一个字符串类型的消息,然后server端接受到后打印信息,并原封不动的把信息发送回给client端,并打印。

基于此设计,我们先将代码的基本框架搭起来:

我们先建立好makefile、udp_client.cc、udp_server.cc、udp_client.hpp、udp_server.hpp文件,并且搭好基本框架,完成makefile的编辑。

.PHONY:all
all: udp_client udp_server

udp_client:udp_client.cc
	g++ -o $@ $^ -std=c++11
udp_server:udp_server.cc
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -f udp_client udp_server

2.2 udp_server.hpp的设计

(1)基本接口框架(初步)

我们先写服务端,采用面向对象的设计思路先搭建一个简单的框架, 首先是设计服务器的类成员参数,我们初步认为,至少需要一个套接字变量,该服务器的端口号,所在主机的IP信息,并且要有构造和析构,作为一个服务器,对外提供的接口,这里我们简单设计一个初始化的接口(Init)和启动服务器(Start)接口。

#pragma once

#include<iostream>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

namespace chk
{
    class UdpServer
    {
    public:
        UdpServer() //对成员变量完成初始化
        {}
        void Init() //创建出套接字,并绑定端口号和ip
        {}
        void Start()//时刻读取套接字中的信息数据,并将其返回
        {}
        ~UdpServer()//析构
        {}

    private:
    int _sock;
    uint16_t _port;
    std::string _ip;
    };
}

———————————————————————————————————————————

前提知识

知识点一:基本认识

这里我们要先对套接字有简单初步的认识,我们现在做的这个类是一个服务端的类,我们要让这个服务端能够链接到网络,在网络中实现通信,那么我们就需要用到套接字,这个套接字我们可以简单的先认为它就是一个特殊的能够在网络中得到或者向网络发生数据的一个文件,创建套接字的过程,就是创建出这样一个网络文件,而返回的类型int则是文件描述符fd。

知识点二:相关的头文件

#include<sys/types.h>   // 包含很多系统数据类型
#include<sys/socket.h>  // 包含了基本的 socket 函数和数据结构定义。
#include<netinet/in.h>  // 定义了 Internet 地址族相关的结构和函数。
#include<arpa/inet.h>   // 提供 IP 地址转换函数。

———————————————————————————————————————————

(2)构造函数的设计

构造函数的任务是将变量完成初始化,由于我们是在云服务器写的代码,所以IP地址我们不需要让外部传入,而是之后会有相关的接口,交给操作系统自己去为我们定IP地址,对于类成员中ip就可以去掉,我们只需要让外部传入该服务器的端口号,并且为了测试方便,我们将其端口号打印出来

    public:
        //对成员变量完成初始化
        UdpServer(uint16_t port = default_port): _port(port) 
        {
            std::cout << "server port: " << _port << std::endl;
        }

———————————————————————————————————————————

前提知识

知识点一:一款服务器上一般不要指明某一个确定的IP

如果服务器程序明确指定了一个具体的 IP 地址,那么当服务器的网络环境发生变化时,比如更换了网络接口或者 IP 地址发生了变动,就需要修改服务器程序并重新编译部署。而如果不指定具体 IP 地址,使用通配地址(如 INADDR_ANY),服务器程序可以在不同的网络环境下无需修改代码即可运行,提高了程序的可移植性。

———————————————————————————————————————————

(3)Init 接口的设计

初始化的任务是要将套接字创建出来,然后是将套接字和IP以及端口号绑定起来

———————————————————————————————————————————

前提知识

知识点一:创建套接字函数

int socket(int domain, int type, int protocol);

domain:指定通信域,常见的有AF_INET(IPv4网络协议)AF_INET6(IP6网络协议)

type:指定套接字类型,常见的有SOCK_STREAM(面向字节流,对应TCP)SOCK_DGRAM(面向数据报,对应UDP)

protocol:通常设置为0,表示使用默认协议。

返回值一般是文件文件描述符fd,若失败则返回小于0的值

知识点二:对于结构体 struct sockaddr 的认识

该结构体的作用是将地址信息(IP地址、端口号)等打包起来,一起交给相关要用到的接口,例如绑定套接字之类的操作,但是Socket编程是一层抽象的网络编程接口,它不仅支持网络通信,它也支持本地的进程间通信,并且还有不同协议的情况(例如IPv4和IPv6),各种网络协议的地址格式并不相同,例如本地通信的地址格式是根据路径去确定的,而网络通信是根据IP和端口号,它能够支持这些地址格式不一样的协议,统一使用一套接口实现通信,就以来于这个结构体的设计

struct sockaddr结构体的前16位统一都是用来标识地址类型的,而后面14字节的地址空间则是根据你实际情况的不同,去选择不同的结构体去完成你相关地址的填充,但在使用接口时,都得将其类型强转成struct sockaddr*类型,这就是一种多态的思想去实现和满足了多种不同情况下的需求

知识点三:struct sockaddr_in

对应我们现在的需要,我们要使用的结构体类型就是sockaddr_in,所以还需要详细的认识其中的成员。

sin_family:第一行的宏转换后就是这个,它指定的是地址族,对于IPv4就设置为AF_INET

sin_port:端口号,要以网络字节序去表示,所以这里一般在填写的时候,还要配合网络字节序的接口htons

sin_addr:里面封装的内容实际就是IP地址,可以使用inet_addr 或 inet_pton函数将点分十进制的 IP 地址字符串转换为合适的格式并存储在 sin_addr.s_addr中。当然这里由于我们用的是云服务器,前面提到云服务器的IP地址不能填唯一确定的,而是这里需要填INADDR_ANY

知识点四:绑定套接字和struct sockaddr的函数接口 bind

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sockfd : 是通过socket函数创建的套接字描述符。

addr:该结构体的解释在知识点三和知识点四有讲解,对于 IPv4 通常是struct sockaddr_in类型,对于 IPv6 是struct sockaddr_in6类型。这个地址结构包含了要绑定的 IP 地址和端口号等信息。

addrlen:结构体的大小

返回值说明:成功返回0,失败返回-1,并将错误码进行设置

———————————————————————————————————————————

代码参考

        void Init() // 创建出套接字,并绑定端口号和ip
        {
            // 1. 创建套接字
            _sock = socket(AF_INET, SOCK_DGRAM, 0);
            if (_sock < 0)
            {
                std::cerr << "create socket error: " << strerror(errno) << std::endl;
                exit(1);
            }
            std::cout << "bind socket success: " << _sock << std::endl; // 3

            // 2. 构建struct sockaddr_in结构体
            struct sockaddr_in local;
            bzero(&local, sizeof(local));       // 初始化
            local.sin_family = AF_INET;         // IPv4
            local.sin_port = htons(_port);      // 端口号
            local.sin_addr.s_addr = INADDR_ANY; // 服务器下的ip

            // 3. 绑定套接字和sockaddr_in
            int n = bind(_sock, (struct sockaddr *)&local, sizeof(local));
            if (n < 0)
            {
                std::cerr << "bind error: " << strerror(errno) << std::endl;
                exit(2);
            }
            std::cout << "bind socket success: " << _sock << std::endl;
        }

(4)接口start的设计

start接口的任务就是为了完成在网络(套接字)中等待获取数据,然后将获取到的数据再发送回去

———————————————————————————————————————————

前提知识

知识点一:接口 recvfrom -- 从套接字中获取信息

int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);

sockfd:套接字描述符

buf:接受数据的数据缓冲区指针,用于接受数据的

len:数据缓冲区的大小

flags:一般设置为0,表示默认的接收行为,也可以设置一些标志位来影响接收操作的行为,例如 MSG_PEEK(查看数据但不实际读取,数据仍留在接收队列中)、MSG_WAITALL(尽可能等待接收完整的请求数据量)等,但这些标志位的使用相对较少

from:是一个struct sockaddr*类型的结构体指针,它用来获取发送方的地址信息

fromlen:是一个指向整数的指针,用于指定 from 所指向的地址结构的长度。在调用 recvfrom 之前,应该将 *fromlen 设置为 sizeof(struct sockaddr)(或对应的具体地址结构的大小);当 recvfrom 返回时,fromlen 会被修改为实际存储在 from 中的地址结构的长度

返回值说明:成功返回收到的字节数,返回值为0说明对端已经关闭了连接,返回-1表示接受数据发生了错误

知识点二:接口 sendto -- 向套接字中写入信息

int sendto(int sockfd, const void *buf, int len, unsigned int flags, const struct sockaddr *to, int tolen);

sockfd:是通过socket函数创建的套接字描述符

buf:要向套接字发送的数据所在的缓冲区指针

len:指定要发生内容的长度

flags: 一般设置为 0,表示默认的发送行为。也可以设置一些标志位来影响发送操作的行为,不过这些标志位的使用相对较少

to:要发送到的目的地的struct sockadd的指针

tolen:to的大小

知识点三:将网络字节序的二进制形式IP地址转化为点分十进制字符串格式函数 -- inet_ntoa

char *inet_ntoa(struct in_addr in);

in:一个 struct in_addr类型的结构体,包含了以网络字节序存储的 IPv4 地址。

———————————————————————————————————————————

代码参考

        void Start() // 时刻读取套接字中的信息数据,并将其返回
        {
            char buffer[1024];
            while(true)
            {
                // 收数据
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                int n = recvfrom(_sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
                if(n>0) buffer[n] = '\0';
                else continue;

                //收到信息后打印出来:对方ip+端口号+内容
                std::cout << inet_ntoa(peer.sin_addr) << " - " << ntohs(peer.sin_port) << " : " << buffer << std::endl;

                // 发回去
                sendto(_sock,buffer,strlen(buffer),0,(struct sockaddr*)&peer,sizeof(peer));
            }
        }

2.3 udp_server.cc 文件部分的设计

我们假定最终希望以"./udp_server port"的方式去调用,例如"./udp_server 1010",基于此设计一个使用说明手册,用智能指针创建服务器类,初始化,调用即可。

代码参考

#include"udp_server.hpp"
#include<string>
#include<memory>
#include<cstdio>
using namespace std;
using namespace chk;

// 我们最终希望以 ./udp_server port 的形式去启动
static void usage(string proc)//使用手册
{
    std::cout << "Usage:\n\t" << proc << " port\n" << std::endl;
}

int main(int argc,char* argv[])
{
    if(argc != 2)
    {
        usage(argv[0]);
        exit(3);
    }
    uint16_t port = atoi(argv[1]);
    unique_ptr<UdpServer> usvr(new UdpServer(port));
    usvr->Init();
    usvr->Start();
    
    return 0;
}

2.4 client端的设计

———————————————————————————————————————————

前提知识

通常来说,对于一个服务器而言,它的IP和端口是不能随便改变的,所以在server端的设计中,我们主动的去bind,但对于客户端而言,端口号和IP地址是有操作系统去为我们分配端口号以及绑定的,而不能自己去bind

———————————————————————————————————————————

我们这里主要的任务实际就只有发送信息和接受信息,不需要自己bind,为了后续改进不太麻烦,这里选择不采用封住,而是都集中在main函数执行。

代码参考

#include"udp_client.hpp"//基本要用到的头文件
using namespace std;


static void usage(std::string proc)
{
    std::cout << "Usage:\n\t" << proc << " serverip serverport\n" << std::endl; 
}

// ./udp_client server_ip server_port
int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        exit(3);
    }
    string server_ip = argv[1];
    uint16_t server_port = atoi(argv[2]);

    //1. 创建套接字
    int sock = socket(AF_INET,SOCK_DGRAM,0);
    if(sock < 0)
    {
        cerr << "client : create socket error" << endl;
        exit(2);
    }

    //2. 创建server端的struct sockaddr
    struct sockaddr_in server;
    memset(&server,0,sizeof(server));//初始化方案2
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(server_ip.c_str());
    server.sin_port = htons(server_port);

    //3. 客户端测试
    while(true)
    {
        // 用户发送消息
        string messages;
        cout << "client : " ;
        cin >> messages;
        // 发送到sock
        sendto(sock,messages.c_str(),messages.size(),0,(struct sockaddr*)&server,sizeof(server));

        // 接受返回的信息
        char buffer[1024];
        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);
        int n = recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&len);
        if(n < 0)
        {
            buffer[n] = 0;
            cout << buffer << endl;
        }
    }
    return 0;
}

2.5 errno.hpp

我们发现,在本次的小demo中,有许多需要用到错误码的情况,我们需要对每个退出原因进行标识和解释,所以我们建立一个errno.hpp,里面以枚举的方式去标识各种不同的错误,这对于项目中的设计是一个良好的习惯

#pragma once

enum
{
    USAGE_ERR = 1,
    SOCKET_ERR,
    BIND_ERR
};

2.6 测试

测试部分我们在客户端需要提供主机IP,由于我们在本机上进行测试,我们的IP默认可以是“127.0.0.1”,这个IP是本地环回,表示的就是当前主机,通常用来进行本地通信或者测试

3. 接口知识点整理

我们通过简单实现UDP代码的方式,一边设计代码,一边理解相关的接口,中间穿插了许多接口以及关于各种理解的知识点,在这部分我们做一个整理归纳我们用到的一些接口

3.1 网络编程相关的头文件

#include<sys/types.h>   // 包含很多系统数据类型
#include<sys/socket.h>  // 包含了基本的 socket 函数和数据结构定义。
#include<netinet/in.h>  // 定义了 Internet 地址族相关的结构和函数。
#include<arpa/inet.h>   // 提供 IP 地址转换函数。

3.2  socket -- 创建套接字函数

int socket(int domain, int type, int protocol);

domain:指定通信域,常见的有AF_INET(IPv4网络协议)AF_INET6(IP6网络协议)

type:指定套接字类型,常见的有SOCK_STREAM(面向字节流,对应TCP)SOCK_DGRAM(面向数据报,对应UDP)

protocol:通常设置为0,表示使用默认协议。

返回值: 一般是文件文件描述符fd,若失败则返回小于0的值

3.3 struct sockaddr_in

sin_family:第一行的宏转换后就是这个,它指定的是地址族,对于IPv4就设置为AF_INET

sin_port:端口号,要以网络字节序去表示,所以这里一般在填写的时候,还要配合网络字节序的接口htons

sin_addr:里面封装的内容实际就是IP地址,可以使用inet_addr 或 inet_pton函数将点分十进制的 IP 地址字符串转换为合适的格式并存储在 sin_addr.s_addr中。当然这里由于我们用的是云服务器,前面提到云服务器的IP地址不能填唯一确定的,而是这里需要填INADDR_ANY

3.4 bind  绑定套接字和struct sockaddr的函数接口

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sockfd : 是通过socket函数创建的套接字描述符。

addr:该结构体的解释在知识点三和知识点四有讲解,对于 IPv4 通常是struct sockaddr_in类型,对于 IPv6 是struct sockaddr_in6类型。这个地址结构包含了要绑定的 IP 地址和端口号等信息。

addrlen:结构体的大小

返回值说明:成功返回0,失败返回-1,并将错误码进行设置

3.4 recvfrom -- 从套接字中获取信息

int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);

sockfd:套接字描述符

buf:接受数据的数据缓冲区指针,用于接受数据的

len:数据缓冲区的大小

flags:一般设置为0,表示默认的接收行为,也可以设置一些标志位来影响接收操作的行为,例如 MSG_PEEK(查看数据但不实际读取,数据仍留在接收队列中)、MSG_WAITALL(尽可能等待接收完整的请求数据量)等,但这些标志位的使用相对较少

from:是一个struct sockaddr*类型的结构体指针,它用来获取发送方的地址信息

fromlen:是一个指向整数的指针,用于指定 from 所指向的地址结构的长度。在调用 recvfrom 之前,应该将 *fromlen 设置为 sizeof(struct sockaddr)(或对应的具体地址结构的大小);当 recvfrom 返回时,fromlen 会被修改为实际存储在 from 中的地址结构的长度

返回值说明:成功返回收到的字节数,返回值为0说明对端已经关闭了连接,返回-1表示接受数据发生了错误

3.5 sendto -- 向套接字中写入信息

int sendto(int sockfd, const void *buf, int len, unsigned int flags, const struct sockaddr *to, int tolen);

sockfd:是通过socket函数创建的套接字描述符

buf:要向套接字发送的数据所在的缓冲区指针

len:指定要发生内容的长度

flags: 一般设置为 0,表示默认的发送行为。也可以设置一些标志位来影响发送操作的行为,不过这些标志位的使用相对较少

to:要发送到的目的地的struct sockadd的指针

tolen:to的大小

3.6 网络字节序接口

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);//将源主机中32位长整形转换成网络字节序要求的大端格式
uint16_t htons(uint16_t hostshort);//将源主机中16位短整形转换成网络字节序要求的大端格式
uint32_t ntohl(uint32_t netlong);//将网络中32位长整形(大端)转换成当前主机的格式
uint16_t ntohs(uint16_t netshort);//将网络中16位短整形(大端)转换成当前主机的格式

总结

本篇我们通过一个简单的UDP设计,对socket编程有了一些初步的了解,以及对一些常用的接口进行了学习,并且对本次模拟中,用到的接口进行了简单的整理。

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

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

相关文章

使用 UWA Gears 测试小游戏性能

UWA Gears 是UWA最新发布的无SDK性能分析工具。针对移动平台&#xff0c;提供了实时监测和截帧分析功能&#xff0c;帮助您精准定位性能热点&#xff0c;提升应用的整体表现。 随着小游戏的规模和用户量持续增长&#xff0c;玩家对于小游戏的性能要求也越来越高。为了能够给玩…

力扣234 回文链表 Java版本

文章目录 题目描述代码 题目描述 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为 回文链表 。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,2,1] 输出&#xff1a;true 示例 2&…

【ASE】第三课_山丘颜色梯度效果

今天我们一起来学习ASE插件&#xff0c;希望各位点个关注&#xff0c;一起跟随我的步伐 今天我们来学习山丘的颜色梯度显示&#xff0c;根据不同的高度显示不同的颜色 最终效果&#xff1a; 思路&#xff1a; 1.先加载模型的纹理贴图和法线贴图 2. 获得模型世界顶点Y向量&am…

【渗透测试】-灵当CRM系统-sql注入漏洞复现

文章目录 概要   灵当CRM系统sql注入漏洞&#xff1a;   具体实例&#xff1a;  技术名词解释  小结 概要 近期灵当CRM系统爆出sql注入漏洞&#xff0c;我们来进行nday复现。 灵当CRM系统sql注入漏洞&#xff1a; Python sqlmap.py -u "http://0.0.0.0:0000/c…

当okhttp网络库遇到不规范的http状态码

如题&#xff0c;最近工作遇到的问题&#xff0c;我们的 Android 应用网络请求埋点报表&#xff0c;收集到了奇怪的网络请求异常&#xff1b;通过日志收集与分析&#xff0c;确定到是服务器返回了不规范的状态码所导致。 如上是根据线上的业务场景&#xff0c;本地写个简单的M…

二进制位运算题

本期介绍&#x1f356; 主要介绍&#xff1a;1. 在不创建临时变量的情况下交换两个变量&#xff0c;2. 计算变量在内存中存放2进制位“1”的个数&#xff0c;3. 求两个数的二进制中不同位的个数&#xff0c;4. 分别打印整数的二进制中奇数位和偶数位&#xff0c;5. 判断一个整数…

SentencePiece进行文本分类

SentencePieces 前言 Step1:故事 SentencePiece 是一个无监督的文本分词器和 detokenizer(还原回去的&#xff1f;)主要用于词汇表大小是预定的文本生成系统中它拓展了原始句子的训练&#xff0c;实现子词单元如 BPE 和 unigram language model技术亮点 纯数据驱动&#xff…

Qemu开发ARM篇-6、emmc/SD卡AB分区镜像制作

文章目录 1、AB分区镜像制作2、uboot修改3、镜像启动 在上一篇 Qemu开发ARM篇-5、buildroot制作根文件系统并挂载启动中&#xff0c;我们通过buildroot制作了根文件系统&#xff0c;并通过 SD卡的形式将其挂载到设备并成功进行了启动&#xff0c;但上一章中&#xff0c;我们的…

车载应用的多功能需求与公安、金融等行业的应用特点

随着科技的快速发展&#xff0c;车载应用的功能需求也日益多样化。除了基本的视频监控功能外&#xff0c;现代车载应用还需满足一系列高级功能&#xff0c;如无线网络视频监控、GPS卫星定位、车辆调度、语音报站、行驶信息记录以及多媒体娱乐广告播放等。这些功能在公安、金融等…

2024年数字化转型与管理国际学术会议(DTM 2024)

目录 重要信息 大会简介 大会组委 征稿主题 论文出版 会议议程 参会方式 重要信息 大会官网&#xff1a;www.icemme.org&#xff08;点击了解大会&#xff0c;投稿等详细信息&#xff09; 大会时间&#xff1a;2024年11月22-24日 大会地点&#xff1a;中国-大连 大会…

三维重建的几何评价指标

1.三维重建的几何评价指标 1.1 Chamfer Distance Geometry quality (1) Chamfer Distance&#xff08;CD&#xff09; CD衡量两组点云之间的几何差异&#xff0c;距离越小越好。 CD是一种用于衡量两个点云之间相似度的常用几何评价指标。它计算一个点云中每个点到另一个点云的…

Qt5.15和Qt6.7配置Android开发环境

最近重新安装了Qt5.15.2和Qt6.7.2,使用Qt Creator14.0.1,配置Android开发环境时又碰到了一些问题,记录如下。 1、Qt6.7.2使用AndroidStudio的JDK 因为系统原来安装了AndroidStudio2024,系统自动检测了JDK位置,点击设置SDK,可以自动安装好相应的NDK。 打开Qt Creator14…

JavaEE——多线程的状态及线程安全问题

目录 一、线程的状态 1、NEW 2、 TERMINATED 3、RUNNABLE 4、TIMED_WAITING 5、 BLOCKED 6、WAITING 二、线程安全问题 1、线程不安全的原因 2、一个线程不安全的实例 3、加锁操作 4、产生线程不安全的原因 什么是内存可见性呢&#xff1f; 解决方案&#xff1f; 5、指令重排序…

【Linux学习】1-2 新建虚拟机ubuntu环境

1.双击打开VMware软件&#xff0c;点击“创建新的虚拟机”&#xff0c;在弹出的中选择“自定义&#xff08;高级&#xff09;” 2.点击下一步&#xff0c;自动识别ubuntu光盘映像文件&#xff0c;也可以点击“浏览”手动选择&#xff0c;点击下一步 3.设置名称及密码后&#xf…

web - RequestResponse

##Request&Response 1&#xff0c;Request和Response的概述 Request是请求对象&#xff0c;Response是响应对象。这两个对象在我们使用Servlet的时候有看到&#xff1a; 此时&#xff0c;我们就需要思考一个问题request和response这两个参数的作用是什么? request:获取请…

基于微信小程序的竞赛答题小程序开发笔记(一)

开发背景调研 中小学学科答题小程序&#xff0c;适合各中小学校方&#xff0c;老师或者家长。通过互动和参与式学习&#xff0c;小程序能够通过游戏化元素提升学习的积极性和参与度&#xff0c;从而提升学习效率&#xff0c;促进学生自主学习 功能规划 分类题库&#xff1a;…

专题八_链表_算法专题详细总结

目录 链表 1.常用技巧 1&#xff09;画图&#xff01;&#xff01;&#xff01; -> 直观 形象 便于我们理解 2&#xff09;引入虚拟“头”节点 1.便于处理边界条件 2.方便我们对链表进行操作 3.不要吝啬空间&#xff0c;大胆定义变量 4.快慢双指针 1.判断链表是否…

redis学习(014 实战:黑马点评:优惠券秒杀——1人只可以下1单问题解决方案)

黑马程序员Redis入门到实战教程&#xff0c;深度透析redis底层原理redis分布式锁企业解决方案黑马点评实战项目 总时长 42:48:00 共175P 此文章包含第54p-第p55的内容 文章目录 一人一单问题分析第一种写法 查询后进行添加第二种写法 加悲观锁在用户上加悲观锁&#xff08;提…

Vue 响应式监听 Watch 最佳实践

一. 前言 上一篇文章我们学习了 watch 的基础知识&#xff0c;了解了它的基本使用方法及注意事项&#xff0c;本篇文章我们继续了解在Vue 中 响应式监听 watch 的妙用。了解 watch 的基础使用请参考上一篇文章&#xff1a; 详解 Vue 中 Watch 的使用方法及注意事项https://bl…

53 语言模型(和之后用来训练语言模型的数据集)_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录理论部分使用计数来建模N元语法总结 代码读取长序列数据随机采样顺序分区 小结练习 理论部分 在上一部分中&#xff0c;我们了解了如何将文本数据映射为词元&#xff0c;以及将这些词元可以视为一系列离散的观测&#xff0c;例如单词或字符…