实现简易HTTP服务器

简介: 实现简易HTTP服务器

一、套接字接口类封装


#include<iostream>
#include<string>
#include<vector>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/socket.h>
//封装TCPsocket类
#define MAX_LISTEN 5
#define CHECK_RETURN(X) if((X) == false) {return -1;}
class TCPsocket {
  private:
    int _sockfd;
  public:
    TCPsocket () : _sockfd(-1) {}
    //1.创建套接字
    bool Socket() {
      _sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
      if (_sockfd < 0) {
        perror("create socket error!");
        return false;
      }
      return true;
    } 
    //2.为套接字绑定地址信息
    bool Bind(const std::string &ip, uint16_t port) {
      struct sockaddr_in addr;
      addr.sin_family = AF_INET;
      addr.sin_port = htons(port);
      addr.sin_addr.s_addr = inet_addr(ip.c_str());
      socklen_t len = sizeof(struct sockaddr_in);
      int ret = bind(_sockfd, (struct sockaddr*)&addr, len);
      if (ret < 0) {
        perror("bind error!");
        return false;
      }
      return true;
    }
    //客户端:3.向服务器发起连接请求
    bool Connect(const std::string &ip, uint16_t port) {
      struct sockaddr_in addr;
      addr.sin_family = AF_INET;
      addr.sin_port = htons(port);
      addr.sin_addr.s_addr = inet_addr(ip.c_str());
      socklen_t len = sizeof(struct sockaddr_in);
      int ret = connect(_sockfd, (struct sockaddr*)&addr, len);
      if (ret < 0) {
        perror("connect error!");
        return false;
      }
      return true;
    }
    //服务端:3.开始监听
    bool Listen(int backlog = MAX_LISTEN) {
      int ret = listen(_sockfd, backlog);
      if (ret < 0) {
        perror("connect error!");
        return false;
      }
      return true;
    }
    //服务端:4. 获取新建连接
    bool Accept(TCPsocket *sock, std::string *ip = NULL, uint16_t *port = NULL) {
      struct sockaddr_in addr;
      socklen_t len = sizeof(struct sockaddr_in);
      int newfd = accept(_sockfd, (struct sockaddr*)&addr, &len);
      if (newfd < 0) {
        perror("accept error!");
        return false;
      }
      sock -> _sockfd = newfd;
      if (ip != NULL) *ip = inet_ntoa(addr.sin_addr);
      if (port != NULL) *port = ntohs(addr.sin_port);
      return true;
    }
    //4. 接收数据
    bool Recve(std::string *body) {
      char temp[8193] = {0};
      int ret = recv(_sockfd, temp, 8192, 0);
      if (ret < 0) {
        perror("recve error!");
        return false;
      }
      else if (ret == 0) {
        std::cout<<"peer shutdown!"<< std::endl;
        return false;
      }
      body -> assign(temp, ret);
      return true;
    }
    //5.发送数据
    bool Send(const std::string &body) {
      int ret = send(_sockfd, body.c_str(), body.size(), 0);
      if (ret < 0) {
        perror("send error!");
        return false;
      }
      return true;
    }
    //6.关闭套接字
    bool Close() {
      if (_sockfd != -1) close(_sockfd);
      return true;
    }
};


二、服务器实现


