首先需要一个差不多的String
我实际上是不喜欢c++的,写起来不仅心智负担太重,还特别简陋。听说c++20马上要出来了,虽然我连c++11都不怎么会,17更不用说,但我估计c++入门书籍快2000页了,估计依旧是标准库里没有网络库,也没有库管理工具,我永远喜欢c#及Nuget.
stl中的string过于简陋,写习惯c#过来感觉就是简直不能用,不过性能还行。为了容易移到其他项目上,oop的封装,solid的开闭原则,继承string自己实现MyString 是最好的选择,后面可能随时修改
项目代码仓库 GitHub
class MyString:public string
{
public:
MyString()=default;
~MyString()=default;
template<class T>
MyString(T&& arg) :string(forward<T>(arg))
{
}
vector<MyString> Split(MyString flag,bool SkipEmpty=true)
{
vector<MyString> return_data;
int pos = find(flag);
bool FirstLine = true;
while (pos!=-1)
{
MyString line;
auto PushData=[&return_data,SkipEmpty](MyString line){
if (line.length() != 0 || !SkipEmpty)
{
return_data.push_back(line);
}
};
if (FirstLine)
{
line = substr(0, pos);
PushData(line);
FirstLine = false;
}
else
{
int right = find(flag, pos+1);
int left = pos + flag.length();
if (right==-1)
{
line = substr(left);
PushData(line);
break;
}
line = substr(left, right - left);
PushData(line);
pos = right;
}
}
return return_data;
}
vector<MyString> Split(char flag='\n')
{
vector<MyString> return_data;
istringstream data;
data.str(data());
string temp;
while (getline(data,temp,flag))
{
return_data.push_back(temp);
}
return return_data;
}
MyString Trim(char target=' ')
{
int left=find_first_not_of(target);
int right = find_last_not_of(target);
return substr(left, right-left+1);
}
private:
};
socket,http是什么东西?这个直接看其他人的,
首先http是基于tcp
图来自其他人,侵删
使用socket来进行Http请求数据需要经过
获取一个套接字
SOCKET GetSocket(int port, bool IsServer = false)
{
//初始化。,WSA windows异步套接字
WSADATA inet_WsaData;//
WSAStartup(MAKEWORD(2, 0), &inet_WsaData);//socket2.0版本
if (LOBYTE(inet_WsaData.wVersion) != 2 || HIBYTE(inet_WsaData.wVersion) != 0)
{//高位字节指明副版本、低位字节指明主版本
WSACleanup();
return -1;
}
auto tcp_socket = socket(AF_INET,SOCK_STREAM, 0);//ipv4,tcp,tcp或udp该参数可为0
if (!IsServer)
{
return tcp_socket;
}//服务器才需要干下面这些
sockaddr_in saddr;
saddr.sin_family = AF_INET;//ipv4 udp tcp 等
saddr.sin_port = htons(port); //端口;
saddr.sin_addr.s_addr = INADDR_ANY;//是监听地址
if (::bind(tcp_socket, (sockaddr*)&saddr, sizeof(saddr)) == -1)
{//因为using namespace std,所以要使用::指定全局函数,而非std::bind
throw exception("bind error");
}
if (::listen(tcp_socket, 20) == -1)//监听的套接字句柄,指定监听的最大连接数量
{//listen函数将socket变为被动类型的,等待客户的连接请求。
throw exception("listen error");
}
return tcp_socket;
}
使用Connect进行连接
in_addr sa;
MyString ServerAddr = "127.0.0.1";//我开了 .net core web server在本地测试,你们自行替换
if (InetPton(AF_INET,ServerAddr.c_str(),&sa)==-1)
{
throw new exception("地址转换错误");
}
sockaddr_in saddr;
saddr.sin_family = AF_INET;//ipv4
saddr.sin_port = htons(5000);//端口
saddr.sin_addr=sa;
if (::connect(Socket, (sockaddr*)&saddr, sizeof(saddr))!=0)
{
MyString Msg = "连接失败错误码:" + WSAGetLastError();
throw new exception(Msg.c_str());
}
使用send发送请求
MyHttp::Request a;//myhttp在最下面放
a.SetHttpMethod(MyHttp::MethodEnum::Get);
a.Path = "/api/auth/zanllp";
a.Header["Host"] = "127.0.0.1:5000";
a.Header["Accept-Encoding"]="gzip, deflate, br";
a.Header["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8";
a.Header["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36 OPR/58.0.3135.79";
MyString SendBuf = a.SendRequest();
cout << SendBuf << endl;
send(Socket, SendBuf.c_str(), SendBuf.length(), 0);
使用recv读取响应
char buf[1024] = { '\0' };
recv(Socket, buf, sizeof(buf), 0);//1
MyHttp::Response da(buf);
cout << da.ParseFromSource()<< endl;
结果
基本可以就行简单的请求了
不知道ip怎么办?
MyString GetIpByHostname(MyString hostname)
{
addrinfo hints, *res;
in_addr addr;
int err;
memset(&hints, 0, sizeof(addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_INET;//ipv4
if ((err = getaddrinfo(hostname.c_str(), NULL, &hints, &res)) != 0) {
MyString Msg = "错误" + err + MyString(gai_strerror(err));
throw exception(Msg.c_str());
}
addr.s_addr = ((sockaddr_in*)(res->ai_addr))->sin_addr.s_addr;
char str[INET_ADDRSTRLEN];
auto ptr = inet_ntop(AF_INET, &addr, str, sizeof(str));
freeaddrinfo(res);
return str;
}
//在connetcion时修改如下
MyString ServerAddr = MyHttp::GetIpByHostname("www.baidu.com");
if (InetPton(AF_INET,ServerAddr.c_str(),&sa)==-1)
{
throw new exception("地址转换错误");
}
sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(80);
saddr.sin_addr=sa;
赶赶单单
目前的MyHttp进度
#include <winsock2.h>
#include <Windows.h>
#include <stdio.h>
#include <iostream>
#include <thread>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include<WS2tcpip.h>
#include<map>
namespace MyHttp
{
MyString GetIpByHostname(MyString hostname)
{
addrinfo hints, *res;
in_addr addr;
int err;
memset(&hints, 0, sizeof(addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_INET;//ipv4
if ((err = getaddrinfo(hostname.c_str(), NULL, &hints, &res)) != 0) {
MyString Msg = "错误" + err + MyString(gai_strerror(err));
throw exception(Msg.c_str());
}
addr.s_addr = ((sockaddr_in*)(res->ai_addr))->sin_addr.s_addr;
char str[INET_ADDRSTRLEN];
auto ptr = inet_ntop(AF_INET, &addr, str, sizeof(str));
freeaddrinfo(res);
return str;
}
enum MethodEnum
{
Get,
Post,
Put,
Delete
};
class Response
{
public:
MyString Source;
MyString ProtocolVersion = "HTTP/1.1";
MyString Code = "200";
MyString Status = "ok";
MyString Server = "CppTinyServer";
map<MyString, MyString> Header;
MyString ResponseBody;
~Response() = default;
Response() = default;
template<class T>
Response(T&& arg)
{
Source = MyString(forward<T>(arg));
}
MyString& BuildResponseHeader()
{
Source = ProtocolVersion + " " + Code + " " + Status + "\r\n";
Header["Server"] = Server;
Header["Content-Length"] = to_string(ResponseBody.length());
for (auto x : Header)
{
Source += x.first + ":" + x.second + "\r\n";
}
Source += "\r\n";
return Source;
}
MyString& ParseFromSource()
{
auto ThrowError = [] {throw exception("解析错误"); };
auto data = Source.Split("\r\n");
if (data.size()==0)
{
return ResponseBody;
}
auto FirstLine = data[0].Split(' ');
if (FirstLine.size() != 3)
{
ThrowError();
}
//
ProtocolVersion = FirstLine[0].Trim();
Code = FirstLine[1].Trim();
Status = FirstLine[2].Trim();
data.erase(data.begin());
//
for (auto x : data)
{
auto pair = x.Split(':');
if (pair.size() != 2)
{
continue;
}
Header[pair[0].Trim()] = pair[1].Trim();
}
//
if (Source.find("\r\n\r\n")==Source.length()+4)
{
return ResponseBody;
}
ResponseBody = move(*--data.end());
return ResponseBody;
}
};
class Request
{
public:
MyString Source;
MyString Method;
MyString Path;
MyString ProtocolVersion = "HTTP/1.1";
map<MyString, MyString> Header;
MyString RequestBody;
Request() = default;
~Request() = default;
template<class T>
Request(T&& arg)
{
Source = MyString(forward<T>(arg));
}
void SetHttpMethod(MethodEnum method, MyString other = "")
{
if (other != "")
{
Method = other;
return;
}
switch (method)
{
case MethodEnum::Get:
Method = "GET";
break;
case MethodEnum::Post:
Method = "POST";
break;
case MethodEnum::Put:
Method = "PUT";
break;
case MethodEnum::Delete:
Method = "DELETE";
break;
default:
break;
}
}
void ParseFromSource()
{
auto ThrowError = [] {throw exception("解析错误"); };
if (Source.length() < 10)
{
ThrowError();
}
auto data = Source.Split("\r\n");
if (data.size() == 0)
{
ThrowError();
}
//请求行
auto FirstLine = data[0].Split(' ');
if (FirstLine.size() != 3)
{
ThrowError();
}
Method = FirstLine[0].Trim();
Path = FirstLine[1].Trim();
ProtocolVersion = FirstLine[2].Trim();
data.erase(data.begin());
//请求头
for (auto x : data)
{
auto pair = x.Split(':');
if (pair.size() != 2)
{
if (pair.size() == 3)//host:127.0.0.1:5000
{
Header[pair[0].Trim()] =
pair[1].Trim() + ":" + pair[2].Trim();
}
continue;
}
Header[pair[0].Trim()] = pair[1].Trim();
}
//请求体
if (Source.find_last_of("\r\n") == Source.length() + 2)
{
return;
}
RequestBody =move( *--data.end());
}
MyString SendRequest()
{
Source = Method + " " + Path + " " + ProtocolVersion + "\r\n";
for (auto x : Header)
{
Source += x.first + ":" + x.second + "\r\n";
}
Source += "\r\n";
Source += RequestBody;
return Source;
}
};
}
有点乱等过几天有时间把工程推到GitHub上,服务器也弄能运行,但是用的thread,而不是线程池,等搞懂后再写。这个系列可能有十几篇也可能直接鸽了。还是c#天下第一,await+async+task 没有解决不了的多线程异步任务,wpf的跨线程更新界面除外,或者说那TM是直接把任务拉到界面处理的绘图一多还是卡,但是瑕不掩瑜最重要的是给我赚了足够多的钱。
大佬们给个star吧 GitHub