C++ 通过TCP Socket实现简单Http服务器

2023-05-16

实现一个简单的Http服务器,基于windows 平台。 总共五个文件 : HttpServer.hpp、HttpServer.cpp、Utils.hpp、Utils.cpp、main.cpp

Utils.hpp

#include <vector>
#include <string>
#include <iostream>

using namespace std;

void splitStr(const string& s, vector<string>& v, const string& c);

Utils.cpp


#include "Utils.hpp"


void splitStr(const string& s, vector<string>& v, const string& c) {
	string::size_type pos1, pos2;
	pos2 = s.find(c);
	pos1 = 0;
	while(string::npos != pos2) {
		v.push_back(s.substr(pos1, pos2 - pos1));

		pos1 = pos2 + c.size();
		pos2 = s.find(c, pos1);
	}
	if(pos1 != s.length())
		v.push_back(s.substr(pos1));
}

HttpServer.hpp

#pragma once

#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>

using namespace std;

#pragma  comment(lib, "Ws2_32.lib")

class HttpServer {
public:
	HttpServer();
	~HttpServer();

	bool start(unsigned short port);
	void run();
	void stop();

private:
	bool init();
	//线程函数 处理客户端来的请求
	DWORD WINAPI clientThreadFun(LPVOID lpThreadParameter);

	bool _isExit = false;
	unsigned short _port = 8892;					//端口号
	SOCKET _listenSocket = INVALID_SOCKET;		//监听套接字

	//处理http消息
	bool getRequest(string str);
	std::string getResponse(string connectLen);

};

HttpServer.cpp



#include "HttpServer.hpp"
#include <thread>
#include <regex>

#include "Utils.hpp"

using namespace std;

HttpServer::HttpServer() {
	//初始化winsock2.2相关的动态库
	WSADATA wd; // 获取socket相关信息
	if(0 != WSAStartup(MAKEWORD(2, 2), &wd)) { //0 表示成功
		cout << "WSAStartup error: " << WSAGetLastError() << endl;
		return;
	}
	cout << "WSAStartup success: " << endl;
}

HttpServer::~HttpServer() {
	//清理winsock2的环境
	WSACleanup();
}

bool HttpServer::start(unsigned short port) {
	_isExit = false;
	_port = port;
	if(!init()) {
		cout << "httpserver start error" << endl;
	}

	thread sth(&HttpServer::run, this);
	sth.detach();

	cout << "httpserver start success" << endl;
	return true;
}

void HttpServer::run() {
	//主线程循环接收客户端的链接
	while(!_isExit) {
		sockaddr_in addrClient;
		int len = sizeof(sockaddr_in);

		//4. 接收成功返回与client通讯的socket
		SOCKET c = accept(_listenSocket, (SOCKADDR*)&addrClient, &len);
		if(INVALID_SOCKET != c) {
			//创建线程 并且传入与client通讯的套接字
			thread sth(&HttpServer::clientThreadFun, this, (LPVOID)c);
			sth.detach();
		}
	}
}

DWORD WINAPI HttpServer::clientThreadFun(LPVOID lpThreadParameter) {
	//5. 与客户端通讯 发送或者接收数据
	SOCKET c = (SOCKET)lpThreadParameter;

	//循环接收客户端数据
	int ret = 0;
	do {
		char buf[1024 * 2] = { 0 };

		ret = recv(c, buf, sizeof(buf)-1, 0);

		if(ret <= 0) {
			break;
		}
		buf[ret] = '\0';
		cout << "客户端" << c << "请求信息 : " << buf << endl;

		if(!getRequest(buf)) {
			break;
		}
		string connect = "124443656556677778cxgyushining";
		string respStr = getResponse(connect);
		ret = send(c, respStr.c_str(), respStr.size(), 0);
		cout << "客户端" << c << "请求应答信息 : " << respStr << endl;
		break;

	} while(ret != SOCKET_ERROR && ret != 0);

	closesocket(c);
	return 0;
}


