3-1:HTTP协议之应用层协议了解

文章目录

网络分层十分复杂,但是我们开发的大部分网络程序,均在应用层上运行

(1)协议

OSI分层模型中,每一层都有自己的协议,还是那句话协议是双方建立的约定,在应用层也是这样。读写数据时,实际上是按照比特位进行接受的,那么对于一些结构化的数据应该怎样传输呢,这样就需要我们制定出一些协议。这种打包数据和解包数据的过程称之为序列化和反序列化

  • 序列化:把对象转为字节序列
  • 反序列化:把字节序列恢复为对象

3-1:HTTP协议之应用层协议了解

(2)网络版“计算器”

根据之前学习过的套接字编程,我们可以编写一个网络版的计算器。代码如下
Server.cpp :服务器接受客户端的发来的操作数和运算符完成相应的计算

#include "Server.h"

int main(int argc,char* argv[])
{
    if(argc!=2)
    {
      cout<<"端口号未传入"<<endl;
      exit(1);
    }
    Server* se=new Server(atoi(argv[1]));
    se->InitServer();
    se->StartServer();
    return 0;

}

Server.h

#include <iostream>
#include <signal.h>
#include <string>
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "protocol.h"
using namespace std;

class Server
{
private:
  int _port;
  int _listen_socket;
public:
  Server(int port):_port(port)
  {}
  void InitServer()
  {
    signal(SIGCHLD,SIG_IGN);
    _listen_socket=socket(AF_INET,SOCK_STREAM,0);
    if(_listen_socket < 0)
    {
      cout<<"socket creat faile"<<endl;
      exit(2);
    }
    struct sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_port=htons(_port);
    local.sin_addr.s_addr=INADDR_ANY;

    if(bind(_listen_socket,(struct sockaddr*)&local,sizeof(local)) < 0)
    {
      cout<<"socket bind faile"<<endl;
      exit(3);
    }

    if(listen(_listen_socket,5) < 0)
    {
      cout<<"listen fail"<<endl;
      exit(4);
    }
  }
  
  void Service(int sock)
  {
    //服务器执行计算
    request_t rq;
    response_t rp={4,0};//默认状态码从4开始

    ssize_t s=recv(sock,&rq,sizeof(rq),0);//接受客户端发来的计算要求  
    if(s > 0)
    { 
        switch(rq.op)//判断是什么操作数
        {
          case '+':
            rp.result=rq.x+rq.y;
            break;
          case '-':
            rp.result=rq.x-rq.y;
            break;
          case '*':
            rp.result=rq.x*rq.y;
            break;
          case '/':
            if(rq.y!=0)
            {
              rp.result=rq.x/rq.y;
            }
            else 
            {
              rp.code=1;//状态码为1表示除数为0
            }
            break;
          case '%':
            if(rq.y!=0)
            {
              rp.result=rq.x%rq.y;
            }
            else 
            {
              rp.code=2;//状态码为2表示%运算时除数为0
            }
          default:
            rp.code=3;//状态码为3表示运算符输入错误

        }
    }
    send(sock,&rp,sizeof(rp),0);//把结果发给客户端
    close(sock);

  }

  void StartServer()
  {
    struct sockaddr_in peer;
    while(1)
    {
      socklen_t len=sizeof(peer);
      int sock=accept(_listen_socket,(struct sockaddr*)&peer,&len);
      if(sock < 0)
      {
        cout<<"accept faile"<<endl;
        continue;
      }
      pid_t id=fork();
      if(id == 0)
      {
        close(_listen_socket);
        Service(sock);
        exit(0);
      }
      close(sock);
    }
  }

  ~Server()
  {
    close(_listen_socket);
  }
};

Client.cpp:客户端将数据发送给服务端

#include "Client.h"

int main(int argc,char* argv[])
{
  if(argc!=3)
  {
    cout<<"请完整输入ip地址和端口号"<<endl;
    exit(1);
  }
  Client* cp=new Client(argv[1],atoi(argv[2]));
  cp->InitClient();
  cp->StartClient();
  return 0;
}

Client.h

#include <iostream>
#include <signal.h>
#include <string>
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "protocol.h"
using namespace std;

class Client
{
private:
  int _port;
  string _ip;//服务器ip地址
  int _socket;

public:
  Client(string ip,int port):_ip(ip),_port(port)
  {}
  void InitClient()
  {
    signal(SIGCHLD,SIG_IGN);
    _socket=socket(AF_INET,SOCK_STREAM,0);
    if(_socket < 0)
    {
      cout<<"socket creat faile"<<endl;
      exit(2);
    }
  }
  void StartClient()
  {
    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(_socket,(struct sockaddr*)&server,sizeof(server)) < 0)
    {
      cout<<"connected fail"<<endl;
      exit(2);
    }

    request_t rq;//客户端接收用户输入
    cout<<"请输入左操作数:"<<endl;
    cin>>rq.x;
    cout<<"请输入右操作数:"<<endl;
    cin>>rq.y;
    cout<<"请输入运算符:"<<endl;
    cin>>rq.op;

    send(_socket,&rq,sizeof(rq),0);
    
    response_t rp;//接收结果
    recv(_socket,&rp,sizeof(rp),0);

    cout<<"状态码为:"<<rp.code<<endl;
    cout<<"结果为:"<<rp.result<<endl;
  }
  ~Client()
  {
    close(_socket);
  }
};

Protocol.h:服务端和客户端都必须遵从的计算协议

#ifndef _PROTOCOL_H
#define _PROTOCOL_H 

#include <iostream>

typedef struct request
{
  int x;//操作数1
  int y;//操作数2
  char op;//运算符
  
}request_t;

typedef struct response
{
  int code;//返回状态,有可能计算失败
  int result;//计算结果
}response_t;
#endif 

关于这份代码有以下几点需要强调

  1. 所谓的协议:指的就是对于客户端和服务端他们都要认为x是左操作数,y是右操作数,并且右操作数在进行某些运算时不能为0;如果出现非法计算,客户端和服务单都要明白不同状态码代表的意思是什么;客户端发送时发送了一个request,接受时接受了一个response
  2. 上述过程其实已经完成了序列化和反序列化,之所以没有明显的感觉是因为客户端发送时发送了一个request,接受时接受了一个response,他们都是非常标准的结构化数据,在发送时是二进制流,但是解包时被当做了标准的结构体解包
  3. 这种“序列化后反序列化”的方式是严重不推荐的,因为客户端和服务端可能在完全不同的环境中。实际工作中可以使用正规的序列化工具,如jsonxml

代码效果如下
3-1:HTTP协议之应用层协议了解

上一篇:程序刚启动,发生三次FullGc的问题追踪


下一篇:进程创建