HTTP-

网络版计算器

抓包:tcpdump -i any -nn tcp port 8080

参数解释:
-i:表示想要抓那个网络
-nn:第一个n表示能把主机名用数字表示的都用数字表示,第二个n表示把更多的信息能用数字表示的用数字表示。
tcp:表示想要抓的协议
any:任意端口
8080:服务器端口号
Protocol.hpp 定义通信的结构体

#ifndef __PROTOCOL_HPP__
#define __PROTOCOL_HPP__ 

#include<iostream>
typedef struct Request{
  int x;//左操作数
  int y;//右操作数
  char op;//操作符
}Request;
typedef struct Response{
  int code;//结果值是否可被信任,0:结果可以信任
  int result;//结果
}Response;
#endif 

Server.hpp

#ifndef __SERVER_HPP__
#define __SERVER_HPP__

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

using namespace std;

class server{
    private:
        int port;
        int lsock;
    public:
        server(int _p):port(_p),lsock(-1)
        {}
        void initServer()
        {
            lsock = socket(AF_INET, SOCK_STREAM, 0);
            if( lsock < 0 )
            {
                cerr << "socket error " << 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(lsock, (struct sockaddr*)&local, sizeof(local)) < 0){
                cerr << "bind error!" << endl;
                exit(3);
            }

            if(listen(lsock, 5) < 0){
                cerr << "listen error!\n" << endl;
            }
        }

        void cal(int sock)
        {
            //短连接来完成对应的计算!
            Request  rq;给服务器发结构化数据rq
            Response rsp={4, 0};
            ssize_t s = recv(sock, &rq, sizeof(rq), 0);从sock里读的内容放到rq,期望读sizeof(rq),以默认的阻塞方式读取,收到rq
            if(s > 0){
                rsp.code = 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(peer);
                int sock = accept(lsock, (struct sockaddr*)&peer,&len);
                if( sock < 0 ){
                    cerr << "accept error!" << endl;
                    continue;
                }
                if(fork() == 0){
                    if(fork() > 0){
                        exit(0);
                    }
                    //孙子
                    close(lsock);
                    //todo
                    cal(sock);
                    exit(0);
                }
                close(sock);
                waitpid(-1, nullptr, 0);
            }
        }
        ~server()
        {
            close(lsock);
        }
};

#endif


Server.cc

#include"Server.hpp"

void Menu(string str)
{
  cout<<"Usage: "<<endl;
  cout<<str<<"server port"<<endl;
}
int main(int argc,char* argv[])
{
  if(argc!=2)
  {
   Menu(argv[0]);
   exit(1);
  }

  server *cp=new server(atoi(argv[1]));端口号
  cp->initServer();
  cp->start();

  delete cp;
  return 0;
}


Client.hpp

#ifndef __CLIENT_HPP__
#define __CLIENT_HPP__ 

#include<iostream>
#include<unistd.h>
#include<cstdlib>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/wait.h>
#include"Protocol.hpp"
class client{
  private:
    std::string ip;
    int port;
    int sock;
  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){通过sock连接server
          std::cerr << "connect error"<<std::endl;
          exit(3);  
        }

      //建立的是短链接,所以只需要C发过去,S返回回来,那么一次通信就算结束了
      Request rq;
      Response rsp;
      std::cout << "date1# ";
      std::cin >> rq.x;
      std::cout << "date2# ";
      std::cin >> rq.y;
      std::cout << "op# ";
      std::cin >> rq.op;
rq看作2进制流发送,
      send(sock,&rq,sizeof(rq),0);把rq往sock里塞,
      ssize_t s = recv(sock,&rsp,sizeof(rsp),0);
      if(s > 0){
        std:: cout <<"code : "<<  rsp.code <<std::endl;
        std::cout <<"result : "<<rsp.result << std::endl;
      }
    }


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

#endif 

Client.cc

#include"Client.hpp"

void Menu(string str)
{
  cout<<"Usage: "<<endl;
  cout<<str<<"server ip and  port"<<endl;
}
int main(int argc,char* argv[])
{
  if(argc!=3)
  {
   Menu(argv[0]);
   exit(1);
  }

  client *cp=new client(argv[1],atoi(argv[2]));IP和端口号
  cp->initClient();
  cp->start();

  delete cp;
  return 0;
}


HTTP

HTTP特征:
无连接:TCP建立连接和http无关,http直接向对方发送http request即可
无状态:http本身是无状态的,并不会记录任何用户信息,真正记录基本信息的技术:cookie+session
简单快速

简易HTTP服务器

#pragma once
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<signal.h>

#define BACKLOG 5
using namespace std;

