协议(初识HTTP协议)

Posted Y—X

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了协议(初识HTTP协议)相关的知识,希望对你有一定的参考价值。

1协议

1.1结构化数据

协议是一种 “约定”. socket api的接口, 在读写数据时, 都是按 “字符串” 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢?

结构化的数据:比如发送微信消息,消息的组合由时间,发送人等信息

序列化:发送信息的时候、需要将信息多变一
反序列化:接收信息、将信息一变多

2网络版计算器

例如, 我们需要实现一个服务器版的加法器. 我们需要客户端把要计算的两个加数发过去, 然后由服务器进行计算, 最后再把结果返回给客户端。

约定方案:

  • 定义结构体来表示我们需要交互的信息;
  • 发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体;
  • 这个过程叫做 “序列化” 和 "反序列化

这种约定, 就是 应用层协议

//Protocol.hpp:
#ifndef __PROTOCOL_HPP_ 
#define __PROTOCOL_HPP_ 

#include <iostream>
//用结构体定的协议
typedef struct request

  int x;
  int y;
  char op;
request_t;

typedef struct response

  int result;
  int code;
response_t;

#endif 

#ifndef _SERVER_HPP_
#define _SERVER_HPP_ 

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstdlib>
#include <sys/wait.h>
#include "Protocol.hpp"

class server 

  private:
    int port;
    int lsock;
  public:
    server(int _port):port(_port),lsock(-1)
    

    void initServer()
    
      //创建套接字
      lsock = socket(AF_INET, SOCK_STREAM, 0);
      if(lsock < 0)
      
        std::cerr << "socket error" << std::endl;
        exit(2);
      
      
      //服务器绑定,填充服务器信息
      struct sockaddr_in local;
      local.sin_family = AF_INET;
      local.sin_port = htons(port);
      local.sin_addr.s_addr = htonl(INADDR_ANY);

      if(bind(lsock, (struct sockaddr*)&local, sizeof(local)) < 0)//传到inet_sock
      
        std::cerr << "bind error! " << std::endl;
        exit(3);
      
      
	  //监听套接字
      if(listen(lsock, 5) < 0)
      
        std::cerr << "listen error!" << std::endl;
        //exit(4); 
      
    
      
    void cal(int sock)
    
      //短链接来完成计算
      request_t rq;
      response_t rsp = 4, 0;
      ssize_t s = recv(sock, &rq, sizeof(rq), 0);//BUG
      if(s > 0)
      
        switch(rq.op)
        
          case '+':
            rsp.result = rq.x + rq.y;
            break;
          case '-':
            rsp.result = rq.x - rq.y;
            break;
          case '*':
            rsp.result = rq.x * rq.y;
            break;
          case '/':
            if(rq.y != 0)
            
              rsp.result = rq.x / rq.y;
            
            else 
            
              rsp.code = 1;
            
            break;
          case '%':
            if(rq.y != 0)
            
              rsp.result = rq.x % rq.y;
            
            else 
            
              rsp.code = 2;
            
            break;
          default:
            rsp.code = 3;
            break;
        
      
      
      send(sock, &rsp, sizeof(rsp), 0);
      close(sock);
    

    void start()
    
      struct sockaddr_in peer;
      for(;;)
      
        socklen_t len = sizeof(len);
        int sock = accept(lsock, (struct sockaddr*)&peer, &len);
        if(sock < 0)
        
          std::cerr << "accept error!" << std::endl;
          continue;
        

        if(fork() == 0)
        
          if(fork() > 0)//子进程
          
            exit(0);
          
          //孙子
          close(lsock);
          
          //具体服务
          cal(sock);
          
          exit(0);//短链接、调用完就退出
        

        //父进程
        close(sock);
        waitpid(-1, nullptr, 0);//等待任意子进程
      
    

    ~server()
    
      close(lsock);
    
;

#endif 

#include "Server.hpp"

void Menu(std::string proc)

  std::cout << "Usage: \\n\\t";
  std::cout << proc << "svr_ip svr_port" << std::endl;


int main(int argc, char *argv[])

  if(argc != 2)
  
    Menu(argv[0]);
    exit(1);
  

  server *sp = new server(atoi(argv[1])); 
  sp->initServer();
  sp->start();
  
  delete sp;
  return 0;


#ifndef _CLIENT_HPP_
#define _CLIENT_HPP_ 

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstdlib>
#include <sys/wait.h>
#include "Protocol.hpp"

class client 

  private:
    int port;
    int sock;
    std::string ip;
  public:
    client(std::string _ip, int _port):ip(_ip),port(_port),sock(-1)
    

    void initClient()
    
      sock = socket(AF_INET, SOCK_STREAM, 0);
      if(sock < 0)
      
        std::cerr << "socket error" << std::endl;
        exit(2);
      
      
    
      

    void start()
    
      struct sockaddr_in server;
      server.sin_family = AF_INET;
      server.sin_port = htons(port);
      server.sin_addr.s_addr = inet_addr(ip.c_str());//字符串转成网络整形

      if(connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0)
      
        std::cerr << "cennect error!" << std::endl;
        exit(2);
      
      
      response_t rsp;
      request_t rq;
      std::cout << "datax# "; 
      std::cin >> rq.x;
      std::cout << "datay# ";
      std::cin >> rq.y;
      std::cout << "op# ";
      std::cin >> rq.op;

      send(sock, &rq, sizeof(rq), 0);
      recv(sock, &rsp, sizeof(rsp), 0);

      std::cout << "code:" << rsp.code << std::endl;
      std::cout << "result: " << rsp.result << std::endl;
    

    ~client()
    
      close(sock);
    
;
#endif 

#include "Client.hpp"

void Menu(std::string proc)

  std::cout << "Usage: \\n\\t";
  std::cout << proc << "svr_ip svr_port" << std::endl;


int main(int argc, char *argv[])

  if(argc != 3)
  
    Menu(argv[0]);
    exit(1);
  

  client *cp = new client(argv[1], atoi(argv[2])); 
  cp->initClient();
  cp->start();
  
  delete cp;
  return 0;


代码运行效果展示:

3.抓包工具

tcpdump:
作用:抓包 (传输层的协议基本都可以抓

tcpdump -i 指定sniffer操作的侦听端口,比如:tcpdump -i eth0 ,

tcpdump -i any(发送至这台主机的都要抓) 。
-n:主机名这些能显示成数字就显示成数字
-nn:将更多的信息显示成数字


4. HTTP协议

虽然说应用层协议是由我们程序猿自己定的,但实际上已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接使用。比如 HTTP(超文本传输协议)

4.1URL

我们平时说的 “网址” 其实就是说的 URL。

?:后面叫做参数
互联网行为:

  1. 把服务器数据拿下来
  2. 把自己的数据传上服务器
    3.一般在搜索引擎中将搜索关键字拿到?后面

http:端口80 https:端口443

以上是关于协议(初识HTTP协议)的主要内容,如果未能解决你的问题,请参考以下文章

http 初识

初识网络协议:HTTP和HTTPS

协议(初识HTTP协议)

协议(初识HTTP协议)

协议(初识HTTP协议)

HTTP/1.1协议初识