Linux_网络项目_WEB服务器 设计CGI机制,与CGI处理数据

2023-11-15

文章紧跟之前web服务器返回静态网页

CGI设计思路:

CGI原理如下图:
在这里插入图片描述
其中CGI程序可以使用任何后端语言进行编写。

而服务器是可执行程序,而CGI程序也是可执行程序。
可以创建子进程使用进程替换来实现exec系列函数来实现。
详情见
Linux_进程替换(execl、execlp、execv、execle)


服务器与CGI程序之间还涉及进程通信。这里采用两个匿名管道进行双向通信。
Linux_Centos进程间通信_管道(匿名管道_命名管道)
这里为了不产生歧义,管道的读写全部站在父进程的角度。

父进程通过input来读取CGI程序数据,output来向CGI程序数据。
父进程关闭input[1],output[0];

子进程通过向input写入数据,output拿取数据
子进程关闭input[0],output[1];


注意:进程替换,原进程的文件描述符值新进程无法得知,但是原进程打开的文件,以及文件描述符组所指向不会改变。(只替换代码和数据,并不替换内核进程相关的数据结构)。

解决方法:这里使用重定向原则,让替换后的进程读取管道数据向标准输入读取,写入数据向标准输出写入即可。在进程替换前进行重定向。
在这里插入图片描述
dup2将oldfd指向的文件拷贝到newfd所指向的文件。
在这里插入图片描述


POST方法参数在正文中,传递参数时通过管道传参数。
GET方法参数在请求行上,这里采用环境变量进行传参。更高效,putenv函数与getenv函数
所以替换的进程还需要知道HTTP的方法才可以得知需要从哪里拿取参数。

环境变量具有全局属性,子进程可以看到。其次环境变量也不受进程替换的影响。

注意:在测试cgi时打印时需要使用cerr输出,因为标准输出与标准输入已经被重定向。

设计CGI代码

服务器将请求数据解析后导入环境变量中。

#pragma once 


//已经存在套接字,线程通过套接字处理任务

#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include"Util.h"
#include<string>
#include<vector>
#include"Log.h"

#include<sstream>
#include<unordered_map>

#include<sys/stat.h>
#include<algorithm>

#include<sys/sendfile.h>
#include<fcntl.h>

#include<sys/wait.h>

#define OK 200
#define NOTFOUND 404

#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define VERSON_HTTP "HTTP/1.0"
#define LINE_END "\r\n" //行结尾标志

static std::string CodeToInfo(int code){
  std::string Info;
  switch(code){
    case 200:
      Info="OK";
      break;
    case 404:
      Info="NotFound";
      break;
    default:
      break;
  }
  return Info;
}

//HTTP响应报文
class HttpResponse{
  public:
    std::string StatusLine_HTTP;//状态行
    std::vector<std::string>ResponHeads;//首部字段
    std::string ResponBlank=LINE_END;//空行
    std::string ResponBody;//正文

    int status_code=OK;//响应状态码

    int fd=-1;//储存发送网页的文件描述符

    size_t size;//打开网页的大小

};

//HTTP请求报文
class HttpRequest{
  public:
    std::string RequestLine_HTTP;//请求行
    std::vector<std::string>RequestHeads;//首部字段
    std::string RequestBlank;//空行
    std::string RequestBody;//正文

    //解析完请求报文后的结果
    std::string Method;
    std::string URI;//Path?Pararm
    std::string Version;

    //保存解析首部字段的map
    std::unordered_map<std::string,std::string>Head_KVS;

    int Content_Lenth=0;

    //访问资源的路径
    std::string Path;

    //如果是GET方法通过URL上传的参数
    std::string Param;

    bool CGI=false;

    std::string Type;//请求文件类型
};