bool HttpServer::init() {

	//1. 创建TCP socket 流式套接字
	_listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_HOPOPTS);
	if(INVALID_SOCKET == _listenSocket) {
		cout << "socket error :" << WSAGetLastError() << endl;
		return false;
	}

	//2. 绑定socket到一个IP地址和端口
	sockaddr_in addr; //不建议使用sockaddr 建议用sockaddr_in
	addr.sin_family = AF_INET; // 地址族
	addr.sin_port = htons(_port);//本地端口 转网络字节序
	inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);//ip地址转网络字节序

	int len = sizeof(sockaddr_in);
	if(SOCKET_ERROR == ::bind(_listenSocket, (sockaddr*)&addr, len)) {
		cout << "bind error: " << WSAGetLastError() << endl;
		return false;
	}

	//3. 监听, 5 代表正在等待完成相应的TCP三次握手过程的队列长度
	if(SOCKET_ERROR == listen(_listenSocket, 1000)) { // 根据电脑配置设定链接数
		cout << "listen error:" << WSAGetLastError() << endl;
		return false;
	}

	cout << "create listen success :" << endl;
	cout << "init htttpserver success :" << endl;
	return true;
}

void HttpServer::stop() {
	_isExit = true;
	//关闭监听套接字
	closesocket(_listenSocket);
}


//----

bool HttpServer::getRequest(string requestStr) {
	string src = requestStr;

	string pattern = "^([A-Z]+) /([a-zA-Z0-9]*([.][a-zA-Z]*)?)[?]?(.*) HTTP/1"; //
	regex r(pattern);
	smatch mas;
	regex_search(src, mas, r);
	if(mas.size() == 0) {
		cout << pattern.c_str() << " failed!" <<endl;
		return false;
	}
	string type = mas[1];
	string path = "/";
	path += mas[2];  //根据path 区别请求的命令 比如 "/login" 登录   
	string filetype = mas[3];
	string queryStr = mas[4];

	if(type != "GET" && type != "POST") {
		cout << "Not GET and POST!\n" << endl;
		return false;
	}

	//query id=1&name=xcj
	vector<string> querys;
	splitStr(queryStr, querys, "&");

	return true;
}

std::string HttpServer::getResponse(string connect) {
	//回应http GET请求
	//消息头
	string rmsg = "";
	rmsg = "HTTP/1.1 200 OK\r\n";
	rmsg += "Server: xHttp\r\n";
	rmsg += "Content-Type: text/html;charset=utf-8\r\n";
	rmsg += "Content-Length: ";
	rmsg += to_string(connect.size());
	rmsg += "\r\n";
	rmsg += "\r\n\r\n";
	rmsg += connect;
	return rmsg;
}



main.cpp


#include "HttpServer.hpp"

//客户端请求格式: string url = "http://localhost:8892/query?type=shunfeng&postid=367847964498";

