HTTP转发小Demo

工作中一个方案可行性预研写的小Demo,一晚上搞定的小程序.

主要功能是一个Http透明转发:

1.监听一个端口, 接收浏览器的连接请求.

2. 接收浏览器发出的请求数据, 将这些转发给一个指定的服务器.

3. 接收服务器的应答,将应答发送给浏览器.

代码都在一个文件中:

// httptranschannel.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "httptranschannel.h"
#include <iostream>
#include <string>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

const int PORT_LOCAL = 8084;
//const char* SERVER_IP = "192.168.84.133";
//const int SERVER_PORT = 80;
const int PACKAGE_BUFFER_SIZE = 100*1024;	// 分包大小.

SOCKET g_listenSocket = INVALID_SOCKET;
SOCKET g_acceptSocket = INVALID_SOCKET;

SOCKET g_serverSocket = INVALID_SOCKET;	// 同服务器之间的连接Socket.

// 唯一的应用程序对象

CWinApp theApp;

using namespace std;

int InitSocket()
{
	// Initialize Winsock
	WSADATA wsaData;
	int iResult = 0;
	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != NO_ERROR) {
		wprintf(L"WSAStartup() failed with error: %d\n", iResult);
		return 1;
	}
	return 0;
}

int ListenSocket()
{
	if ( g_listenSocket == INVALID_SOCKET )
	{
		int iResult = 0;

		sockaddr_in service;

		//----------------------
		// Create a SOCKET for listening for incoming connection requests.
		g_listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (g_listenSocket == INVALID_SOCKET) {
			wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
			WSACleanup();
			return 1;
		}
		//----------------------
		// The sockaddr_in structure specifies the address family,
		// IP address, and port for the socket that is being bound.
		service.sin_family = AF_INET;
		service.sin_addr.s_addr = ADDR_ANY; // inet_addr("127.0.0.1");
		service.sin_port = htons(PORT_LOCAL);

		iResult = bind(g_listenSocket, (SOCKADDR *) & service, sizeof (service));
		if (iResult == SOCKET_ERROR) {
			wprintf(L"bind function failed with error %d\n", WSAGetLastError());
			iResult = closesocket(g_listenSocket);
			if (iResult == SOCKET_ERROR)
				wprintf(L"closesocket function failed with error %d\n", WSAGetLastError());
			WSACleanup();
			return 1;
		}
		//----------------------
		// Listen for incoming connection requests
		// on the created socket
		if (listen(g_listenSocket, SOMAXCONN) == SOCKET_ERROR)
			wprintf(L"listen function failed with error: %d\n", WSAGetLastError());

		wprintf(L"Listening on socket...\n");

		//----------------------
		// Create a SOCKET for accepting incoming requests.
		wprintf(L"Waiting for client to connect...\n");
	}

	//----------------------
	// Accept the connection.
	g_acceptSocket = accept(g_listenSocket, NULL, NULL);
	if (g_acceptSocket == INVALID_SOCKET) {
		wprintf(L"accept failed with error: %ld\n", WSAGetLastError());
		closesocket(g_listenSocket);
		WSACleanup();
		return 1;
	} else
		wprintf(L"Client connected.\n");

	//g_acceptSocket = accept(g_listenSocket, NULL, NULL);
	//if (g_acceptSocket == INVALID_SOCKET) {
	//	wprintf(L"accept failed with error: %ld\n", WSAGetLastError());
	//	closesocket(g_listenSocket);
	//	WSACleanup();
	//	return 1;
	//} else
	//	wprintf(L"Client connected.\n");

	return 0;
}

int DestroySocket()
{
	if ( INVALID_SOCKET != g_listenSocket )
	{
		int iResult = closesocket(g_listenSocket);
		if (iResult == SOCKET_ERROR) {
			wprintf(L"closesocket function failed with error %d\n", WSAGetLastError());
			WSACleanup();
			return 1;
		}
	}

	WSACleanup();
	return 0;
}

int ReceivFromSocket( SOCKET hSocket, char* pBuf, const int bufLen, int& dataLen, bool& disconnected )
{
	disconnected = false;

	fd_set fdSocket;        // 所有可用套节字集合
	FD_ZERO(&fdSocket);
	FD_SET( hSocket, &fdSocket);
	timeval tv;
	tv.tv_sec   =   0;
	tv.tv_usec   =   0;
	int nHasData = select( 1,&fdSocket, NULL, NULL, &tv) ;
	if ( SOCKET_ERROR == nHasData )
	{
		cout << "ReceivFromSocket select fail! er: " << WSAGetLastError() << endl;

		disconnected = true;

		return -1;
	}
	else if( 1 == nHasData )
	{
		dataLen = recv( hSocket, pBuf, bufLen, 0 );

		// 返回0说明正常断开连接了.
		disconnected = (dataLen == 0);
		return 0;
	}
	else if( 0 == nHasData )
	{
		return 0;
	}
	else
	{
		cout << "ReceivFromSocket select return :" << nHasData << endl;
	}
	return -1;
}