//读取请求,分析请求,构建响应,基本IO通信,实现基本业务逻辑
class EndPoint{
  private:
    int sock;
    HttpRequest http_request;//http请求
    HttpResponse http_response;//http响应
  private:
    void GetHttpRequestLine(){//读请求行
      Util::ReadLine(sock,http_request.RequestLine_HTTP);//读取HTTP请求第一行
      http_request.RequestLine_HTTP.pop_back();
      ERRORLOG(INFO,http_request.RequestLine_HTTP);
    }

    void GetHttpRequstHeads(){//读取首部字段
      std::string line;
      while(true){
        Util::ReadLine(sock,line);
        if(line=="\n"){
          //ERRORLOG(INFO,line);
          http_request.RequestBlank=line;
          break;
        }
        line.pop_back();//去掉每行的\n
        http_request.RequestHeads.push_back(line);
        ERRORLOG(INFO,line);
        line.clear();
      }
    }

    void AnalyQuestLine(){//解析请求行  方法 URL HTTP版本
      std::stringstream Str(http_request.RequestLine_HTTP);
      Str>>http_request.Method>>http_request.URI>>http_request.Version;
      //将方法统一转化成大写 Get->GET
      std::string& strtmp=http_request.Method;
      std::transform(strtmp.begin(),strtmp.end(),strtmp.begin(),::toupper);//写回strtmp首部
    }

    void AnalyuestHeadS(){
      std::string key;
      std::string value;
      for(auto&line:http_request.RequestHeads){
        if(Util::CutString(line,key,value,": ")){
          http_request.Head_KVS.insert(std::make_pair(key,value)); 
        }
        else{
          ERRORLOG(FATA,"AnalyuestHeadS error");
        }
      }
    }

    bool HaveHttpBody(){
      //判断是否是GET方法,GET方法没有正文
      std::string& Method=http_request.Method;
      if(Method=="POST"){
        std::unordered_map<std::string,std::string>::iterator iter=http_request.Head_KVS.find("Content-Length");
        if(iter!=http_request.Head_KVS.end()){
          http_request.Content_Lenth=atoi(iter->second.c_str());
          return true;
        }
      }
      return false;
    }

    void GetHttpBody(){
      if(HaveHttpBody()){
        //std::cout<<"需要读取正文"<<std::endl;
        int Content_Lenth=http_request.Content_Lenth;
        char ch=0;
        while(Content_Lenth>0){
          ssize_t size=recv(sock,&ch,1,0);
          if(size>0){
            http_request.RequestBody.push_back(ch);
            Content_Lenth--;
          }
          else{
            break;
          }
        }
        //std::cout<<http_request.RequestBody<<std::endl;
      }
    }

    int ProceNoCGI(size_t size){
      //构建HTTP响应网页
      //填充状态行
      http_response.fd=open(http_request.Path.c_str(),O_RDONLY);//只读方式打开
      if(http_response.fd>0){
        std::string& Str=http_response.StatusLine_HTTP;
        Str+=VERSON_HTTP;
        Str+=" ";
        Str+=std::to_string(http_response.status_code);
        Str+=" ";
        Str+=CodeToInfo(http_response.status_code);
        Str+=LINE_END;
        http_response.size=size;

        std::string Content_Lenth_str="Content-Length: ";
        Content_Lenth_str+=std::to_string(size);
        Content_Lenth_str+=LINE_END;
        http_response.ResponHeads.push_back(Content_Lenth_str);
        std::string Content_Type_str="Content-Type: ";
        Content_Type_str+=Util::SuffixToDesc(http_request.Type);
        Content_Type_str+=LINE_END;
        http_response.ResponHeads.push_back(Content_Type_str);
        return OK;
      }
      return NOTFOUND;
    }