#include "socket_tcp.hpp"
#include <sstream>
int main(int argc, char *argv[]) {
  if (argc != 2) {
    std::cout<<"./http_server  port"<< std::endl;
    return -1;
  }
  int srv_port = std::stoi(argv[1]);
  std::string srv_ip = "0.0.0.0";
  TCPsocket sock;
  //1.创建套接字
  CHECK_RETURN(sock.Socket());
  //2.绑定地址信息
  CHECK_RETURN(sock.Bind(srv_ip, srv_port));
  //3.开始监听
  CHECK_RETURN(sock.Listen());
  while(1) {
    TCPsocket new_sock;
    bool ret = sock.Accept(&new_sock);
    //4.获取新建连接
    if (ret == false) continue;
    //5.收发数据
    std::string rec;
    new_sock.Recve(&rec);
    size_t pos = rec.find("\r\n\r\n");//查找头部结尾标志
    if (pos == std::string::npos) {
      //没有找到,则认为头部过大
      //将响应状态码设置为414
      //这里简单实现,就直接错误返回了
      return -1;
    }
    std::string header = rec.substr(0,pos);//截取头部
    std::string body = rec.substr(pos + 4);//截取正文
    //正常的截图正文,应该将头部按照\r\n进行分割,逐个取出头部;
    //然后根据头部中的Content-Length确定正文长度,正文长度= rec.size() - header.size() - 4;
    //然后在截取正文
    std::cout<<"header:[" << body << "]" << std::endl;
    std::cout<< "body:[" << body << "]" << std::endl;
    //响应
    std::string rep_body = "<html><body><h1>Hello world!</h1></body></html>";
    std::stringstream ss_reply;
    ss_reply << "HTTP/1.1 200 OK\r\n";
    ss_reply << "Connection: close\r\n";
    ss_reply << "Content-Type: text/html\r\n";
    ss_reply << "Content-Length: " << rep_body.size() << "\r\n";
    ss_reply << "\r\n";
    ss_reply << rep_body;
    std::cout << "response:[" << ss_reply.str() << "]" << std::endl;
    new_sock.Send(ss_reply.str());
    //短连接,完成响应,关闭连接
    new_sock.Close();
  }
  //6.关闭套接字
  sock.Close();
  return 0;
}


三、实现效果


1.运行服务端


1.png


2.在浏览器输入URL进行请求获取响应


 image.png


相关文章
|
24天前
|
XML 网络协议 Java
JavaWeb -- HTTP -- WEB服务器TOMCAT
JavaWeb -- HTTP -- WEB服务器TOMCAT
|
2月前
|
监控 Unix 应用服务中间件
Android-音视频学习系列-(八)基于-Nginx-搭建(rtmp、http)直播服务器
Android-音视频学习系列-(八)基于-Nginx-搭建(rtmp、http)直播服务器
|
10天前
|
前端开发 小程序 应用服务中间件
在服务器上正确配置域名https证书(ssl)及为什么不推荐使用宝塔申请免费ssl证书
在服务器上正确配置域名https证书(ssl)及为什么不推荐使用宝塔申请免费ssl证书
59 4
|
18天前
|
Java 应用服务中间件 程序员
JavaWeb基础第四章(SpringBootWeb工程,HTTP协议与Web服务器-Tomcat)
JavaWeb基础第四章(SpringBootWeb工程,HTTP协议与Web服务器-Tomcat)
|
2月前
|
网络协议 数据格式 Python
Python进阶---HTTP协议和Web服务器
Python进阶---HTTP协议和Web服务器
29 4
|
2月前
|
自然语言处理 负载均衡 监控
处理HTTP请求的服务器
处理HTTP请求的服务器
41 1
|
7天前
|
弹性计算 缓存 安全
阿里云服务器ECS收费标准参考,2核4G配置ECS实例规格整理
阿里云提供多种2核4G ECS实例,如计算型c7、经济型e、u1等,价格不等,从68.0元/月到203.0元/月。ECS通用算力型u1实例采用高性能Intel处理器,网络收发包能力达30万PPS。经济型e实例基于Intel Xeon Platinum,适合入门级需求。2核4G服务器支持的并发访问人数依赖于软件效率、带宽、应用架构和用户行为等因素。更多信息请查看阿里云ECS产品页。
|
2天前
|
弹性计算
阿里云ECS的使用心得
本文主要讲述了我是如何了解到ECS,使用ECS的一些经验,以及自己的感悟心得
|
4天前
|
弹性计算 运维 Kubernetes
阿里云ECS与混合云策略结合,提供云上云下无缝对接,提升业务灵活性和运维效率。
【7月更文挑战第3天】阿里云ECS与混合云策略结合,提供云上云下无缝对接,提升业务灵活性和运维效率。ECS支持多种计费模式和先进架构,保证低延迟计算。混合云融合公有云灵活性与私有云安全,实现资源最优配置。通过VPC互通、应用迁移、数据同步实践,确保安全合规,助力企业数字化转型。阿里云服务展示技术实力,支持企业在混合云时代抓住机遇。
28 3
|
3天前
|
弹性计算
阿里云ECS的使用心得
本文主要讲述了我是如何了解到ECS,使用ECS的一些经验,以及自己的感悟心得