Socket套接字编程

news2025/1/20 7:05:21

文章目录

  • 1、网络字节序列
  • 2、socket编程接口
    • <1>socket常见ARI
    • <2>sockaddr结构 VS sockaddr_in结构
  • 3、UDP套接字
  • 4、TCP套接字
  • 5、总结

1、网络字节序列

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

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

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

在这里插入图片描述

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

2、socket编程接口

<1>socket常见ARI

在这里插入图片描述

  • socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符;

  • 应用程序可以像读写文件一样用read/write在网络上收发数据;

  • 对于IPv4, family参数指定为AF_INET;

  • 对于TCP协议,type参数指定为SOCK_STREAM, 表示面向流的传输协议

  • protocol参数指定为0即可

在这里插入图片描述

  • 服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接; 服务器需要调用bind绑定一个固定的网络地址和端口号;

  • bind()的作用是将参数sockfd和myaddr绑定在一起, 使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址和端口号;

  • struct sockaddr *是一个通用指针类型,myaddr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度

在这里插入图片描述

  • listen()声明sockfd处于监听状态, 并且最多允许有backlog个客户端处于连接等待状态, 如果接收到更多的连接请求就忽略, 这里设置不会太大(一般是5);

在这里插入图片描述

  • 三次握手完成后, 服务器调用accept()接受连接;
  • 如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来;
  • address 是一个传出参数,accept()返回时传出客户端的地址和端口号;
  • 如果给address 参数传NULL,表示不关心客户端的地址;
  • address _len参数是一个传入传出参数(value-result argument), 传入的是调用者提供的, 缓冲区address的长度以避免缓冲区溢出问题, 传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区);

在这里插入图片描述

  • 客户端需要调用connect()连接服务器;
  • connect和bind的参数形式一致, 区别在于bind的参数是自己的地址, 而connect的参数是对方的地址;

<2>sockaddr结构 VS sockaddr_in结构

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6, 然而, 各种网络协议的地址格式并不相同。
在这里插入图片描述

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

sockaddr结构

struct sockaddr
  {
    __SOCKADDR_COMMON (sa_);	/* Common data: address family and length.  */
    char sa_data[14];		/* Address data.  */
  };

sockaddr_in结构

struct sockaddr_in
  {
    __SOCKADDR_COMMON (sin_);
    in_port_t sin_port;			/* Port number.  */
    struct in_addr sin_addr;		/* Internet address.  */

    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
			   __SOCKADDR_COMMON_SIZE -
			   sizeof (in_port_t) -
			   sizeof (struct in_addr)];
  };

虽然socket api的接口是sockaddr, 但是我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主要有三部分信息: 地址类型, 端口号, IP地址.

3、UDP套接字


/接收套接字上的数据
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);    

/返回值:接收到的字节数,出错返回-1

/将消息发给套接字
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen); 

参数:sockfd:文件描述符
     buf:接收缓存区
     len:缓存区大小
     flags:一般设为0
     dest_addr和addrlen为输出型参数
    
FILE *popen(const char *command, const char *type); 
/执行command中的命令,执行结果以文件返回。底层是fock创建子进程利用pipe通信。


下面上手写代码,先说下最终实现功能,通过套接字来进行UDP通信,在客户端上输入简单命令,服务端负责执行然后将执行结果回显到客户端。

server端

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

void Usage(std::string proc)
{
    std::cout << "Usage: \n\t" << proc << " server_post" << std:: endl;
}