    int ProceCGI(){
      //进程替换
      std::string&bin=http_request.Path;


      //父进程数据
      std::string& query=http_request.Param;//GET方法参数在请求行
      std::string& body=http_request.RequestBody;//POST方法参数在正文
      int PostReadSize=http_request.Content_Lenth;//POST方法需要导入正文大小的环境变量

      std::string query_env;
      std::string method_env;
      std::string PostReadSize_env;

      int exit_code=OK;//退出码

      int input[2];
      int output[2];//站在父进程角度
      if(pipe(input)<0){
        //创建管道失败->
        ERRORLOG(ERROR,"pipe input error");
        exit_code=NOTFOUND;
        return exit_code;
      }
      if(pipe(output)<0){
        ERRORLOG(ERROR,"pipe output error");
        exit_code=NOTFOUND;
        return exit_code;//->
      }
      pid_t pid=fork();
      if(pid==0){
        //子进程
        //需要替换的程序在PATH上,给程序传递的参数与方法有关。
        close(input[0]);
        close(output[1]);

        method_env="METHOD=";
        method_env+=http_request.Method;
        //把方法也传给子进程
        putenv((char*)method_env.c_str());
        //std::cerr<<"debug# "<<method_env<<std::endl;

        //如果是GET方法,通过环境变量传参
        if(http_request.Method=="GET"){
          query_env="QUERY_STRING=";
          query_env+=query;
          putenv((char*)query_env.c_str());
          ERRORLOG(INFO,"putenv Query_string!");
        }
        else if(http_request.Method=="POST"){
          PostReadSize_env="CONTENT_LENGTH=";
          PostReadSize_env+=std::to_string(PostReadSize);
          putenv((char*)PostReadSize_env.c_str());
          ERRORLOG(INFO,"putenv Content_Lenth!");
        }

        //向input[1]写入->1 向output[0]进行读取->0
        dup2(input[1],1);
        dup2(output[0],0);

        //std::cerr<<"debug# "<<bin.c_str()<<std::endl;
        execl(bin.c_str(),bin.c_str(),nullptr);//读0写1
        //cerr<<"替换失败"<<std::endl;
        exit(1);//替换失败
      }
      else if(pid<0){
        //创建进程失败->
        ERRORLOG(ERROR,"fork error");
        return NOTFOUND;
      }
      else{
        //父进程
        close(input[1]);
        close(output[0]);

        if(http_request.Method=="POST"){
          //将数据写入到管道中      
          //  std::cerr<<"写入开始"<<std::endl;
          const char*start=body.c_str();
          int total=0;//已经写了几个字符
          ssize_t size=0;//写了几个字符
          //保证全部数据都写入
          while(total<http_request.Content_Lenth&&(size=write(output[1],start+total,body.size()-total))>0){
            total+=size;
          }
        }

        //父进程获取子进程处理结果
        char ch=0;
        while(read(input[0],&ch,1)>0){
          http_response.ResponBody.push_back(ch);
        }

        int status=0;
        pid_t ret=waitpid(pid,&status,0);//阻塞等待
        if(ret==pid){
          if(WIFEXITED(status)){
            if(WEXITSTATUS(status)==0){
              //进程正常退出
              exit_code=OK;
            }
            else{
              exit_code=NOTFOUND;
            }
          }
          else{
            exit_code=NOTFOUND;
          }
        }
        close(input[0]);
        close(output[1]);
      }
      return exit_code;
    }

  public:
    EndPoint(int _sock):sock(_sock){}

    void RecvQuest_HTTP(){//读取请求
      GetHttpRequestLine();
      GetHttpRequstHeads();
    }

    void AnalyQuest_HTTP(){//解析请求
      AnalyQuestLine();
      AnalyuestHeadS();
      //std::cout<<"解析完毕"<<std::endl;
      GetHttpBody();
    }

