c++ socket http服务端

非c++程序员,因要写一个dll注入工具,需要交互,写了一个socket实现的http服务端。

由于非c++科班,代码可能会有错误,仅供参考。

#include <map>
#include<string>
//soket头文件
#include<winsock2.h>  
#include <Ws2tcpip.h> 
//链接库文件
#pragma comment(lib,"ws2_32.lib")

using namespace std;

#define PORT 9999//定义服务器端口号

//声明函数
DWORD WINAPI http_server(LPVOID lpParam);
void response(SOCKET connect_socket, string html);
string getURI(string str);
string getBody(string str);
map<string, wstring> getParams(string str);
int hex2num(string str);
wstring URLDecode(string str);
wstring ANSI2UTF8(string text);


int main()
{
	//F1查看函数api
	/*HANDLE	hThread = CreateThread(NULL, 0, http_server, NULL, 0, NULL);
	if (hThread != 0)
	{
		CloseHandle(hThread);
	}*/
	http_server(NULL);
	return 0;
}


DWORD WINAPI http_server(LPVOID lpParam)
{
	//参考官网例子:https://learn.microsoft.com/zh-cn/windows/win32/winsock/complete-server-code?source=recommendations
	SOCKET listen_socket;//监听socket
	SOCKET connect_socket;//连接socket
	WORD ver = MAKEWORD(2, 2);//2.2版本的Socket
	WSADATA dat;
	int ret;
	if (WSAStartup(ver, &dat))
	{
		OutputDebugString(L"WSAStartup失败!\n");
		return 1;
	}

	//AF_INET:地址族,SOCK_STREAM:连接类型,IPPROTO_TCP:协议类型
	listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (listen_socket == INVALID_SOCKET)
	{
		WSACleanup();
		return 1;
	}

	//第1个参数:秒,第2个参数微秒
	struct timeval timeout = { 30,0 };
	//设置接收超时
	ret = setsockopt(listen_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeval));
	if (ret == -1) {
		WSACleanup();
		return 1;
	}
	//设置发送超时
	ret = setsockopt(listen_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeval));
	if (ret == -1) {
		WSACleanup();
		return 1;
	}


	//创建结构体 sockaddr_in对象储存自身信息
	struct sockaddr_in sever_address;
	memset(&sever_address, 0, sizeof(sever_address));
	sever_address.sin_family = AF_INET;//设置服务器地址家族
	//sever_address.sin_addr.s_addr = htonl(INADDR_ANY);//设置服务器IP地址
	//sever_address.sin_addr.s_addr = inet_addr("127.0.0.1");//设置服务器IP地址
	inet_pton(AF_INET, "127.0.0.1", &sever_address.sin_addr.s_addr);//vs2013版本以上使用新的函数转换IP地址
	sever_address.sin_port = htons(PORT);//设置服务器端口号
	//把名字和套接字绑定
	ret = bind(listen_socket, (sockaddr*)&sever_address, sizeof(sever_address));
	if (ret == SOCKET_ERROR)
	{
		closesocket(listen_socket);
		WSACleanup();
		return 1;
	}

	//listen函数在一般在调用bind之后调用,accept之前调用
	ret = listen(listen_socket, 1);//1表示是未经过处理的连接请求队列可以容纳的最大数目
	if (ret == SOCKET_ERROR)
	{
		closesocket(listen_socket);
		WSACleanup();
		return 1;
	}
	while (1)
	{
		OutputDebugString(L"等待客户端连接\n");
		//创建结构体 sockaddr_in对象储存客户端信息
		struct sockaddr_in client_address;
		int len = sizeof(client_address);
		memset(&client_address, 0, len);

		//从监控队列取出一个socket,取出connect_socket后
		//接受客户端连接,返回一个新的socket来和客户端通信
		connect_socket = accept(listen_socket, (sockaddr*)&client_address, &len);
		// 取出connect_socket后应该放入线程池执行,循环继续执行到accept()等待新的连接进来
		if (connect_socket == INVALID_SOCKET) {
			closesocket(connect_socket);
			break;
		}
		char buf[1];
		string buffer;
		while (1) //循环接收发送的内容
		{
			//memset(&buf, 0, sizeof(buf));
			//阻塞函数,等待接受内容
			//如果发送方发送了多次信息,接收方没来得及进行recv,则数据堆积在输入缓冲区中,取数据的时候会都取出来。
			//换句话说,recv并不能判断数据包的结束位置。这里简单使用超时来处理。
			ret = recv(connect_socket, buf, sizeof(buf), 0);//每次接受的数据大小。结束返回-1
			if (ret > 0)
			{
				buffer.push_back(buf[0]);
			}
			else break;
		}
		wstring out_buffer = ANSI2UTF8(buffer);
		OutputDebugString(out_buffer.c_str());


		string method = buffer.substr(0, buffer.find(" "));
		string uri = getURI(buffer);
		string body = getBody(buffer);

		map<string, wstring> params = getParams(body);
		string send_body = "成功";
		if (uri.compare("/api/sendtxtmsg") == 0) {
			send_body = "发送文本消息成功";
			response(connect_socket, send_body);
		}
		else if (uri.compare("/api/sendpic") == 0) {
			send_body = "发送图片消息成功";
			response(connect_socket, send_body);
		}
		else if (uri.compare("/api/sendattatch") == 0) {
			send_body = "发送文件消息成功";
			response(connect_socket, send_body);
		}
		else {
			send_body = "404";
			response(connect_socket, send_body);
		}
		closesocket(connect_socket);

	}
	closesocket(listen_socket);
	WSACleanup();
	ret = getchar();
	//Sleep(10*1000);//1000表示睡眠1000毫秒(及一秒)
	return 0;
}