// ./udp_server  serverport 
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    //1.创建套接字,打开网络文件
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock < 0)
    {
        std::cerr<< "socket create error: " << errno << std::endl;
        return 1;
    }
    uint16_t port = atoi(argv[1]);
    // 2.给服务器绑定端口和ip(特殊处理)
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(port); //此处的端口号,是我们计算机上的变量,是主机序列

    //INADDR_ANY-> 要所有发送到该主机,发送到该端口的数据
    local.sin_addr.s_addr = INADDR_ANY; //INADDR_ANY 实际就是值为0的一个宏

    if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
    {
        std::cerr << "bind error: " << errno << std::endl;
        return 2;
    }

    //3.提供服务
    bool quit = false;
    char buffer[1024];
    while(!quit)
    {
        struct sockaddr_in peer;      //谁发送过来的
        socklen_t len = sizeof(peer); //长度
        //接收数据,最后两个参数为输型参数
        ssize_t cnt = recvfrom(sock, buffer, sizeof(buffer) - 1, 0 , (struct sockaddr*)&peer, &len);
        if(cnt > 0)
        {
            buffer[cnt] = 0; //向字符串一样看待

            //将buffer当成字符串命令来执行
            FILE* fp = popen(buffer, "r"); //popen,创建子进程,执行buffer中的命令,执行结果以文件方式返回
            std::string echo_hello;
            char line[1024] = {0};
            while(fgets(line, sizeof(line),fp) != NULL)
            {
                echo_hello += line;
            }
            pclose(fp);

            std::cout << "clinet# " << buffer << std::endl;

        
            //发送数据
            sendto(sock, echo_hello.c_str(), echo_hello.size(),0, (struct sockaddr*)&peer, len); 
        }
    }
    return 0;
}

client端

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

void Usage(std::string proc)
{
    std::cout << "Usage: \t" << proc << "  server_ip  server_post" << std:: endl;
}
// ./client server_ip server_port -->我们在执行的时候把ip和端口号传进来,参数不对就报错
int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        return 0;
    }
    
    //1.创建套接字,打开网络文件
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock < 0)
    {
        std::cerr << "socket error: " << errno << std::endl;
        return 1;
    }

    //客户端需要显示的bind的吗?
    // a.首先,客户端也必须要有ip和port
    // b.但是,客户端不需要显示的bind!一旦显示bind就必须明确,client要和哪一个port关联
    // client指明的端口号,在client端一定会有吗??有可能被占用,被占用导致client无法使用
    // server要的是port必须明确,而且不变,但client只要有就行!一般是由OS自动给你bind()
    // 就是client正常发送数据的时候,OS会自动给你bind,采用的是随机端口的方式!
    
    //2.发文件给谁
    struct sockaddr_in server;
    server.sin_family = AF_INET;            //通讯协议
    server.sin_port = ntohs(atoi(argv[2])); //端口号
    server.sin_addr.s_addr = inet_addr(argv[1]); // ip
    
    //3.使用服务
    while(1)
    {
        //发送数据 ->数据从哪里来
        std::cout << "MyShell $ ";
        char message[1024] = {0};
        fgets(message, sizeof(message), stdin);

        //发送数据
        sendto(sock, message, sizeof(message), 0, (struct sockaddr*)&server, sizeof(server));

        //接收数据
        struct sockaddr_in temp;    //此处temp就是一个“占位符”
        socklen_t len = sizeof(temp);
        char buffer[1024];
        ssize_t cnt = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr*)& temp, &len);
        if(cnt > 0)
        {
            //在网络通信中,只有报文大小,或者字节流中字节个数,没有C/C++中字符串的概念
            buffer[cnt] = 0;
            std::cout << "server echo# " << buffer << std::endl;
        }
        else
        {
			//出错处理
        }   
    }
    return 0;
}

运行结果:
在这里插入图片描述

4、TCP套接字

通过套接字来进行TCP通信,在客户端上输入字符串,服务端显示并回显到客户端。

server端

#include "Task.hpp"
#include "thread_pool.hpp"
#include <iostream>
#include <unistd.h>
#include <string>
#include <cstring>
#include <cerrno>
#include <signal.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <sys/wait.h>

using namespace ns_task;
using namespace ns_threadpool;

void ServiceIO(int new_sock)
{
  //提供服务,我们是一个死循环
  while (true)
  {
    char buffer[1024];
    memset(buffer, 0, sizeof(buffer));
    ssize_t s = read(new_sock, buffer, sizeof(buffer) - 1);
    if (s > 0)
    {
      buffer[s] = 0; //将获取的内容当成字符串
      std::cout << "client# " << buffer << std::endl;

      std::string echo_string = ">>>server<<<, ";
      echo_string += buffer;

      write(new_sock, echo_string.c_str(), echo_string.size());
    }
    else if (s == 0)
    {
      std::cout << "client quit ..." << std::endl;
      break;
    }
    else
    {
      std::cerr << "read error" << std::endl;
      break;
    }
  }
}

void Usage(std::string proc)
{
  std::cout << "Usage: " << proc << "port" << std::endl;
}

