从零实现HTTP服务器

响应:

第一部分测试代码,读取请求

Makefile

bin=httpserver #生成的可执行程序
cc=g++ #编译器名称
LD_FLAGS=-std=c++11 -lpthread #-DDEBUG=1 #链接选项
src=main.cc$(bin):$(src)$(cc) -o $@ $^ $(LD_FLAGS).PHONY:clean
clean:rm -f $(bin)

1111111

main.cc

#include <iostream>
#include <string>
#include <memory>
#include "HttpServer.hpp"
static void Usage(std::string proc)
{std::cout << "Usage:\n\t" << proc << " port" << std::endl;;
}int main(int argc, char *argv[])
{if( argc != 2 ){Usage(argv[0]);exit(4);}int port = atoi(argv[1]);std::shared_ptr<HttpServer> http_server(new HttpServer(port));http_server->InitServer();http_server->Loop();return 0;
}

11111

Log.hpp

#pragma once#include <iostream>
#include <string>
#include <ctime>#define INFO    1
#define WARNING 2
#define ERROR   3
#define FATAL   4#define LOG(level, message) Log(#level, message, __FILE__, __LINE__)void Log(std::string level, std::string message, std::string file_name, int line)
{   // [日志级别][时间戳][日志信息][错误文件名称][行数]std::cout << "[" << level << "]" << "[" << time(nullptr) << "]" << "[" << message << "]" << "[" << file_name << "]" << "[" << line << "]" << std::endl;
}

111

TcpServer.hpp

#pragma once#include <iostream>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include "Log.hpp"#define BACKLOG 7class TcpServer
{private:int _port;int _listen_sock;static TcpServer* _svr; // 指向单例对象的static指针,懒汉模式private: // 单例模式TcpServer(int port): _port(port), _listen_sock(-1){}TcpServer(){}TcpServer(const TcpServer& s) = delete;TcpServer* operator=(const TcpServer& s) = delete;public:static TcpServer *GetInstance(int port){static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;if(nullptr == _svr){pthread_mutex_lock(&lock);if(nullptr == _svr){_svr = new TcpServer(port);_svr->InitServer();}pthread_mutex_unlock(&lock);}return _svr;}void InitServer(){Socket();Bind();Listen();LOG(INFO, "tcp_server init ... success");}void Socket(){_listen_sock = socket(AF_INET, SOCK_STREAM, 0);if(_listen_sock < 0){LOG(FATAL, "socket error");exit(1);}int opt = 1; // 端口复用:宕机了也能立马绑定端口号setsockopt(_listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));LOG(INFO, "create socket ... success");}void Bind(){struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY; //云服务器不能直接绑定公网IPif(bind(_listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){LOG(FATAL, "bind error");exit(2);}LOG(INFO, "bind socket ... success");}void Listen(){if(listen(_listen_sock, BACKLOG) < 0){LOG(FATAL, "listen socket error");exit(3);}LOG(INFO, "listen socket ... success");}int Sock(){return _listen_sock;}~TcpServer(){if(_listen_sock >= 0){close(_listen_sock);}}
};TcpServer* TcpServer::_svr = nullptr;

11111111

HttpServer.hpp