int Connect2Server( const char* serverIp, const int serverPort )
{
	if ( g_serverSocket == INVALID_SOCKET )
	{
		sockaddr_in server;

		//----------------------
		// Create a SOCKET for listening for incoming connection requests.
		g_serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (g_listenSocket == INVALID_SOCKET) {
			wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
			WSACleanup();
			return 1;
		}
		//----------------------
		// The sockaddr_in structure specifies the address family,
		// IP address, and port for the socket that is being bound.
		server.sin_family = AF_INET;
		server.sin_addr.s_addr = inet_addr( serverIp );
		server.sin_port = htons( serverPort );

		int result = connect( g_serverSocket, (SOCKADDR *)&server, sizeof( server ) );
		if ( 0 != result )
		{
			// 连接失败.
			cout << "Connect to server fail! ip: " << serverIp << " port: " << serverPort << " er: " << result << endl;

			// 销毁.
			closesocket( g_serverSocket );
			g_serverSocket = INVALID_SOCKET;
			return -1;
		}
	}
	return 0;
}

int Send2Socket( SOCKET hSock, const char* pBuf, const int dataLen )
{
	// 将数据发送给Server.
	int sendLen = 0;
	while( sendLen < dataLen )
	{
		int sendRet = send( hSock, pBuf + sendLen, dataLen - sendLen, 0 );
		if ( sendRet < 0 )
		{
			cout << "Send Data 2 server fail! ret: " << sendRet << " ec: " << WSAGetLastError() << endl;
			return -1;
		}
		else
		{
			sendLen += sendRet;
		}
	}
	return 0;
}

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
	int nRetCode = 0;

	HMODULE hModule = ::GetModuleHandle(NULL);

	if (hModule != NULL)
	{
		// 初始化 MFC 并在失败时显示错误
		if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
		{
			// TODO: 更改错误代码以符合您的需要
			_tprintf(_T("错误: MFC 初始化失败\n"));
			nRetCode = 1;
		}
		else
		{
			string serverIp;
			int serverPort = 0;

			while( serverIp.empty() )
			{
				cout << "Please type in server ip: ";
				cin >> serverIp;
			}

			while( serverPort == 0 )
			{
				cout << "Please type in server port: ";
				cin >> serverPort;
			}

			// 初始化
			InitSocket();

			while(1)
			{
				int sleepTime = 50;
				if ( g_acceptSocket == INVALID_SOCKET )
				{
					// 监听端口,处理新连接.
					ListenSocket();
				}
				else
				{
					// 连接服务端
					Connect2Server( serverIp.c_str(), serverPort );

					// 新连接中,接收数据.
					char clientDataBuf[ PACKAGE_BUFFER_SIZE ] = {0};
					int dataLen = 0;
					bool disconnected = false;
					ReceivFromSocket( g_acceptSocket, clientDataBuf, sizeof( clientDataBuf ), dataLen, disconnected );

					if( disconnected )
					{
						cout << "accept socket disconnect!" << endl;
						closesocket( g_acceptSocket );
						g_acceptSocket = INVALID_SOCKET;
					}

					// 建立同服务端的新连接,将数据转发.
					if ( dataLen > 0 )
					{
						// 如果有数据, 就不等待. 否则等待.
						sleepTime = 0;

						if ( g_serverSocket != INVALID_SOCKET )
						{
							Send2Socket( g_serverSocket, clientDataBuf, dataLen );
						}
					}

					// 接收服务端的数据,将数据发送给浏览器.
					if ( g_serverSocket != INVALID_SOCKET )
					{
						char serverDataBuf[ PACKAGE_BUFFER_SIZE ] = {0};
						int serverDataLen = 0;
						bool serverDisconnected = false;
						ReceivFromSocket( g_serverSocket, serverDataBuf, sizeof( serverDataBuf ), serverDataLen, serverDisconnected );

						if ( serverDataLen > 0 )
						{
							// 如果有数据, 就不等待. 否则等待.
							sleepTime = 0;

							Send2Socket( g_acceptSocket, serverDataBuf, serverDataLen );
						}

						if ( serverDisconnected )
						{
							cout << "Server disconnect! " << endl;
							closesocket( g_serverSocket );
							g_serverSocket = INVALID_SOCKET;

							// 也和浏览器断链.
							closesocket( g_acceptSocket );
							g_acceptSocket = INVALID_SOCKET;
						}
					}

				}
				Sleep( sleepTime );
			}
		}
	}
	else
	{
		// TODO: 更改错误代码以符合您的需要
		_tprintf(_T("错误: GetModuleHandle 失败\n"));
		nRetCode = 1;
	}

	return nRetCode;
}

