最近因为要和java进行通信、约定好使用http协议进行消息传递。在网上找了很久server编写发现有个博主写的很详细,因此把东西记录下来以便下次使用。这是原博主网址:https://blog.csdn.net/h593245631/article/details/94033451

1、httpserver创建

1)httpserver.cpp

#include "httpserver.h"
#include <thread>
#include <regex> #include "utils.h"
#include "TransactionApi.h" 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);
//thread sth(&HttpServer::clientThreadFun, this);
sth.detach();
}
}
} DWORD WINAPI HttpServer::clientThreadFun(LPVOID lpThreadParameter) {
//5. 与客户端通讯 发送或者接收数据
SOCKET c = (SOCKET)lpThreadParameter; //循环接收客户端数据
int ret = 0;
int cun =0;
string connect;
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 respStr = getResponse(to_string(cun));
string respStr = getResponse("testok");
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" 登录
FunName =mas[2];
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 += connect;
rmsg += "\r\n";
rmsg += "\r\n\r\n";
rmsg += connect;
return rmsg;
}

2)httpserver.h

#pragma once
#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();
string FunName;
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); };

3)utils.cpp

#include "utils.h"

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));
}

4)utils.h

#include <vector>
#include <string>
#include <iostream> using namespace std; void splitStr(const string& s, vector<string>& v, const string& c);

5)main

#include "httpserver.h"
#include <WinSock.h>
#include <iostream>
#include <Windows.h> int main(int argc, char* argv[]) { unsigned short port = 8888;
HttpServer server;
server.start(port); getchar();
}

本次程序是使用vs2019进行编译运行,配置的ip是127.0.0.1端口8888。

2、httpclient创建

1)httpclient.cpp