//响应数据到客户端
void response(SOCKET connect_socket, string body) {
	char head[] = "HTTP/1.1 200 OK\r\nContent-Type: text/html;charset=GB2312\r\n\r\n";
	send(connect_socket, head, strlen(head), 0);
	//不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据
	send(connect_socket, body.c_str(), strlen(body.c_str()), 0);
}
//截取请求地址
string getURI(string str) {
	int posStart = str.find(" ") + 1;
	int posEnd = str.find(" ", posStart);
	string uri = str.substr(posStart, posEnd - posStart);
	posStart = uri.find("?");
	if (posStart > 0) {
		uri = uri.substr(0, posStart);
	}
	return uri;
}
//截取请求体
string getBody(string str) {
	if (str.empty()) return str;
	int posStart = str.find("\r\n\r\n") + 4;
	int size = str.size();
	string body = str.substr(posStart, size - posStart);
	return body;
}

//解析请求参数
map<string, wstring> getParams(string str) {
	int pos;
	map<string, wstring> params;
	string param, key, val;
	wstring wval;
	str += "&";//扩展字符串以方便判断
	int size = str.size();
	for (int i = 0; i < size; i++)
	{
		pos = str.find("&", i);
		if (pos > 0)
		{
			param = str.substr(i, pos - i);
			i = pos;
			pos = param.find("=");
			key = param.substr(0, pos);
			val = param.substr(pos + 1, param.size() - pos - 1);
			wval = URLDecode(val);
			params[key] = wval;

		}
	}
	return params;
}

//16进制转10进制
int hex2num(string str)
{
	char c;
	int num;
	int total = 0;
	int size = str.size();
	for (int i = 0; i < size; i++)
	{
		c = str[i];
		switch (c)
		{
		case 'A': num = 10; break;
		case 'B': num = 11; break;
		case 'C': num = 12; break;
		case 'D': num = 13; break;
		case 'E': num = 14; break;
		case 'F': num = 15; break;
		default:  num = c - '0'; break;
		}
		total += (num * (int)pow(16, size - 1 - i));//FC = 15*16^1 + 12*16^0
	}
	return total;
}

// url解码
wstring URLDecode(string str)
{
	char c;
	int num;
	int size = str.size();
	string result;
	for (int i = 0; i < size; i++) {
		c = str[i];
		switch (c) {
		case '+':
			result.push_back(' ');
			break;
		case '%':
			// 每3个字符串%xy将产生一个字节
			if (i + 2 < size) {
				num = hex2num(str.substr(i + 1, 2));
				result.push_back((char)num);
				i += 2;
			}
			break;
		default:
			result.push_back(c);
			break;
		}
	}
	wstring wresult = ANSI2UTF8(result);
	return wresult;
}