HTTP转发小Demo

时间: 2025-01-03 23:03:58

HTTP转发小Demo的相关文章

Nancy之基于Self Hosting的补充小Demo

前面把Hosting Nancy with ASP.NET.Self Hosting Nancy和Hosting Nancy with OWIN 以demo的形式简单描述了一下. 这篇是为Self Hosting Nancy.和Owin 下面的Self Hosting作个补充. 首先是Self Hosting Nancy的补充: 这里主要是介绍一下Topshelf 官网:http://topshelf-project.com/ GitHub地址:https://github.com/Topshe

用backbone实现的一个MVC的小demo

一.Apache配置 本实例需要使用php支持.要现在Apache中配置虚拟目录,在Apache下的httpd-vhosts.conf文件中添加如下代码 <VirtualHost *:80> DocumentRoot "D:/htdocs/backbone_demo" ServerName www.backbonedemo.cn </VirtualHost> 在windows的hosts文件中添加配置,hosts文件的位置在c:\windows\system32

结对项目小DEMO

这次小DEMO主要实现下面两个功能: 实现两个页面的相互跳转 通过多线程实现网络发送请求 本人负责界面与说明文档编写,搭档负责java代码的具体实现. xml代码: 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_

React问答小demo

在学习react初期,看了一些视频和资料,react基础知识差不多学完,跟着网上的一个教程,做了一个小型的问答demo. 需求看图说: 1.点击"添加"按钮,显示问题输入表单,再次点击,隐藏表单.同时,点击"取消"按钮,隐藏表单. 2.输入问题标题和内容后,点击"确认"按钮,将问题显示在下方(按照投票数从高到低). 3.每个问题有加票和减票功能,在点击的同时,将问题按照投票数从高到低排序. 实现过程: 一.开发环境和工具 1.npm init (

Nancy之基于Nancy.Owin的小Demo

前面做了基于Nancy.Hosting.Aspnet和Nancy.Hosting.Self的小Demo 今天我们来做个基于Nancy.Owin的小Demo 开始之前我们来说说什么是Owin和Katana 什么是Owin呢? 官网地址:http://owin.org OWIN在.NET Web Servers与Web Application之间定义了一套标准接口,OWIN的目标是用于解耦Web Server和Web Application. 什么是Katana呢? 官网地址:http://kata

第一个spring小demo

工作中用spring有一年多了,基本弄懂了bean配置文件的配置,但是却没有对spring配置文件,加载有更多的认识,今天动手写了第一个spring的小demo. 这个demo之前是想做web版的,但是web的启动比较麻烦,不如直接使用main方法执行直观,所以,就使用main方法来读取配置文件,启动spring. 看一下项目的结构  其中src部分是项目源码 和 配置文件applicationContext.xml WebContent 目录下面的WEB-INF/lib 目录下面存放的是spr

利用block完成回调,小demo一个

利用block完成回调,小demo一个.闲话少说,直接上代码了!O(∩_∩)O~ TestObject.h #import <Foundation/Foundation.h> typedef void (^FinishBlock)(NSString *backStr); @interface TestObject : NSObject //能进行回调的方法 - (void)playSomeTime:(FinishBlock)block; @end TestObject.m #import &q

新手 gulp+ seajs 小demo

首先,不说废话,它的介绍和作者就不在多说了,网上一百度一大堆: 我在这里只是来写写我这2天抽空对seajs的了解并爬过的坑,和实现的一个小demo(纯属为了实现,高手请绕道); 一.环境工具及安装 1.首先,务必先说明,本demo是基于nodeJs环境下开发的,因此要安装nodeJs(地址:https://nodejs.org/en/): 2.接下来安装gulp: 在第一步成功的情况下,安装gulp构建工具,并且将其依赖到项目进来:同时需要安装browser-sync,gulp-seajs-co

Win10 FaceAPI小demo开发问题汇总

最近使用微软牛津计划做一个小demo,使用FaceAPI做一个小应用,实现刷脸的功能.开发的过程中用到几个问题,具体如下: Stream 与IRandomAccessStream转换 sdk需要的是Stream,拍照直接获取到的类型是IRandomAccessStream,虽然可以转换,但IRandomAccessStream转换为Stream之后使用的时候会出现异常, 希望大神看到肯赐教,不胜感激 解决方法是使用FileOpen方法,将图片的路径作为参数传递给方法 MSDN网页上的为Group