    void MakeRespon_HTTP(){//构建响应
      //判断请求类型
      std::string tmpPath;//临时保存请求路径,方便修改
      size_t size =0;//记录文件大小
      struct stat stat_buff;//记录打开文件属性
      size_t suffix_pos=0;//找后缀
      if(http_request.Method!="GET"&&http_request.Method!="POST"){
        ERRORLOG(WARNING,"error request");
        http_response.status_code=NOTFOUND;
        goto END;
      }
      //如果是GET方法需要处理URL,看URL是否有参数
      if(http_request.Method=="GET"){
        size_t pos=http_request.URI.find('?');
        if(pos!=std::string::npos){
          Util::CutString(http_request.URI,http_request.Path,http_request.Param,"?");
          http_request.CGI=true;
        }
        else{
          //不是通过GET传参数
          http_request.Path=http_request.URI;
        }
      }
      else if(http_request.Method=="POST"){
        //POST
        http_request.CGI=true;//需要CGI技术
        http_request.Path=http_request.URI;
      }
      //std::cout<<"Debug# URI: "<<http_request.URI<<" Path:"<<http_request.Path<<" Param:"<<http_request.Param<<std::endl;
      //拼接web根目录;
      tmpPath=http_request.Path;
      http_request.Path=WEB_ROOT;
      http_request.Path+=tmpPath;
      //std::cout<<"Debug# "<<http_request.Path<<std::endl;
      if(http_request.Path[http_request.Path.size()-1]=='/'){
        //默认访问index.html
        http_request.Path+=HOME_PAGE;
      }
      //std::cout<<"Debug# "<<http_request.Path<<std::endl;
      //判断路径是否合法
      if(stat(http_request.Path.c_str(),&stat_buff)==0){
        //资源存在,需要判断这个路径是否访问了路径下的某个资源,如果没有,直接将路径的默认网页响应回去
        if(S_ISDIR(stat_buff.st_mode)){
          //是目录,添加首页信息后还需要重新获取文件stat状态
          http_request.Path+="/";
          http_request.Path+=HOME_PAGE;
          stat(http_request.Path.c_str(),&stat_buff);
        }
        if((stat_buff.st_mode &S_IXUSR)||(stat_buff.st_mode &S_IXGRP)||(stat_buff.st_mode& S_IXOTH)){
          //可执行文件,需要特殊处理
          http_request.CGI=true;
        }
        size=stat_buff.st_size;
      }
      else{
        //资源不存在状态码 404
        ERRORLOG(WARNING,http_request.Path+" Not Found!");
        http_response.status_code=NOTFOUND;
        goto END;
      }

      suffix_pos=http_request.Path.rfind(".");
      if(suffix_pos==std::string::npos){
        http_request.Type=".html";
      }
      else{
        http_request.Type=http_request.Path.substr(suffix_pos);
      }

      if(http_request.CGI==true){
        http_response.status_code=ProceCGI();
      }
      else{
        http_response.status_code=ProceNoCGI(size);//一定是GET方法,一定不带参,简单的文本网页返回
      }
END:
      //进行响应
      if(http_response.status_code!=OK){
        //错误响应
        
      }
      return;
    }

    void SendRespon_HTTP(){//发送响应
        send(sock,http_response.StatusLine_HTTP.c_str(),http_response.StatusLine_HTTP.size(),0);
        //std::cout<<"DeBug# "<<http_response.StatusLine_HTTP<<std::endl;
        for(size_t size=0;size<http_response.ResponHeads.size();size++){         
           send(sock,http_response.ResponHeads[size].c_str(),http_response.ResponHeads[size].size(),0);
           //std::cout<<"send succeed"<<std::endl;
        }   //->
        send(sock,http_response.ResponBlank.c_str(),http_response.ResponBlank.size(),0);
        sendfile(sock,http_response.fd,nullptr,http_response.size);
    //    std::cout<<"close fd="<<http_response.fd<<std::endl;
        close(http_response.fd);
    }

    ~EndPoint(){close(sock);}
};

class Entry{//线程执行任务的入口
  public:
    static void*SolveQuest(void*_sock){
      ERRORLOG(INFO,"Processing Requests...");
      int sock=*(int*)_sock;
      delete(int*)_sock;
      //std::cout<<" Get a New Link: sock="<<sock<<std::endl;
      EndPoint* endpoint=new EndPoint(sock);
      endpoint->RecvQuest_HTTP();
      endpoint->AnalyQuest_HTTP();
      endpoint->MakeRespon_HTTP();
      endpoint->SendRespon_HTTP();
      delete endpoint;
      ERRORLOG(INFO,"Processing Request End!");
      return nullptr;
    }
};