class httpServer{
    private:
        int port;
        int lsock;
    public:
        httpServer(int _p):port(_p),lsock(-1)
        {}
        void InitServer()
        {
            signal(SIGCHLD,SIG_IGN);父进程忽略子进程发的的信号
            lsock = socket(AF_INET, SOCK_STREAM, 0);
            if( lsock < 0 )
            {
                cerr << "socket error " << 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(lsock, (struct sockaddr*)&local, sizeof(local)) < 0){
                cerr << "bind error!" << endl;
                exit(3);
            }

            if(listen(lsock, BACKLOG) < 0)//listen:等待队列
            {  cerr << "listen error"<<endl;
              exit(4);
            }
        }
        
        void Echo_http(int sock)
        {
          char request[2048];
          ssize_t s = recv(sock,request,sizeof(request),0);//从sock里读的数据放到request
          if(s > 0)
            //表示接收成功
            request[s] = 0;
            cout << request << endl;打印请求
           开始响应:
            string response = "HTTP/1.0 200 OK\r\n";//响应行
            response += "Content-type: text/html\r\n";//构造响应报头(Content-type:这里是有一个空格的)
            response += "\r\n";//空行
            response += "\
              <!DOCTYPE html>\
              <html>\
              <head>\
              <title>192.168.31.223</title>\
              </head>\
              <body>\
              <h1>Welcome</h1>\
              <p>helloworld</p>\
              </body>\
              </html>\
              ";  
              //响应正文
              send(sock,response.c_str(),response.size(),0);
          }给response往sock发送
          close(sock);短连接:11收即结束
        }

        void start()启动服务器
        {
            struct sockaddr_in peer;
            for(;;){
                socklen_t len = sizeof(peer);
                int sock = accept(lsock, (struct sockaddr*)&peer,&len);
 从peer接收lsock;&:取地址;获得新链接时,新链接的套接字信息在&peer里保存
                if( sock < 0 ){
                    cerr << "accept error!" << endl;
                    continue;
                }
                if(fork() == 0){
                  close(lsock);子进程处理链接
                  Echo_http(sock);
                  exit(0);任务处理完,退出
                }
                close(sock);父进程
            }
        }
        ~httpServer()
        {
            close(lsock);
        }
};

HttpServer.cc:

#include "httpserver.hpp"
static void Usage(string proc)
{cout c< "Usage : \n\t" ;
cout << proc< " port" << endl;}
int main ( int argc ,char *argv[])
if(argc !=2)参数传入1个,可执行行程序名1{
usage(argv[0]);
exit ( 1);
}

HttpServer *hp = new HttpServer(atoi(argv[1]));
hp->InitServer( );
hp->start();
return 0;

TCP协议段格式

32位序号:可靠传输。TCP将要传输的每个字节都进行了编号,序号是本报文段发送的数据组的第一个字节的编号,序号可以保证传输信息的有效性。(解决按序到达问题)
32位确认序号:确认号只有当ACK标志为1时才有效。(解决确认应答问题)
客户端发送fin之后,server没有关闭文件描述符,挂满CLOSE_WAIT时,消耗资源;文件描述符(文件)生命周期随进程,进程结束,自动释放=操作系统自动回收=自动关闭文件=自动4次挥手;
有了TIME_WAIT状态,即使不能够完全的确定是否在特定的时间内server端有接收到ACK信号,但是可以确认的是如果没有接收到,client端会再次收到FIN的信号,所以这里可以理解为在特定的时间段内”没有消息便是好消息”,所以TIME_WAIT最大的作用有两个①尽量保证最后一个ACK被对方收到,双方是正常状态下断开,尽快释放服务器的资源(那么如果你的ACK就是丢了,server端在多次重传下,依旧无法完成四次挥手,那么肯定是网络出现了问题,此时就会强制的关闭连接)②等待历史数据在网络上进行消散,发送的数据全都被收到
主动断开连接的一方要进入TIME_WAIT状态,连接没有完全释放,不能立即启动,端口复用函数:立即启动
TIME_WAIT的时间:2MSL(最大传送时间—数据从一方发送到另外一方所花费的最大时间)。因为不管是超时重传还是等待历史数据在网络中进行消散2MSL已经足够了
客户端退出,服务器启动,客户端连接,服务器不能启动,再过一段时间(TIME_WAIT时间段过去之后),可以启动。
那么既然快重传即快又能重传,超时重传呢?原因是他们两个之间其实是互相补充的,并不矛盾。如果ACK都丢了,那么也就不可能收到三个连续重复的确认应答信号,所以超时重传是一种兜底,少于3次应答的情况时,使用超时重传
3次握手:最后1次ack丢失时,1方客户端建立连接,1方没有连接,客户端:背锅:浪费资源;
4次挥手

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值