#include "httpclient.h"
#include <WinSock.h>
#include <iostream>
#include <Windows.h> #pragma comment(lib, "ws2_32.lib") HttpRequest::HttpRequest(const std::string& ip, int port) : m_ip(ip), m_port(port)
{
} HttpRequest::~HttpRequest(void)
{
} // Http GET请求
std::string HttpRequest::HttpGet(std::string req)
{
std::string ret = ""; // 返回Http Response
try
{
// 开始进行socket初始化
WSADATA wData;
::WSAStartup(MAKEWORD(2, 2), &wData); SOCKET clientSocket = socket(AF_INET, 1, 0);
struct sockaddr_in ServerAddr = {0};
ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
ServerAddr.sin_port = htons(8892);
ServerAddr.sin_family = AF_INET;
int errNo = connect(clientSocket, (sockaddr*)&ServerAddr, sizeof(ServerAddr));
if(errNo == 0)
{
// "GET /[req] HTTP/1.1\r\n"
// "Connection:Keep-Alive\r\n"
// "Accept-Encoding:gzip, deflate\r\n"
// "Accept-Language:zh-CN,en,*\r\n"
// "User-Agent:Mozilla/5.0\r\n\r\n";
std::string strSend = " HTTP/1.1\r\n"
"Cookie:16888\r\n\r\n";
strSend = "GET " + req + strSend; // 发送
errNo = send(clientSocket, strSend.c_str(), strSend.length(), 0);
if(errNo > 0)
{
//cout << "发送成功" << endl;
}
else
{
std::cout << "errNo:" << errNo << std::endl;
return ret;
} // 接收
char bufRecv[3069] = {0};
errNo = recv(clientSocket, bufRecv, 3069, 0);
if(errNo > 0)
{
ret = bufRecv;// 如果接收成功,则返回接收的数据内容
}
else
{
std::cout << "errNo:" << errNo << std::endl;
return ret;
}
}
else
{
errNo = WSAGetLastError();
std::cout << "errNo:" << errNo << std::endl;
}
// socket环境清理
::WSACleanup();
}
catch (...)
{
return "";
}
return ret;
} // Http POST请求
std::string HttpRequest::HttpPost(std::string req, std::string data)
{
std::string ret = ""; // 返回Http Response
try
{
// 开始进行socket初始化;
WSADATA wData;
::WSAStartup(MAKEWORD(2, 2), &wData); SOCKET clientSocket = socket(AF_INET, 1, 0);
struct sockaddr_in ServerAddr = {0};
ServerAddr.sin_addr.s_addr = inet_addr(m_ip.c_str());
ServerAddr.sin_port = htons(m_port);
ServerAddr.sin_family = AF_INET;
int errNo = connect(clientSocket, (sockaddr*)&ServerAddr, sizeof(ServerAddr));
if(errNo == 0)
{
// 格式化data长度
char len[10] = {0};
sprintf(len, "%d", data.length());
std::string strLen = len; // "POST /[req] HTTP/1.1\r\n"
// "Connection:Keep-Alive\r\n"
// "Accept-Encoding:gzip, deflate\r\n"
// "Accept-Language:zh-CN,en,*\r\n"
// "Content-Length:[len]\r\n"
// "Content-Type:application/x-www-form-urlencoded; charset=UTF-8\r\n"
// "User-Agent:Mozilla/5.0\r\n\r\n"
// "[data]\r\n\r\n";
std::string strSend = " HTTP/1.1\r\n"
"Cookie:16888\r\n"
"Content-Type:application/x-www-form-urlencoded\r\n"
"Charset:utf-8\r\n"
"Content-Length:";
strSend = "POST " + req + strSend + strLen + "\r\n\r\n" + data; // 发送
errNo = send(clientSocket, strSend.c_str(), strSend.length(), 0);
if(errNo > 0)
{
//cout<<"发送成功\n";
}
else
{
std::cout << "errNo:" << errNo << std::endl;
return ret;
} // 接收
char bufRecv[3069] = {0};
errNo = recv(clientSocket, bufRecv, 3069, 0);
if(errNo > 0)
{
ret = bufRecv;// 如果接收成功,则返回接收的数据内容
}
else
{
std::cout << "errNo:" << errNo << std::endl;
return ret;
}
}
else
{
errNo = WSAGetLastError();
}
// socket环境清理
::WSACleanup();
}
catch (...)
{
return "";
}
return ret;
} // 合成JSON字符串
std::string HttpRequest::genJsonString(std::string key, int value)
{
char buf[128] = {0};
sprintf(buf, "{\"%s\":%d}", key.c_str(), value);
std::string ret = buf;
return ret;
} // 分割字符串
std::vector<std::string> HttpRequest::split(const std::string &s, const std::string &seperator)
{
std::vector<std::string> result;
typedef std::string::size_type string_size;
string_size i = 0; while(i != s.size()){
// 找到字符串中首个不等于分隔符的字母
int flag = 0;
while(i != s.size() && flag == 0){
flag = 1;
for(string_size x = 0; x < seperator.size(); ++x)
if(s[i] == seperator[x]){
++i;
flag = 0;
break;
}
} // 找到又一个分隔符,将两个分隔符之间的字符串取出
flag = 0;
string_size j = i;
while(j != s.size() && flag == 0){
for(string_size x = 0; x < seperator.size(); ++x)
if(s[j] == seperator[x]){
flag = 1;
break;
}
if(flag == 0)
++j;
}
if(i != j){
result.push_back(s.substr(i, j-i));
i = j;
}
}
return result;
} // 从Response中查找key对应的Header的内容
std::string HttpRequest::getHeader(std::string respose, std::string key)
{
std::vector<std::string> lines = split(respose, "\r\n");
for (int i = 0; i < lines.size(); i++)
{
std::vector<std::string> line = split(lines[i], ": ");// 注意空格
if (line.size() >= 2 && line[0] == key)
{
return line[1];
}
}
return "";
}

2)httpclient.h

#pragma once
#include <string>
#include <vector> class HttpRequest
{
public:
HttpRequest(const std::string& ip, int port);
~HttpRequest(void); // Http GET请求
std::string HttpGet(std::string req); // Http POST请求
std::string HttpPost(std::string req, std::string data); // 合成JSON字符串
static std::string genJsonString(std::string key, int value); // 分割字符串
static std::vector<std::string> split(const std::string &s, const std::string &seperator); // 根据key从Response获取Header中的内容
static std::string getHeader(std::string respose, std::string key); private:
std::string m_ip = "127.0.0.1";
int m_port=8892;
};

当ip127.0.0.1port8892服务器起好时将可以连接到服务器进行消息发送和接受应答结果。