// ./tcp_server server_port
int main(int argc, char *args[])
{
  if (argc != 2)
  {
    Usage(args[0]);
    return 1;
  }

  // 1.创建套接字,打开网络文件
  int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
  if (listen_sock < 0)
  {
    std::cerr << "socket creare error: " << errno << std::endl;
    return 2;
  }

  // 2.bind
  uint16_t port = atoi(args[1]); //端口号
  //建立链接关系
  struct sockaddr_in local; //--> <netinet/in.h>
  memset(&local, 0, sizeof(local));
  local.sin_family = AF_INET;
  local.sin_port = htons(port);
  local.sin_addr.s_addr = INADDR_ANY;

  if (bind(listen_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
  {
    std::cerr << "bind error: " << errno << std::endl;
    return 3;
  }


  //   设置套接字是Listen状态, 本质是允许用户连接
  const int backlog = 5;
  if (listen(listen_sock, backlog) < 0)
  {
    std::cerr << "listen error " << errno << std::endl;
    return 4;
  }
  
  for (;;)
  {
    struct sockaddr_in peer;
    socklen_t len = sizeof(peer);
    int new_sock = accept(listen_sock, (struct sockaddr *)&peer, &len);
    if (new_sock < 0)
    {
      continue;
    }
    uint16_t cli_port = ntohs(peer.sin_port);
    std::string cli_ip = inet_ntoa(peer.sin_addr);
    //inet_ntoa函数作用将以网络字节序给出的网络主机地址 in 转换成以点分十进制表示的字符串(如127.0.0.1)。
    //结果作为函数返回结果返回。
    
    std::cout << "get a new link -> : [" << cli_ip << ":" << cli_port << "]# " << new_sock << std::endl;
    
    pid_t id = fork();
    if (id < 0)
    {
      continue;
    }
    else if (id == 0)
    { //曾经被父进程打开的fd,是否会被子进程继承呢? 无论父子进程中的哪一个,强烈建议关闭掉不需要的fd
      // child
      close(listen_sock);

      if (fork() > 0) //不用信号来处理进程资源释放,用爷孙关系 
        exit(0); //退出的是子进程

      //向后走的进程,其实是孙子进程,不用管会被系统领养
      ServiceIO(new_sock);
      close(new_sock);
      exit(0);
    }
    else
    {
      // father,不需要等待
      waitpid(id, nullptr, 0); //这里等待的时候会不会被阻塞呢? 不会(子进程刚创建就退出了)
      close(new_sock);
    }
  }
  return 0;
}

client端

#include <iostream>
#include <string>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>


void Usage(std::string proc)
{
    std::cout << "Usage: " << proc << " server_ip server_port" << std::endl;
}
// ./udp_client server_ip server_port
int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        return 1;
    }
    std::string svr_ip = argv[1];
    uint16_t svr_port = (uint16_t)atoi(argv[2]);

    //1. 创建socket
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock < 0)
    {
        std::cerr << "socket error!" << std::endl;
        return 2;
    }

    //2. bind, 3. listen 4. accept             ??
    //client无需显示的bind, client->server
    //client -> connect!
    
    //2.配置信息
    struct sockaddr_in server;
    bzero(&server, sizeof(server));  // bzero 和memset作用一样
    server.sin_family = AF_INET;
    //该函数做两件事情
    //1. 将点分十进制的字符串风格的IP,转化成为4字节IP
    //2. 将4字节由主机序列转化成为网络序列
    server.sin_addr.s_addr = inet_addr(svr_ip.c_str()); //server ip
    server.sin_port = htons(svr_port); // server port

    //3. 发起链接
    if(connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0){
        std::cout << "connect server failed !" << std::endl;
        return 3;
    }

    std::cout << "connect success!" << std::endl;

    // 进行正常的业务请求了
    while(true)
    {
        std::cout << "Please Enter# ";
        char buffer[1024];
        fgets(buffer, sizeof(buffer)-1, stdin);

        write(sock, buffer, strlen(buffer));

        ssize_t s = read(sock, buffer, sizeof(buffer)-1);
        if(s>0)
        {
            buffer[s] = 0;
            std::cout << "server echo# " << buffer << std::endl;
        }
    }

    return 0;
}

执行结果:
在这里插入图片描述

