所以我正在搞套接字编程,我写了一个转发代理,其工作原理如下:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <iostream>
#include <thread>
#pragma comment(lib, "ws2_32.lib")
#define BUFFER_SIZE 8192
#define DEFAULT_PORT "87"
// Global variables
SOCKET g_listenSocket = INVALID_SOCKET;
bool g_running = false;
bool initWinsock() {
WSADATA wsaData;
return WSAStartup(MAKEWORD(2, 2), &wsaData) == 0;
}
bool initServer() {
struct addrinfo* result = nullptr, hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
if (getaddrinfo(NULL, DEFAULT_PORT, &hints, &result) != 0) {
return false;
}
g_listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (g_listenSocket == INVALID_SOCKET) {
freeaddrinfo(result);
return false;
}
if (bind(g_listenSocket, result->ai_addr, (int)result->ai_addrlen) == SOCKET_ERROR) {
freeaddrinfo(result);
closesocket(g_listenSocket);
return false;
}
freeaddrinfo(result);
if (listen(g_listenSocket, SOMAXCONN) == SOCKET_ERROR) {
closesocket(g_listenSocket);
return false;
}
return true;
}
bool isConnectMethod(const char* request) {
return strncmp(request, "CONNECT", 7) == 0;
}
void parseHostAndPort(const char* request, char* host, char* port) {
char* line = new char[strlen(request) + 1];
strcpy(line, request);
if (isConnectMethod(line)) {
// Handle CONNECT method (HTTPS)
char* start = line + 8; // Skip "CONNECT "
char* end = strchr(start, ' ');
if (end) {
*end = '\0';
char* colon = strchr(start, ':');
if (colon) {
*colon = '\0';
strcpy(host, start);
strcpy(port, colon + 1);
}
else {
strcpy(host, start);
strcpy(port, "443");
}
}
}
else {
// Handle HTTP requests
bool foundHost = false;
char* current = line;
char* eol;
// Look for Host header
while ((eol = strstr(current, "\r\n")) != nullptr) {
*eol = '\0';
if (strncmp(current, "Host: ", 6) == 0) {
char* hostLine = current + 6;
char* colon = strchr(hostLine, ':');
if (colon) {
*colon = '\0';
strcpy(host, hostLine);
strcpy(port, colon + 1);
}
else {
strcpy(host, hostLine);
strcpy(port, "80");
}
foundHost = true;
break;
}
current = eol + 2;
}
// If no Host header found, try to parse from the request line
if (!foundHost) {
if (strncmp(line, "GET http://", 11) == 0 ||
strncmp(line, "POST http://", 12) == 0) {
char* start = strchr(line, '/') + 2;
char* end = strchr(start, '/');
if (end) {
*end = '\0';
char* colon = strchr(start, ':');
if (colon) {
*colon = '\0';
strcpy(host, start);
strcpy(port, colon + 1);
}
else {
strcpy(host, start);
strcpy(port, "80");
}
}
}
}
}
delete[] line;
}
SOCKET connectToHost(const char* host, const char* port) {
struct addrinfo hints = {}, * result = nullptr;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(host, port, &hints, &result) != 0) {
return INVALID_SOCKET;
}
SOCKET serverSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (serverSocket == INVALID_SOCKET) {
freeaddrinfo(result);
return INVALID_SOCKET;
}
if (connect(serverSocket, result->ai_addr, (int)result->ai_addrlen) == SOCKET_ERROR) {
closesocket(serverSocket);
freeaddrinfo(result);
return INVALID_SOCKET;
}
freeaddrinfo(result);
return serverSocket;
}
void handleBidirectionalTraffic(SOCKET client, SOCKET server) {
fd_set readSet;
char buffer[BUFFER_SIZE];
while (true) {
FD_ZERO(&readSet);
FD_SET(client, &readSet);
FD_SET(server, &readSet);
// Wait for data on either socket
if (select(0, &readSet, NULL, NULL, NULL) == SOCKET_ERROR) {
break;
}
// Check client -> server
if (FD_ISSET(client, &readSet)) {
int bytes = recv(client, buffer, sizeof(buffer), 0);
if (bytes <= 0) break;
if (send(server, buffer, bytes, 0) <= 0) break;
}
// Check server -> client
if (FD_ISSET(server, &readSet)) {
int bytes = recv(server, buffer, sizeof(buffer), 0);
if (bytes <= 0) break;
if (send(client, buffer, bytes, 0) <= 0) break;
}
}
closesocket(client);
closesocket(server);
}
void handleClient(SOCKET clientSocket) {
char buffer[BUFFER_SIZE];
int bytesReceived = recv(clientSocket, buffer, BUFFER_SIZE - 1, 0);
if (bytesReceived <= 0) {
closesocket(clientSocket);
return;
}
buffer[bytesReceived] = '\0';
char host[256] = { 0 };
char port[16] = { 0 };
parseHostAndPort(buffer, host, port);
if (strlen(host) == 0) {
const char* response = "HTTP/1.1 400 Bad Request\r\n\r\n";
send(clientSocket, response, strlen(response), 0);
closesocket(clientSocket);
return;
}
SOCKET serverSocket = connectToHost(host, port);
if (serverSocket == INVALID_SOCKET) {
const char* response = "HTTP/1.1 502 Bad Gateway\r\n\r\n";
send(clientSocket, response, strlen(response), 0);
closesocket(clientSocket);
return;
}
if (isConnectMethod(buffer)) {
// HTTPS: Send connection established
const char* response = "HTTP/1.1 200 Connection established\r\n\r\n";
if (send(clientSocket, response, strlen(response), 0) <= 0) {
closesocket(clientSocket);
closesocket(serverSocket);
return;
}
}
else {
// HTTP: Forward the original request
if (send(serverSocket, buffer, bytesReceived, 0) <= 0) {
closesocket(clientSocket);
closesocket(serverSocket);
return;
}
}
// Handle bidirectional traffic for both HTTP and HTTPS
handleBidirectionalTraffic(clientSocket, serverSocket);
}
void cleanup() {
g_running = false;
if (g_listenSocket != INVALID_SOCKET) {
closesocket(g_listenSocket);
g_listenSocket = INVALID_SOCKET;
}
WSACleanup();
}
int main() {
if (!initWinsock()) {
std::cerr << "Failed to initialize Winsock\n";
return 1;
}
if (!initServer()) {
std::cerr << "Failed to initialize server\n";
cleanup();
return 1;
}
g_running = true;
std::cout << "Proxy server listening on localhost:87...\n";
while (g_running) {
SOCKET clientSocket = accept(g_listenSocket, NULL, NULL);
if (clientSocket == INVALID_SOCKET) {
continue;
}
std::cout << "New client connected\n";
// Create a new thread to handle the client
std::thread(handleClient, clientSocket).detach();
}
cleanup();
return 0;
}
程序在循环中使用accept(),为每个连接的客户端创建一个线程,浏览器为每个http请求打开一个tcp连接。现在我想将此代码转换为一个反向代理,它将像这样工作,server.cpp,客户端.cpp。服务器将监听浏览器的连接,接收请求并将其传递给 client.cpp ,其中 client.cpp 将连接到目标主机,然后将响应传递给 server.cpp 然后将响应传递给 browser.client.cpp 将通过以下方式连接简单的 tcp 流到 server.cpp。
我的问题是:由于client.cpp/server.cpp将通过一个tcp流进行通信,我如何才能传递所有请求处理它们并将它们发送回server.cpp?,其中server.cpp针对从浏览器有一个线程吗? 我的第一个想法是在 client.cpp 中的循环内有一个 recv() ,并为从 server.cpp 发送的每个请求创建一个线程,但问题是我不知道如何将每个响应发送回其server.cpp 中的特定线程。
提前致谢。
如果您要通过单个连接发送多个数据流,则必须封装每个流中的数据。为此,您可能需要构建数据。
也就是说,在代理中读取一些数据,然后在发送到服务器时,添加一点标头,表示后面的 N 字节数据来自流 M。然后在服务器端,你需要有一点一段代码,用于解压数据、单独处理每个流中的数据,并将结果与类似的标头打包在一起,表示以下 N 个字节的回复用于流 M。
为了提供一点额外的保证(以及在出现问题时恢复同步的能力),您可能还需要在每帧的开头和结尾添加相当独特的模式。