创建一个httpserver、httpclient的更多相关文章

  1. 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用

    由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...

  2. C#中自己动手创建一个Web Server(非Socket实现)

    目录 介绍 Web Server在Web架构系统中的作用 Web Server与Web网站程序的交互 HTTPListener与Socket两种方式的差异 附带Demo源码概述 Demo效果截图 总结 ...

  3. ASP.NET Core中如何针对一个使用HttpClient对象的类编写单元测试

    原文地址: How to unit test a class that consumes an HttpClient with IHttpClientFactory in ASP.NET Core? ...

  4. SpringCloud学习6-如何创建一个服务消费者consumer

    上一节如何创建一个服务提供者provider已经启动了一个provider的server,提供用户信息查询接口.接下来,我们启动另一个provider,由于是同一台机器本地测试,我们换一个端口 --s ...

  5. 创建多线程的HttpClient

    在实际的应用中,我们的联网应用程序里应该有一个HttpClient,并将其用于所有的HTTP通信.这就可能在同一个Http Client同时发出多个请求,也就产生了多线程的问题.幸运的是,在HttpC ...

  6. 简单创建一个SpringCloud2021.0.3项目(四)

    目录 1. 项目说明 1. 版本 2. 用到组件 3. 功能 2. 上三篇教程 3. 日志处理 1. 创建日志公共模块 2. Eureka引入日志模块 4. 到此的功能代码 5. 注册中心换成naco ...

  7. 简单创建一个SpringCloud2021.0.3项目(三)

    目录 1. 项目说明 1. 版本 2. 用到组件 3. 功能 2. 上俩篇教程 3. Gateway集成sentinel,网关层做熔断降级 1. 超时熔断降级 2. 异常熔断 3. 集成sentine ...

  8. 用html5的canvas和JavaScript创建一个绘图程序

    本文将引导你使用canvas和JavaScript创建一个简单的绘图程序. 创建canvas元素 首先准备容器Canvas元素,接下来所有的事情都会在JavaScript里面. <canvas ...

  9. 搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 (1)

    搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 原文地址(英文):http://www.networkcomms.net/creating ...

  10. ASP.NET Core管道深度剖析(2):创建一个“迷你版”的管道来模拟真实管道请求处理流程

    从<ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求>我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但 ...

随机推荐

  1. 《Terraform 101 从入门到实践》 第五章 HCL语法

    <Terraform 101 从入门到实践>这本小册在南瓜慢说官方网站和GitHub两个地方同步更新,书中的示例代码也是放在GitHub上,方便大家参考查看. 介绍了Terraform一些 ...

  2. Nginx01 简介和安装

    1 简介 Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务.Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler ...

  3. C#支付宝用户的静默授权

    支付宝官方的用户信息授权,具体地址:https://opendocs.alipay.com/open/289/105656 商户/开发者通过以下的 URL 拼接规则拼接用户授权的 URL 地址,该地址 ...

  4. P20_事件绑定

    事件绑定 什么是事件 事件是渲染层到逻辑层的通讯方式.通过事件可以将用户在渲染层产生的行为,反馈到逻辑层进行业务的处理. 小程序中常用的事件 事件对象的属性列表 当事件回调触发的时候,会收到一个事件对 ...

  5. .net core 删除指定路径下的所有文件以及文件夹(文件夹建议保留目录)

    1.服务层 /// <summary> /// 删除指定路径下的所有文件 /// </summary> /// <param name="filepath&qu ...

  6. CSS常用属性(3)

    (9) list-style(列表样式) 类型 list-style-type: disc | circle | square | decimal | lower-roman | upper-roma ...

  7. steamdeck使用SSH远程控制

    为了方便去折腾steamdeck,我想用Windows11远程控制steam deck,我开始在网上查找Linux远程控制的方法,决定使用SSH来达到目的. 首先开始查看我的steamdeck的ip地 ...

  8. 原生微信小程序封装request

    request文件 // 封装请求 const baseURL = 'https://api-hmugo-web.itheima.net/api/public/v1' const request = ...

  9. 通过Sql Server 作业实现定时任务

    最近需要一个业务需求.一条数据的状态在指定时间更改状态并且要在另一张表中添加条数据.要实现这个需求有两种方式:一种方式是使用Windows服务来实现,另一种是通过Sql Server作业的方式来实现. ...

  10. MSF后渗透常用命令

    一.在meterpreter命令行下常用的操作 ps # 查看当前活跃进程 migrate pid # 将Meterpreter会话移植到进程数位pid的进程中 execute -H -i -f cm ...