初识协议

Posted ych9527

tags:

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

1.协议

1.1结构化数据

程序员写的一个个解决实际问题,满足日常需求的网络程序都是在应用层
协议是一种约定、socket api的都是按照比特位的方式来进行发送和接收的,如果要传输一些"结构化的数据",怎么办呢?

结构化数据:比如发送QQ消息 、消息的组成有 -> 头像、称谓、信息、时间等等 ->这种数据就叫做结构化数据

1.2序列化和反序列化

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

序列化和反序列化工具(json、xml两种数据格式 -> 序列化和反序列化建议使用工具)

2.网络版的计算器

了解协议 -> 自定义协议实现网络计数器

//自定义协议
#pragma once

typedef struct Cal
{
  int x;
  int y;
  char op;
  int result;
  int code;
}cal;

#pragma once
#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <unistd.h>
#include "protocol.hpp"
#include <netinet/in.h>
#include <arpa/inet.h>

#define NUM 5

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)
    {
      cerr<<"lsock error!!"<<endl;
      exit(1);
    }

    //服务器绑定,填充服务器信息
    struct sockaddr_in ser_add;
    ser_add.sin_family=AF_INET;
    ser_add.sin_port=htons(port);
    ser_add.sin_addr.s_addr=INADDR_ANY;

    if(bind(lsock,(struct sockaddr*)&ser_add,sizeof(ser_add))< 0)
    {
      cerr<<"bind error"<<endl;
      exit(2);
    }
    
    
    //监听套接字
    if(listen(lsock,NUM)< 0)
    {
      cerr<<"listen error"<<endl;
      exit(3);
    }
  }

  void Cal(int sock)
  {
    cal ret;
    size_t size=recv(sock,&ret,sizeof(ret),0);
    if(size>0)
    {
      ret.code=0;
      switch(ret.op)
      {
        case'+':
          ret.result=ret.x+ret.y;
          break;
        case '-':
          ret.result=ret.x-ret.y;
          break;
        case'*':
          ret.result=ret.x*ret.y;
          break;
        case '/':
          if(ret.y==0)//除数不能为0
          {
            ret.code=1;
          }
          else
            ret.result=ret.x/ret.y;
          break;
        default:
          ret.code=2;//表示输入的操作数不符合
          break;
      }
    }
    send(sock,&ret,sizeof(ret),0);
    close(sock);//短链接
  }

  void start()
  {
     sockaddr_in peer;//获取对方信息
     socklen_t len =sizeof(peer);
     
     //建立链接
     while(true)
     {
        int sock=accept(lsock,(struct sockaddr*)&peer,&len); 
        if(sock < 0)
        {
          cerr<<"accept errot"<<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);
    }
};


#include "server.hpp"
void Usage(string str)
{
  cout<<"Usage"<<"\\t"<<"\\n";
  cout<<str<<":"<<"please enter your port"<<endl;
}

int main(int argc , char *argv[])
{
  if(argc!=2)
  {
    Usage(argv[0]);
    exit(5);
  }

  Server *sv=new Server(stoi(argv[1]));
  sv->InitServer();
  sv->start();
  delete sv;
  return 0;
}


#pragma once
#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <unistd.h>
#include "protocol.hpp"
#include <netinet/in.h>
#include <arpa/inet.h>

#define NUM 5
class Client
{
  private:
    string ip;
    int port;
    int sock;

  public:
    Client(string _ip,int _port)
      :ip(_ip)
      ,port(_port)
       ,sock(-1)
  {}
    
  void InitClient()
  {
    //创建套接字
    sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock < 0)
    {
      cerr<<"sock error!!"<<endl;
      exit(1);
    }
  }

  void start()
  {
    //发送给谁、填写信息
    sockaddr_in ser_add;
    ser_add.sin_family=AF_INET;
    ser_add.sin_addr.s_addr=inet_addr(ip.c_str());//字符串转成网络整形
    ser_add.sin_port=htons(port);

    if(connect(sock,(struct sockaddr*)&ser_add,sizeof(ser_add))!=0)
    {
      cerr<<"connect error"<<endl;
      exit(2);
    }

    //链接成功,发送信息
    cal mes;
    cout<<"please enter one num:";
    cin>>mes.x;
    cout<<"please enter two num:";
    cin>>mes.y;
    cout<<"please enter op:";
    cin>>mes.op;

    send(sock,&mes,sizeof(mes),0);
    recv(sock,&mes,sizeof(mes),0);

    cout<<"code:"<<mes.code<<" "<<"result:"<<mes.result<<endl;
  }

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



#include "client.hpp"
void Usage(string str)
{
  cout<<"Usage"<<"\\t"<<"\\n";
  cout<<str<<":"<<"please enter your ip and port"<<endl;
}

int main(int argc , char *argv[])
{
  if(argc!=3)
  {
    Usage(argv[0]);
    exit(5);
  }

 Client *ct = new Client(argv[1],atoi(argv[2]));
 ct->InitClient();
 ct->start();

  delete ct;

  return 0;
}


效果展示:

image-20210522145312151
注意:
实验演示是在同一台机器上的,所以用相等的结构体相当于完成了数据的序列化和反序列化,但是这种方法是不推荐的。
因为不在同一台机器上时,用结构体来进行发送,内部对齐解释可能会不一样

3.抓包工具

tcpdump:传输层的协议基本可以抓
tcpdump -i 指定sniffer操作的侦听端口,比如:tcpdump -i eth0 , tcpdump -i lo0 。
image-20210522160811800
tcpdump -i any 只要发送至这台主机的都要抓
-n:主机名这些能显示成数字就显示成数字
-nn:将更多的信息显示成数字
image-20210522160842537

4.初识http协议

尽管应用层协议是由程序员自己来定的,但是已经有一些现成的并且非常好用的应用层协议,供我们直接参考使用,比如http(超文本传输协议)就是其中之一

4.1初识URL

URL就是平时俗称的网址
image-20210522171420747

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

Java初识方法

初识OpenGL 片段着色器(Fragment Shader)

初识OpenGL 片段着色器(Fragment Shader)

Linux丨初识Linux

初识OpenGL (-)纹理(Texture)

初识OpenGL (-)纹理(Texture)