5、总结

  • 创建socket过程本质是打开文件,仅仅有系统相关内容。
  • bind(), struct sockaddr_in 本质是ip + port和文件信息相关联。
  • listen()本质是设置socket文件的状态,允许别人来连我。
  • accept()获取新链接到应用层,以fd为代表。
  • read/write,本质就是进行网络通信,对于用户来说相当与正常文件读写。
  • connect(),本质就是发起连接。
    系统层面就是构建一个请求报文发送过去。
    在网络层面发起TCP三次握手。
  • close(fd),关闭文件。
    系统层面,释放曾经申请的文件资源,连接资源等。
    网络层面,其实就是在进行四次挥手。

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

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

相关文章

腾讯T9纯手写基于Mycat中间件的分布式数据库架构笔记

随着移动互联网的兴起和大数据的蓬勃发展&#xff0c;系统的数据量正呈几何倍数增长&#xff0c;系统的压力也越来越大&#xff0c;这时最容易出现的问题就是服务器繁忙&#xff0c;我们可以通过增加服务器及改造系统来缓解压力&#xff0c;然后采用负载均衡、动静分离、缓存系…

流媒体传输 - RTSP Over HTTP

RTSP 的标准端口是 554&#xff0c;但是由于各种不同的防火墙等安全策略配置的原因&#xff0c;客户端在访问 554 端口时可能存在限制&#xff0c;从而无法正常传输 RTSP 报文。 但是 HTTP 端口&#xff08;80 端口&#xff09;是普遍开放的&#xff0c;于是就有了让 RTSP 报文…

【Android App】给三维的地球仪贴上动物贴纸实战(附源码和演示 超详细必看)

需要源码和图片集请点赞关注收藏后评论区留言~~~ 一、纹理贴图 给三维物体穿衣服的动作&#xff0c;通常叫做给三维图形贴图&#xff0c;更专业地说叫纹理渲染。 渲染纹理的过程主要由三大项操作组成&#xff0c;分别说明如下&#xff1a; &#xff08;1&#xff09;启用纹理…

STL的常用遍历算法(20221128)

STL的常用算法 概述&#xff1a; 算法主要是由头文件<algorithm> <functional> <numeric> 组成。 <algorithm>是所有STL头文件中最大的一个&#xff0c;涉及比较、交换、查找、遍历等等&#xff1b; <functional>定义了一些模板类&#xff0…

2022VR高级研修班总结

本人有幸参加2022VR高级研修班&#xff0c;此次高级研修班由赵沁平院士和丁文华院士领衔&#xff0c;全国知名专家及长期在相关领域从事产业、管理、科研工作的专家参与&#xff0c;带来了18个专题讲座&#xff0c;内容涵盖虚拟现实技术与系统现状与发展、产学研合作与产业协同…

都什么年代了,你居然还连不上GitHub?

前言 众所周知&#xff0c;GitHub是我们程序员在上班或者学习的时候经常会逛的一个地方[手动狗头]&#xff0c;而且如果我们想参与开源项目的话&#xff0c;GitHub也是一个很好的平台。 可问题是&#xff0c;GitHub网页总是进不去&#xff0c;提交代码到GitHub也总是超时&…

在Navicat上如何停止正在运行的MYSQL语句

目录 &#xff08;一&#xff09;前言 &#xff08;二&#xff09;正文 1. 图形化停止SQL 2. 用SQL方式停止运行的SQL &#xff08;1&#xff09;找到运行的SQL的ID &#xff08;2&#xff09;运行kill命令杀掉SQL &#xff08;一&#xff09;前言 众所周知&#xff0c;…

BI数据分析软件有哪些?为什么说奥威BI很特别?

经过十几年的发展&#xff0c;以及近几年国家的大力鼓励发展大数据智能产业等原因&#xff0c;BI数据分析软件开始越来越被大家所熟知&#xff0c;那么BI数据分析软件都有哪些&#xff0c;为什么说奥威BI数据分析软件很特别&#xff1f;它对企业的大数据智能数据可视化分析起到…

什么样的人最适合做软件测试---喜欢找人帮忙办事的人

今天和大家说下什么样类型的人适合做软件测试。 经验干货&#xff0c;可仔细品 很多测试工程师面试中也可能会遇到问题&#xff0c;说怎么做一名优秀合格的测试工程师&#xff0c;需要有哪些品质&#xff0c;很多人会回答说要仔细&#xff0c;要承受压力&#xff0c;要有责任感…

