创建一个httpserver、httpclient
最近因为要和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的更多相关文章
- 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用
由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...
- C#中自己动手创建一个Web Server(非Socket实现)
目录 介绍 Web Server在Web架构系统中的作用 Web Server与Web网站程序的交互 HTTPListener与Socket两种方式的差异 附带Demo源码概述 Demo效果截图 总结 ...
- ASP.NET Core中如何针对一个使用HttpClient对象的类编写单元测试
原文地址: How to unit test a class that consumes an HttpClient with IHttpClientFactory in ASP.NET Core? ...
- SpringCloud学习6-如何创建一个服务消费者consumer
上一节如何创建一个服务提供者provider已经启动了一个provider的server,提供用户信息查询接口.接下来,我们启动另一个provider,由于是同一台机器本地测试,我们换一个端口 --s ...
- 创建多线程的HttpClient
在实际的应用中,我们的联网应用程序里应该有一个HttpClient,并将其用于所有的HTTP通信.这就可能在同一个Http Client同时发出多个请求,也就产生了多线程的问题.幸运的是,在HttpC ...
- 简单创建一个SpringCloud2021.0.3项目(四)
目录 1. 项目说明 1. 版本 2. 用到组件 3. 功能 2. 上三篇教程 3. 日志处理 1. 创建日志公共模块 2. Eureka引入日志模块 4. 到此的功能代码 5. 注册中心换成naco ...
- 简单创建一个SpringCloud2021.0.3项目(三)
目录 1. 项目说明 1. 版本 2. 用到组件 3. 功能 2. 上俩篇教程 3. Gateway集成sentinel,网关层做熔断降级 1. 超时熔断降级 2. 异常熔断 3. 集成sentine ...
- 用html5的canvas和JavaScript创建一个绘图程序
本文将引导你使用canvas和JavaScript创建一个简单的绘图程序. 创建canvas元素 首先准备容器Canvas元素,接下来所有的事情都会在JavaScript里面. <canvas ...
- 搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 (1)
搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 原文地址(英文):http://www.networkcomms.net/creating ...
- ASP.NET Core管道深度剖析(2):创建一个“迷你版”的管道来模拟真实管道请求处理流程
从<ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求>我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但 ...
随机推荐
- Pytest插件pytest-order指定用例顺序
Pytest插件pytest-order指定用例顺序 安装 pip install pytest-order 注意不是pytest-ordering 说起来这里有个故事 关于pytest-order ...
- 编写antd的Cascader 级联选择组件市级地区数据
下面是该组件的使用数据的格式 options: [ { value: 'zhejiang', label: 'Zhejiang', children: [ { value: 'hangzhou', l ...
- Redis-05持久化
1 Redis持久化 RDB(Redis DataBase) AOF(Append Only File) 2 RBD 2.1 基本说明 在指定的时间间隔内将内存中的数据集快照写入磁盘文件,它恢复时将快 ...
- 本地python环境安装kylin项目依赖时报认证错误信息
问题描述:项目需要连接kylin数据库查询数据,本地安装kylin项目的依赖环境后报认证错误 python版本3.7 32位 pycharm版本 2022版 成功方法: 更换解释器选择无解释器,创建虚 ...
- 2211-11Flask入门教程
本篇记录来自Flask入门教程 准备工作 在通过这本书学习 Flask 开发前,我假设你已经了解了 Python 和 HTML 的基础知识.如果还没有,那么可以先从下面这些在线资源入手: <使用 ...
- Vue框架:6、Vue组件间通信,动态组件,插槽,计算属性,监听属性
目录 前端开发之Vue框架 一.Vue组件间通信 1.组件间通讯父传子 2.组件间通讯子传父 3.ref属性 二.动态组件 1.不使用动态组件 2.使用动态组件 3.keep-alive保持组件不销毁 ...
- 1.初识 Django
设计模式 定义 # mysite/news/models.py from django.db import models class Reporter(models.Model): full_name ...
- 易语言 CS1.6单机开源
一个绘制 一个修改器 垃圾玩意 https://kxd.lanzoul.com/iJiwf07ve61a https://kxd.lanzoul.com/iTT4n07w61tg
- 集合类再探:不可变类的好处,Collector接口详解,使用内部迭代
集合类再探 注:本文使用的pom依赖见文末. 集合类的基础 - Iterable.Iterator java语言层面支持对实现了Iterable接口的对象使用for-each语句.Iterator可以 ...
- python爬取网页的多种方式以及保存方法
爬取网页信息并保存 bs4和lxml都是用来将接收的数据解析html 1.bs4+excel(openpyxl): import requests from bs4 import BeautifulS ...