CGI程序代码

工具头文件

#pragma once 

//提供编码过程中所需要的工具函数 

#include<string>
#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<unordered_map>

class Util{
  public:
    static int ReadLine(int sock,std::string&outBuff){
      char ch='D';//ch先随机赋不为'\n'、'\r'的值
      while(ch!='\n'){
        //将三种行结束符统一转化为\n换行
        ssize_t size=recv(sock,&ch,1,0);
        if(size>0){
          if(ch=='\r'){
            //特殊处理 \r->\n   \r\n->\n
            //查看\r后的字符,不取走
            recv(sock,&ch,1,MSG_PEEK);
            if(ch=='\n'){
              //是\r\n换行格式,重复recv即可
              recv(sock,&ch,1,0);//用\n将\r覆盖
            }
            else{
              //是\r的格式,修改ch
              ch='\n';
            }
          }
          outBuff.push_back(ch);//换行格式一定是\n
        }
        else if(size==0){
          return 0;//对端关闭
        }
        else{
          return -1;//读取错误
        }
      }
      return outBuff.size();//返回读取一行字符的个数
    }

    static bool CutString(std::string&src,std::string& key,std::string&value,std::string gist){//根据gist(: )字符串切分HTTP首部字段,将切分的两个字符串放入到map中
      size_t pos=src.find(gist);
      if(pos!=std::string::npos){
        key=src.substr(0,pos);
        value=src.substr(pos+gist.size());//默认截取到字符串尾
        return true;
      }
      return false;
    }

    static std::string SuffixToDesc(const std::string&suffix){
      static std::unordered_map<std::string,std::string>Desc={
        {".html","text/html"},{".css","text/css"},{".js","application/javascript"},
        {".jpg","application/x-jpg"},{".xml","application/xml"},
        {".htm","text/html"},{".ttf","font/ttf"},{".woff","font/woff"},
        {".woff2","font/woff2"}
      };      
      auto pos=Desc.find(suffix);
      if(pos!=Desc.end()){
        return pos->second;
      }
      return "text/html";
    }
};
#include<unistd.h>
#include<stdlib.h>
#include<string>
#include<iostream>
#include"Util.h"

bool GetQuerySrt(std::string& query_str){
  std::string method=getenv("METHOD");
  bool resault=true;
  if(method=="GET"){
    query_str=getenv("QUERY_STRING");
    std::cerr<<"Debug# "<<query_str<<std::endl;
    resault=true;
  }
  else if(method=="POST"){
    int Content_Lenth=atoi(getenv("CONTENT_LENGTH"));
    char ch=0;
    while(Content_Lenth>0){
      read(0,&ch,1);
      query_str.push_back(ch); 
      Content_Lenth--;
    }
    resault=true;
  }
  else{
    resault=false;
  }
  return resault;
}

int main(){
  std::string query_str;
  GetQuerySrt(query_str);

  // a=100&b=200
  std::string str1;std::string str2;
  Util::CutString(query_str,str1,str2,"&");

  std::string key1;std::string value1;
  Util::CutString(str1,key1,value1,"=");

  std::string key2;std::string value2;
  Util::CutString(str2,key2,value2,"=");

  //重定向标准输出,直接向标准输出打印字符,调用方可以通过read读取
  std::cout<<key1<<"->"<<value1<<std::endl;
  std::cout<<key2<<"->"<<value2<<std::endl;
  
  std::cerr<<"Debug:"<<key1<<"->"<<value1<<std::endl;
  std::cerr<<"Debug: "<<key2<<"->"<<value2<<std::endl;
  return 0;
}