#pragma once#include <iostream>
#include <pthread.h>
#include <signal.h>
#include "Log.hpp"
#include "TcpServer.hpp"
#include "Protocol.hpp"#define PORT 7777class HttpServer
{private:int _port;bool _stop;public:HttpServer(int port = PORT): _port(port), _stop(false){}void InitServer(){// 信号SIGPIPE进行忽略,否则在写入时候,可能直接崩溃serversignal(SIGPIPE, SIG_IGN); }void Loop(){TcpServer *tsvr = TcpServer::GetInstance(_port);LOG(INFO, "Loop begin");while(!_stop){struct sockaddr_in peer;socklen_t len = sizeof(peer);int sock = accept(tsvr->Sock(), (struct sockaddr*)&peer, &len);if(sock < 0) // 获取套接字失败{continue;}LOG(INFO, "Get a new link");int *_sock = new int(sock); // 暂时方案pthread_t pid;pthread_create(&pid, nullptr, Entrance::HandlerRequest, _sock);pthread_detach(pid);}}~HttpServer(){}
};

1111

Protocol.hpp

#pragma once#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "Log.hpp"
#include "Util.hpp"#define DEBUG
class Entrance // 线程入口
{
public:Entrance(){}~Entrance(){}static void* HandlerRequest(void * _sock){int sock = *(int*)_sock; // 临时测试方案LOG(INFO, "Hander Request Begin");
#ifdef DEBUG//For Testchar buffer[4096];recv(sock, buffer, sizeof(buffer), 0);std::cout << "-------------begin----------------" << std::endl;std::cout << buffer << std::endl;std::cout << "-------------end----------------" << std::endl;
#else EndPoint* ep = new EndPoint(sock);ep->RecvHttpRequest();ep->BuildHttpResponse();ep->SendHttpResponse();delete ep;
#endifLOG(INFO, "Hander Request End");}
};

测试结果

11111

第二部分测试代码,解析请求,返回静态网页

过程截图:

读取请求行和报头

解析uri:

返回了静态网页:

telnet测试:

加上了响应类型和长度:

第二份代码链接:

Linux_Code: 存放linux学习代码 - Gitee.com


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/76388.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

构建高可靠C++服务框架:从日志系统到任务调度器的完整实现

构建高可靠C服务框架&#xff1a;从日志系统到任务调度器的完整实现 一、深度解析示例代码技术体系 1.1 日志系统的进阶应用 示例代码中的ZRY_LOG_XXX宏展示了基础日志功能&#xff0c;但在生产环境中我们需要更完善的日志系统&#xff1a; 推荐技术栈组合&#xff1a; sp…

小张的工厂进化史——工厂模式

小张的工厂进化史——工厂模式 一、简单工厂模式&#xff1a;全能生产线二、工厂方法模式&#xff1a;分品牌代工三、抽象工厂模式&#xff1a;生态产品族四、三种模式核心对比表五、结合Spring实现简单工厂&#xff08;实践&#xff09; 小张从华强北起家&#xff0c;最初只有…

Python中的eval()函数详解

文章目录 Python中的eval()函数详解基本语法基本用法安全性问题安全使用建议实际应用场景与exec()的区别性能考虑总结 Python中的eval()函数详解 eval()是Python的一个内置函数&#xff0c;用于执行字符串形式的Python表达式并返回结果。它是一个强大但需要谨慎使用的函数。 …

银行业务发展历史

银行业务发展历史 银行业务的发展可以追溯到古代&#xff0c;但其现代形式的发展可以追溯到中世纪。以下是银行业务发展的主要历史阶段&#xff1a; 1. 古代和中世纪时期 特点&#xff1a;商人提供贷款和存款服务&#xff0c;充当中间人转移资金&#xff0c;发行纸币作为支付…

SQL实战篇,数据库在Kooboo中的实际应用(一)

本文将结合实际操作与代码示例&#xff0c;展示SQL 在 Kooboo 中的实际应用 仅需两步&#xff1a;动态创建表 基础查询&#xff0c;无需复杂配置&#xff0c;快速上手&#xff01; 一、动态创建表&#xff1a;插入数据 Kooboo 支持多种数据库&#xff0c;以 SQLite 为例&…

克魔助手(Kemob)安装与注册完整教程 - Windows/macOS双平台指南

iOS设备管理工具克魔助手便携版使用全指南 前言&#xff1a;为什么需要专业的iOS管理工具 在iOS开发和设备管理过程中&#xff0c;开发者经常需要突破系统限制&#xff0c;实现更深层次的控制和调试。本文将详细介绍一款实用的便携式工具的使用方法&#xff0c;帮助开发者快速…

搜索插入位置 -- 二分查找

目录 一&#xff1a;题目 二:算法原理 三&#xff1a;代码分析 一&#xff1a;题目 题目链接&#xff1a;35. 搜索插入位置 - 力扣&#xff08;LeetCode&#xff09; 二:算法原理 三&#xff1a;代码分析 class Solution { public:int searchInsert(vector<int>&am…

Apache Doris内存与超时参数配置详解

一、查询任务内存限制调整 1. ​默认内存限制与问题定位 Apache Doris默认限制单个BE节点上的查询任务内存使用不超过2GB&#xff08;即exec_mem_limit2147483648字节&#xff09;。当复杂查询或大规模数据操作超过此限制时&#xff0c;会触发Memory limit exceeded错误。通过…

龙虎榜——20250411

今天缩量&#xff0c;上方压力依然在&#xff0c;外围还在升级&#xff0c;企稳还需要时日。 2025年4月11日龙虎榜行业方向分析 一、核心主线方向 半导体与芯片&#xff08;国产替代加速&#xff09; • 代表标的&#xff1a;圣邦股份&#xff08;模拟芯片&#xff09;、中电…

若依前后端分离版本从mysql切换到postgresql数据库

一、修改依赖&#xff1a; 修改admin模块pom.xml中的依赖,屏蔽或删除mysql依赖&#xff0c;增加postgresql依赖。 <!-- Mysql驱动包 --> <!--<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId> &l…

自定义排序注意点

这段 Java 代码展示了两种排序方式的用法&#xff0c;分别是&#xff1a; 对普通数组进行排序&#xff08;Integer[] nums&#xff09;对对象数组进行排序&#xff08;Student[] students&#xff09; 我来一步步给你讲清楚&#xff1a; ✅ 第1部分&#xff1a;普通数组降序排…

第十四届蓝桥杯大赛软件赛省赛C/C++ 大学 A 组真题

文章目录 1 幸运数题目描述&#xff1a;答案&#xff1a;4430091 代码&#xff1a; 2 有奖问答题目描述&#xff1a;重点&#xff1a;答案&#xff1a;8335366 代码&#xff1a; 3 平方差题目描述&#xff1a;思路&#xff1a;数学找规律代码&#xff1a; 4 更小的数题目描述&a…

C++ 入门四:类与对象 —— 面向对象编程的核心基石

一、类的定义 1. 类的基本形式 class 类名 { public: // 公有成员&#xff08;类内外均可访问&#xff09;数据类型 数据成员; // 公有数据成员数据类型 成员函数(参数列表); // 公有成员函数声明 protected: // 保护成员&#xff08;类内和派生类可访问&…

嵌入式---电机分类

一、按电流类型分类&#xff08;最基础分类&#xff09; 1. 直流电机&#xff08;DC Motor&#xff09; 工作原理&#xff1a;通过换向器&#xff08;有刷&#xff09;或电子换向&#xff08;无刷&#xff09;将直流电源转换为交变磁场&#xff0c;驱动转子旋转。 核心特点&a…

【python】并行编程模块:threading / mutliprocess / parallel / Celery

在并行编程中&#xff0c;Python 具有简化实现的内置和外部模块。 本书是基于Python3.X的。 Python的threading模块 Python的threading模块为模块 _thread 提供了一个抽象层&#xff0c;它是一个较低级别的模块。 它提供的功能可以帮助程序员完成基于线程开发并行系统的艰巨任…

OpengGL教程(七)---摄像机

本章参考官方教程&#xff1a;摄像机 本系列历史文 OpengGL教程(一)—OpenGL环境的配置(GLFW3,GLAD) OpengGL教程(二)—渲染一个简单的窗体 OpengGL教程(三)—使用VAO和VBO方式绘制三角形 OpengGL教程(四)—使用EBO方式绘制矩形 OpengGL教程(五)—纹理的应用 OpengGL教程(六)—…

安卓手机怎样开启双WiFi加速

1. 小米/Redmi手机 路径&#xff1a; 设置 → WLAN → 高级设置 → 双WLAN加速 操作&#xff1a; 开启功能后&#xff0c;可同时连接一个2.4GHz WiFi和一个5GHz WiFi&#xff08;或两个不同路由器&#xff09;。 可选择“智能选择”或手动指定辅助网络。 2. 华为/荣耀手机…

什么是八步工作法?

八步工作法&#xff0c;顾名思义&#xff0c;就是把一项工作拆分成八个步骤来完成。它的核心目的是让工作变得更有条理&#xff0c;更高效&#xff0c;避免忙而无序&#xff0c;做到事事有着落&#xff0c;件件有结果。这个方法在很多企业和单位中都有应用&#xff0c;尤其适合…

前端Node.js的包管理工具npm指令

‌npm&#xff08;Node Package Manager&#xff09;是Node.js的包管理工具&#xff0c;主要用于安装、更新、删除和管理JavaScript包。以下是前端开发中常用的npm命令及其用途‌&#xff1a; 基本命令 npm提供了一系列命令行工具&#xff0c;用于执行各种包管理操作。以下是一…

掌握C语言文件操作:从理论到实战指南

文件操作是C语言编程中不可或缺的一部分&#xff0c;它使得程序能够持久化存储数据&#xff0c;并在需要时高效读写。本文将从基础概念到实战技巧&#xff0c;系统讲解C语言文件操作的核心知识点&#xff0c;并结合代码示例帮助读者深入理解。 一. 为什么需要文件操作&#xf…