int main(int argc, char* argv[]) {
	
	unsigned short port = 8892;
	HttpServer server;
	server.start(port);

	getchar();
}


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 通过TCP Socket实现简单Http服务器 的相关文章

  • 2G、3G、4G网络制式上下行频率、速率比较

    本文介绍第二代 xff08 2G xff09 移动通信系统和第三代 xff08 3G xff09 移动通信系统常见制式基础知识 xff0c 主要有2G xff08 GSM900 EGSM DCS1800 PCS1800 xff09 3G x
  • 23种设计模式UML图

  • lvs负载均衡基础知识梳理

    https www cnblogs com kevingrace p 6137975 html
  • 微内核和宏内核

    https zhuanlan zhihu com p 53612117
  • ARMv7-A/R/M系列 --- 简介

    1 简介在ARM11之后的处理器家族 xff0c 改采Cortex命名 xff0c 并针对高 中 低阶分别划分为A R M三大处理器 像是高阶手机用的Coretex A系列 xff0c 或者是微控制器所使用的Coretex M系列 xff0
  • ubuntu-C++Demo处理数据的一些小知识点

  • enum类型变量的使用和赋值

    enum 是枚举型 union 是共用体 xff0c 成员共用一个变量缓冲区 现在基本已经不用了 枚举类型 在实际问题中 xff0c 有些变量的取值被限定在一个有限的范围内 例如 xff0c 一个星期内只有七天 xff0c 一年只有十二个月
  • 单总线和多总线的区别

    在计算机系统的硬件组成中 xff0c 总线 xff08 Bus xff09 是不可缺少的一部分 xff0c 将各大基本部件按照一定的方式链接起来就构成了计算机硬件系统 就目前来讲 xff0c 许多微型计算机的各大部件之间都是用总线链接起来的
  • Win7系统下怎么扩大C盘容量 合并磁盘分

    C磁盘空间总是不够用 有没有想过把磁盘扩大点呢 下面小编就教你个办法 xff0c 把其他盘的空间转换成C盘的容量 xff0c 这要怎么弄呢 其实挺简单的 方法如下 xff1a 1 首先从电脑桌面左下脚开始里面进去找到 计算机 找到计算机名称
  • 操作系统与应用程序的关系

    操作系统与应用程序的关系 操作系统主要可以分为两大部分 xff1a 内核和内核之外的一些程序 内核就是直接控制最底层的硬件 xff0c 而我们日常所用到的软件 xff0c 大都是通过内核之外一些程序与内核之间的接口完成的 xff0c 例如W
  • GSM系统构成(NSS,OSS,BSS,MS)

    第一部分NSS EIR HLR AUC MSC VLR EIR Equipment identify register装备身份注册 HLR Home Location Register 归属位置寄存器 AUC Authentication
  • DSP指数编码器(EXP)工作原理

    例 xff1a 完成对累加器A的归一化处理 EXP A 多余符号位数 8 T寄存器 ST T EXPONENT 将保存在T寄存器中的指数存入指定的数据存储器中 NORM A 对累加器A进行归一化处理 40位累加器A中的定点数FF FFFF
  • VS C++调用Lua动态链接库

    这里使用的是VS2019 xff0c 其他版本类似 新建一个控制台应用 创建好工程后 xff0c 打开工程属性页 进入C C 43 43 gt 常规 gt 附加包含目录 xff0c 将lua的头文件目录包含进去 进入链接器 gt 输入 gt
  • Unity 如何判断GameObject是否为Prefab

    public static bool IsPrefabInstance UnityEngine GameObject obj var type 61 PrefabUtility GetPrefabAssetType obj var stat
  • Git 常用命令

    注册用户名和邮箱 xff1a git config global user name username git config global user email useremail 查看log的快捷定义 xff1a 在C Users 当前用
  • C++值赋值运算符重载

    C 43 43 值赋值运算符重载的格式是这样的 xff1a 类名 amp span class hljs keyword operator span xff08 span class hljs keyword const span 类名 a
  • 学习笔记:GDB 调试

    01 什么是GDB GDB 是由 GNU 软件系统社区提供的调试工具 xff0c 同 GCC 配套组成了一套完整的开发环境 xff0c GDB 是 Linux 和许多类 Unix 系统中的标准开发环境 一般来说 xff0c GDB 主要帮助
  • C++之类对象的返回与引用

    一 类对象的返回 在拷贝构造器中提到过 xff1a class span class hljs literal A span span class hljs comment span span class hljs literal A sp
  • 字符串的操作

    一 字符串的初始化 1 定长字符数组 1 gt span class hljs keyword char span buf1 span class hljs number 128 span 61 span class hljs string
  • 哈希表的大小为什么最好是素数

    在看数据结构和算法分析这本书的时候 xff0c 哈希表建议大小为素数 xff0c 但里面并没有详细说明为什么 xff0c 只说了因为它在哈希表最小化集群 xff0c 这又是为什么 xff0c 我通过百度 xff0c 外加自己的理解说明一下为

随机推荐