在这里插入图片描述
Github项目代码位置
Gitee项目代码位置

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux_网络项目_WEB服务器 设计CGI机制,与CGI处理数据 的相关文章

  • 使用 inotify 的正确方法是什么?

    我想使用inotifyLinux 上的机制 我希望我的应用程序知道文件何时aaa被改变了 您能给我提供一个如何做到这一点的示例吗 文档 来自监视文件系统活动 inotify https developer ibm com tutorials
  • 在 Linux 上访问 main 之外的主要参数

    是否可以访问参数main在外面main 即在共享库构造函数中 在 Linux 上除了通过解析之外 proc self cmdline 您可以通过将构造函数放入 init array部分 功能在 init array 不像 init 使用相同
  • 从c调用汇编函数

    我试图从 c 调用汇编函数 但我不断收到错误 text globl integrate type integrate function integrate push ebp mov esp ebp mov 0 edi start loop
  • 码头无故停止

    我需要经验丰富的码头用户的建议 我在负载均衡器 亚马逊云 后面维护着 2 台 Linux 机器 使用 Jetty 9 0 3 有时我的 Jetty 容器会被 Thread 2 无故关闭 同时地 显示以下日志并且容器无故停止 没有错误 没有例
  • 从 Python 访问 802.11 无线管理帧

    我想从 Linux 上的 Python 嗅探 802 11 管理 探测请求 帧 这可以从 Scapy 中实现 如下所示 coding utf 8 from scapy all import def proc p if p haslayer
  • 用于编辑 /etc/sudoers 文件的正则表达式模式

    我想删除 etc sudoers 文件中的 uncommnet 轮组 那么我应该使用什么正则表达式模式 cat etc sudoers Allows members of the sys group to run networking so
  • 如何在gnuplot中将字符串转换为数字

    有没有办法将表示数字 以科学格式 的字符串转换为 gnuplot 中的数字 IE stringnumber 1 0e0 number myconvert stringnumber plot 1 1 number 我可能使用 shell 命令
  • 错误:命令“c++”失败,退出状态为 1

    所以我尝试按照以下说明安装 Pyv8https andrewwilkinson wordpress com 2012 01 23 integrating python and javascript with pyv8 https andre
  • 使用 gcc 理解共享库

    我试图理解 C 中共享库的以下行为 机器一 cat one c include
  • 如何重命名 .tar.gz 文件而不提取内容并在 UBUNTU 中创建新的 .tar.gz 文件?

    我有一个命令将创建一个新的 tar gz现有文件中的文件 sudo tar zcvf Existing tar gz New tar gz 该命令将创建一个新的New tar gz从现有的文件Existing tar gz file 谁能告
  • XAMPP Windows 上的 Php Cron 作业

    嗯 我是这个词的新手CRON 据我所知 这是一个Unix安排特定操作在定义的时间间隔后执行的概念 我需要运行一个php文件 每小时更新一次数据库 但我的困惑在于安排执行 我在用XAMPP用于 Windows 7 上的本地开发测试 我发现了什
  • 在 debian wheezy amd64 上安装 ia32-libs

    我正在使用 Debian 7 喘息 amd64 uname a Linux tzwm debian 3 2 0 4 amd64 1 SMP Debian 3 2 51 1 x86 64 GNU Linux 我想安装ia32 libs在我的
  • 使用c在linux上分块读写

    我有一个 ASCII 文件 其中每一行都包含一个可变长度的记录 例如 Record 1 15 characters Record 2 200 characters Record 3 500 characters Record n X cha
  • 在 Linux 控制台中返回一行?

    我知道我可以返回该行并用以下内容覆盖其内容 r 现在我怎样才能进入上一行来改变它呢 或者有没有办法打印到控制台窗口中的特定光标位置 我的目标是使用 PHP 创建一些自刷新的多行控制台应用程序 Use ANSI 转义码 http en wik
  • SMP 上如何处理中断?

    SMP 对称多处理器 多核 机器上如何处理中断 内存管理单元是只有一个还是多个 假设两个线程 A 和 B 运行在不同的内核上 同时 访问页表中不存在的内存页面 在这种情况下 将会出现页面错误 并从内存中引入新页面 将会发生的事件的顺序是什么
  • 如何在特定的Java版本上运行应用程序?

    如何运行具有特定 Java 版本的应用程序 我安装了三个 Java 版本 myuser mysystem sudo update alternatives config java There are 3 choices for the al
  • 用于时间线数据的类似 gnuplot 的程序

    我正在寻找一个类似 gnuplot用于在时间轴中绘制数据图表的程序 类似 gnuplot 在 Linux 上运行 命令行功能 GUI 对我帮助不大 可编写脚本的语法 输出为 jpg png svg 或 gif 输出应该是这样的 set5 s
  • Grep 递归和计数

    需要在具有大量子目录的目录中搜索文件内的字符串 我在用着 grep c r string here 我怎样才能找到总数量 如何仅输出至少具有一个实例的文件 使用 Bash 的进程替换 这给出了我认为是您想要的输出 如果不是 请澄清问题 gr
  • 用于获取特定用户 ID 和进程数的 Bash 脚本

    我需要 bash 脚本来计算特定用户或所有用户的进程 我们可以输入 0 1 或更多参数 例如 myScript sh root deamon 应该像这样执行 root 92 deamon 8 2 users has total proces
  • 如何回忆上一个 bash 命令的参数?

    Bash 有没有办法回忆上一个命令的参数 我通常这样做vi file c其次是gcc file c Bash 有没有办法回忆上一个命令的参数 您可以使用 or 调用上一个命令的最后一个参数 Also Alt can be used to r