2013-2020年全国31省数字经济数据集

1、时间&#xff1a;2013-2020年 2、来源&#xff1a;整理自国家统计J和统计NJ 3、指标包括&#xff1a; 信息化基础&#xff1a;"光缆线路长度(公里)、移动电话基站&#xff08;万个&#xff09;、信息传输、软件和信息技术服务业城镇单位就业人员(万人)、年末常住人口…

Android-P夜间模式

0 前言 Android-P可通过“Developer options > Night mode”打开夜间模式&#xff0c;本文研究其配置和效果。 验证环境&#xff1a;Pixel 3a、模拟器 1 设置 图1.1 Night mode上图对应XML配置如下&#xff1a; <ListPreferenceandroid:key"dark_ui_mode"and…

【Python】六、程序流程控制综合训练

文章目录实验目的一、列表定义二、元组定义三、列表的常用操作append() -- 向列表的尾部添加元素insert(index&#xff0c;object) -- 向指定的下标处添加元素sort&#xff08;&#xff09;-- 进行排序&#xff08;从下到大 int类型&#xff09;可以对字母进行排序&#xff08;…

多态原理、虚表指针与虚基表的内存布局。

文章目录前言多态虚函数静态类型与动态类型重载、覆盖和隐藏的区别final和override虚函数的默认实参虚析构函数在构造函数和析构函数中调用虚函数多态原理各种形式继承的虚函数内存布局单一继承无覆盖单一继承有覆盖单一虚拟继承有覆盖多继承无覆盖多继承有覆盖菱形继承有覆盖菱…

STC/MLLT--学习笔记

gmm建模方差使用对角矩阵的前提是假设特征之间相互独立&#xff0c;使用full或者block-diagonal矩阵可以对相关性的特征建模&#xff0c;但是参数增多。为了解决使用这个问题&#xff0c;有两种方法&#xff1a; feature-space 使用DCT或者LDA去相关model-space 不同的模型可以…

连续时间系统的时域分析

一.微分方程的求解 1.求微分方程的齐次解 &#xff08;1&#xff09;写出特征方程并求解 2.写出齐次解 2.求微分方程的特解 已知 &#xff08;1&#xff09;根据表2-2&#xff0c;写出特解函数 ​​​​​​​ &#xff08;2&#xff09;带入并求解 3.完全解 二.微分方…

基于GRU与注意力机制实现法语-葡萄牙语的翻译详细教程 数据+代码

本教程通过机器翻译的例子来介绍和实现一个简单的机器翻译方法,机器翻译是指将一段文本从源语言(如语言A)自动翻译到目标语言(如语言B)。本教程通过加载和预处理数据、构造编码器和解码器、训练模型、结果评价得到一个可以应用的机器翻译工具。 1.2 任务描述 神经机器翻译方…

uni-app 介绍及使用

一、什么是uni-app uni-app由dcloud公司开发的多端融合框架&#xff0c;是一个使用 Vue.js 开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布到iOS、Android、Web&#xff08;响应式&#xff09;、以及各种小程序&#xff08;微信/支付宝/百度/头条/飞…

单文件组件环境配置步骤---vue-cli版

因为浏览器只认识&#xff1a;html、css、js文件&#xff0c;其他一概不认识&#xff1b; 所以要把单文件组件的vue文件转化为上面浏览器能认识的文件&#xff1b; 有两种环境配置途径&#xff1a; 第一种就是&#xff1a;配置webpack环境&#xff0c;要下载很多东西&#x…

灰色预测GM(1.1)模型及matlab程序负荷预测

灰色GM(1.1)预测模型 GM&#xff08;1.1&#xff09;模型由包含单一变量的一阶微分方程构成的模型&#xff0c;是灰色模型中最常用的模型。 设有负荷变量为的原始数据列: (3-1) 生成一阶累加数据列&#xff1a; (3-2) 其中 (3-3) 一阶微分方程的解呈指数增长形式&#xff0c…

URLDNS利用链分析

目录 前言&#xff1a; (一&#xff09;原理 &#xff08;二&#xff09;利用链 再来分析 URLDNS.java 这个文件&#xff0c;并且在入口处设置断点进行调试&#xff1a; &#xff08;三&#xff09; POC 参考资料 前言&#xff1a; URLDNS是Java反序列化中比较简单的一个链…