//ANSI转UTF8
wstring ANSI2UTF8(string text) {
	/*
	MultiByteToWideChar 参数详解
	1、CodePage:编码类型
	2、dwFlags:UTF-8的dwFlags必须设置为0
	3、lpMultiByteStr:要转换的字符串。注意c_str()函数:将字符串的内容转换为以 null 结尾的 C 样式字符串。
	4、cbMultiByte:要转换的字符串的大小(以字节为单位)。 或者,如果字符串以 null 结尾,则可以将此参数设置为 -1。
	5、lpWideCharStr:接收转换后的字符串
	6、cchWideChar:指示的缓冲区的大小(以字符为单位)。 如果此值为 0,则该函数将返回所需的缓冲区大小(以字符为单位,包括任何终止 null 字符)并且不使用 lpWideCharStr 缓冲区。
	*/
	//获取转为UTF8多字节后需要的缓冲区大小
	int len = MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, NULL, 0);
	//创建多字节缓冲区
	WCHAR* buffer = new WCHAR[len];
	MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, buffer, len);
	buffer[len - 1] = '\0';
	wstring wstr(buffer);
	delete[] buffer;
	return wstr;
}

浏览器访问 http://localhost:9999/api/sendattatch

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现一个基本的HTTP服务器,你需要完成以下步骤: 1. 创建一个TCP套接字并绑定到一个口 2. 监听传入的连接请求 3. 接受连接请求并处理客户请求 4. 解析HTTP请求 5. 处理HTTP请求并生成HTTP响应 6. 发送HTTP响应到客户并关闭连接 以下是一个简单的c++实现HTTP服务器的示例代码: ```c++ #include <iostream> #include <cstring> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> using namespace std; const int PORT = 8080; const int MAX_CONNECTIONS = 10; int main() { int server_socket, client_socket; struct sockaddr_in server_address, client_address; char buffer[1024] = {0}; // 创建一个套接字 server_socket = socket(AF_INET, SOCK_STREAM, 0); if (server_socket == -1) { cerr << "Failed to create socket" << endl; exit(EXIT_FAILURE); } // 绑定套接字到指定口 server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = INADDR_ANY; server_address.sin_port = htons(PORT); if (bind(server_socket, (struct sockaddr *) &server_address, sizeof(server_address)) == -1) { cerr << "Failed to bind socket to port " << PORT << endl; exit(EXIT_FAILURE); } // 监听传入的连接请求 if (listen(server_socket, MAX_CONNECTIONS) == -1) { cerr << "Failed to listen for connections" << endl; exit(EXIT_FAILURE); } cout << "Server listening on port " << PORT << endl; // 接受连接请求并处理客户请求 while (true) { socklen_t client_address_size = sizeof(client_address); client_socket = accept(server_socket, (struct sockaddr *) &client_address, &client_address_size); if (client_socket == -1) { cerr << "Failed to accept connection" << endl; continue; } cout << "Client connected: " << inet_ntoa(client_address.sin_addr) << endl; // 读取客户发送的HTTP请求 memset(buffer, 0, sizeof(buffer)); if (read(client_socket, buffer, sizeof(buffer)) == -1) { cerr << "Failed to read HTTP request" << endl; close(client_socket); continue; } cout << "Received HTTP request:" << endl << buffer << endl; // 处理HTTP请求并生成HTTP响应 const char *response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nHello World!"; int response_length = strlen(response); // 发送HTTP响应到客户并关闭连接 if (write(client_socket, response, response_length) == -1) { cerr << "Failed to send HTTP response" << endl; } cout << "Sent HTTP response:" << endl << response << endl; close(client_socket); cout << "Client disconnected" << endl; } return 0; } ``` 这个示例代码创建了一个TCP套接字并绑定到8080口。它监听传入的连接请求,接受连接请求并处理客户请求。当客户发送HTTP请求时,它读取HTTP请求并生成HTTP响应,然后将响应发送回客户并关闭连接。 请注意,这只是一个简单的示例代码,并不具有完整的HTTP服务器功能。如果你想要实现一个完整的HTTP服务器,你需要考虑更多的因素,比如线程管理、错误处理、安全性等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值