随机推荐

  • 个人支付接口

    分享个人可以用的支付接口 用来实现收款实时回调 不用企业资质 不用签约 很方便 1 接口地址 请求地址 https www gogozhifu com shop api createOrder 2 注意事项 数据编码统一为utf 8 Req
  • 仿滴滴打车百度地图定位查找附近出租车或门店信息

    随着技术的发展 开发的复杂度也越来越高 传统开发方式将一个系统做成了整块应用 经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改 造成牵一发而动全身 通过组件化开发 可以有效实现单独开发 单独维护 而且他们之间可以
  • 前置++与后置++区别 以及效率

    之前在杭州实习面试中科视拓时候面试官问道 前置 与后置 区别 以及效率 打破对C 的认识 后来 寒假回来看书后才恍然大悟 前置 和后置 的区别 C专家编程 中有如下描述 P276 人民邮电出版社 a表示取a的地址 增加它的内容 然后把值放在
  • 2021-Linux系统与管理 - (二)Linux系统命令【超详细】

    自说 Linux命令 Linux命令行的格式 Linux系统的基本命令 自说 学习Linux必定要学习的就是命令了 凡事都是一步一个脚印 这样才踏实 呢么在学习Linux这条道上 我们更加要循序渐进 先学会走再勇往直前 以下便是Linux命
  • 【Mysql】图解左、右、内与全连接

    一 前言 使用学生表与成绩表来演示Mysql中的各种连接查找 学生表的建表语句如下 CREATE TABLE student id int 11 NOT NULL AUTO INCREMENT COMMENT 自增序号 st id int
  • 深度学习的名词解释

    深度学习入门必须理解这25个概念 神经网络基础 1 神经元 Neuron 就像形成我们大脑基本元素的神经元一样 神经元形成神经网络的基本结构 想象一下 当我们得到新信息时我们该怎么做 当我们获取信息时 我们一般会处理它 然后生成一个输出 类
  • 【C语言程序设计实验】试卷管理系统(完整代码+函数详解) -->源代码文件文章末尾免费领取

    目录 实验简介 一 实验要求 二 实验代码 1 完整代码 2 代码解析 1 get next question number 2 add question 3 delete question 4 backup questions 5 del
  • js实现一个简易计算器

    正文 使用js实现一个简易计算器 可以进行简单的加减乘除 js代码 function jishuan 1 获取num1 num2 var num1 document getElementById num1 value var num2 do
  • TCP协议三次握手和四次分手以及数据传输过程

    1 三次握手 TCP是面向连接的 在面向连接的环境中 开始传输数据之前 在两个终端之间必须先建立一个连接 建立连接同步的过错称为三次握手 具体过程如下 1 当主机A想同主机B建立连接 主机A会发送SYN给主机B 初始化序列号seq x 主机
  • 深入了解数据导入的一些解决方案

    项目中除了有数据导出之外 也存在批量数据导入的场景 比如通过Execl电子表格进行批量开卡 批量导入用户或者部门信息等 今天探讨一下常见的数据导入方案 0x01 同步导入 如果批量导入的数据就是几十条到一两百条的数据量的话 而且每条数据导入
  • angular:使用Router.navigateByUrl传递state后如何在新组建获取state传递的data

    问题 如题 解决办法 使用history state来获取
  • 网络分析器Wireshark过滤器设置

    1 简介 Wireshark是目前全球使用最广泛的开源抓包软件 可以对网络进行故障定位 对网络黑客攻击进行快速定位 分析底层通信机制等 2 Wireshark安装 下载地址 Wireshark Go Deep https www wires
  • Mybatis快速学习笔记(建议收藏)

    Mybatis快速学习目录 Mybatis概念 MyBatis 快速入门 Mapper代理开发 MyBatis核心配置文件 配置文件的各种细节 Mybatis三部曲 如何解决字段名和属性名不一致问题 Mybatis如何接收来自数据库参数 多
  • 引用window自定义变量以及ts在window上自定义变量数据类型报错的解决方案

    一 问题描述 举个例子 在TypeScript中 当我们需要给window对象添加全局变量 如testName 或者需要使用window下自定义创建的变量 以testName为例 会出现以下ts报错 类型 Window typeof glo
  • 计算机网络详细讲解-----核心,边缘;datagram,virtual circuit【计算机网络养成】

    计算机网络养成 内容导航 计算机网络养成 网络边缘 应用进程和应用进程联系的模式 客户 服务器模式 C S架构 对等模式 peer peer 模式 网络边缘 采用网络设施的面向连接服务 目标 握手 TCP 传输控制协议 传输层 Transm
  • 英语专栏——application

    application application的基本意思是申请 申请表 但在IT术语中它是应用程序的意思 表示某种技术 系统 产品的应用 我们所称的APP就是这个的缩写 From Wikipedia Application software
  • Vue3通透教程【十五】补充TS开发环境搭建问题

    文章目录 写在前面 Node中搭建TS开发环境 验证环境 写在最后 写在前面 专栏介绍 凉哥作为 Vue 的忠实 粉丝输出过大量的 Vue 文章 应粉丝要求开始更新 Vue3 的相关技术文章 Vue 框架目前的地位大家应该都晓得 所谓三大框
  • Sigrity高速信号仿真之直流压降仿真

    1 简介 本文主要介绍直流压降仿真分析 电源直流压降的仿真 又称电源直流跌落仿真 DC IR Drop 或IR Drop HyperLynx软件中集成了电源完整性电源电压跌落仿真功能 DC Drop Simulation PowerScop
  • 2023 年 IDEA Maven 源修改为国内阿里云镜像的正确方式,亲测可用

    前言 由于众所周知的原因 maven的库在中国大陆非常慢 我在百度上搜到的大部分文章都是直接在 m2 settings xml 加入以下内容
  • Linux_网络项目_WEB服务器 设计CGI机制,与CGI处理数据

    文章紧跟之前web服务器返回静态网页 文章目录 CGI设计思路 设计CGI代码 CGI设计思路 CGI原理如下图 其中CGI程序可以使用任何后端语言进行编写 而服务器是可执行程序 而CGI程序也是可执行程序 可以创建子进程使用进程替换来实现