创建一个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服务器起好时将可以连接到服务器进行消息发送和接受应答结果。