实现简易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


相关文章
|
11天前
|
搜索推荐 安全 网络安全
服务器支持HTTPS的时机和条件
【10月更文挑战第23天】服务器支持HTTPS的时机和条件
10 5
|
1月前
使用Netty实现文件传输的HTTP服务器和客户端
本文通过详细的代码示例,展示了如何使用Netty框架实现一个文件传输的HTTP服务器和客户端,包括服务端的文件处理和客户端的文件请求与接收。
32 1
使用Netty实现文件传输的HTTP服务器和客户端
|
5天前
|
存储 Oracle 关系型数据库
oracle服务器存储过程中调用http
通过配置权限、创建和调用存储过程,您可以在Oracle数据库中使用UTL_HTTP包发起HTTP请求。这使得Oracle存储过程可以与外部HTTP服务进行交互,从而实现更复杂的数据处理和集成。在实际应用中,根据具体需求调整请求类型和错误处理逻辑,以确保系统的稳定性和可靠性。
8 0
|
2月前
HAProxy的高级配置选项-配置haproxy支持https协议及服务器动态上下线
文章介绍了如何配置HAProxy以支持HTTPS协议和实现服务器的动态上下线。
131 8
HAProxy的高级配置选项-配置haproxy支持https协议及服务器动态上下线
|
2月前
|
开发者
HTTP状态码是由网页服务器返回的三位数字响应代码,用于表示请求的处理结果和状态
HTTP状态码是由网页服务器返回的三位数字响应代码,用于表示请求的处理结果和状态
31 1
|
3月前
|
缓存 数据安全/隐私保护 UED
代理服务器在HTTP请求中的应用:Ruby实例
代理服务器在HTTP请求中的应用:Ruby实例
|
3月前
|
移动开发 网络协议 编译器
实战案例3:C语言实现的HTTP服务器
实战案例3:C语言实现的HTTP服务器
143 0
|
Web App开发 安全 应用服务中间件
|
2月前
|
监控 安全 搜索推荐
设置 HTTPS 协议以确保数据传输的安全性
设置 HTTPS 协议以确保数据传输的安全性