用socket实现自己的HTTP代理服务器

50 篇文章 1 订阅
18 篇文章 0 订阅

下面是一个先前我用socket实现HTTP代理服务器的例子,代码很简单我就不一一解析了,要注意的是:当该代理服务器收到IE发来的请求时,要将IE请求行中的webserver的域名(或者IP)剥去,再将请求转发给webserver,如果不注意这点而是直接毫无处理的把IE请求信息直接发给webserver某些网站会显示不了网页,比如新浪,pfan,这是测试时发现的,其他就没什么可以说的了。实际上能否完成这个简单的HTTP proxy server可以看出对HTTP协议的熟悉程度,只要有一定的sock编程基础并熟悉了HTTP协议的工作流程,那我想就没什么障碍了。著此文章以供初学者参考,能从里面得到一点点的帮助那我已经心满意足了,高人指点改进意见邮箱联系,下面是源代码:

#include <stdio.h>
#include <iostream>
#include <string>
#include <fstream>
#include <winsock2.h>
#include <process.h>
#include "SockInit.h"
using namespace std;

#define MAX_REQUEST_LEN  2048  // IE请求长度
char    szFileName[] ="2004041155.txt";  // 日志文件

CSockInit initSock; // initalize winsock

CRITICAL_SECTION CriticalSection;  // 临界区对象(global variable)

void __cdecl ServerThreadProc(void *param);

/************************************************

 调试环境:Visual C++6.0 SP6+platform SDK 2003 R2

 程序名称:HTTP proxy Server(HttpProxySvr.cpp)

 功能    :IE通过设置代理方式上网,IE所有请求均
          先发到该代理服务器,代理服务器记录IE请
    求日志 ,并将请求直接转发给webserver, 
    从webserver读取响应后发回IE
            
 Date:    2007/10/12
 author:  liqisong
 eamil :  liqisong611@yahoo.com.cn
 
**************************************************/

int main()
{
 USHORT uPort=9999;   // 通信端口
    
 SOCKET sListen = socket(AF_INET,SOCK_STREAM,0);
 if(sListen == INVALID_SOCKET)
 {
  printf("Failed socket(),error code :%d",WSAGetLastError());
  return 0;
 }

 sockaddr_in sin;
 sin.sin_addr.S_un.S_addr = INADDR_ANY;
 sin.sin_family =AF_INET;
 sin.sin_port = htons(uPort);
 
 if(bind(sListen,(sockaddr*)&sin,sizeof(sin))==SOCKET_ERROR)
 {
     printf("Failed bind(),error code :%d",WSAGetLastError());
  closesocket(sListen);
  return 0;
 }

 int res =listen(sListen,200);
    
 sockaddr_in addrRemote;       // IE地址信息
 int len =sizeof(addrRemote);  
 SOCKET sNew;   // 新建立的连接状态套接口
  
    InitializeCriticalSection(&CriticalSection);

 while(true)
 {
     sNew = accept(sListen,(sockaddr*)&addrRemote,&len);
  if(sNew == INVALID_SOCKET)
  {
   printf("Failed accept(),error code :%d",WSAGetLastError());
   DeleteCriticalSection(&CriticalSection);
            closesocket(sListen);
   return 0;
  }
     
  printf("收到一个IE连接(IP:%s)\n",inet_ntoa(addrRemote.sin_addr));

  // 创建一个线程处理该连接 (每客户单线程模式...)
  stParam *pParam = new stParam;
        pParam->s = sNew;
  pParam->addr = addrRemote;
  _beginthread(ServerThreadProc,0,(void*)pParam);

 }

    DeleteCriticalSection(&CriticalSection);
    closesocket(sListen);

 return 0;
}

void __cdecl ServerThreadProc(void *param)
{
 stParam *pParam = (stParam*)param;

 SOCKET sock = pParam->s;          // 与浏览器连接的套接口
 sockaddr_in addrIE = pParam->addr;  // 与浏览器连接的套接口地址信息
    delete pParam; 

 char request[MAX_REQUEST_LEN];
 memset(request,0,MAX_REQUEST_LEN);
 int res =TCPrecv(sock,request,MAX_REQUEST_LEN,0);  // 接收浏览器(客户端)发来的请求
 if(res==0)
 {
  
  printf("连接断开\n");
  shutdown(sock,SD_RECEIVE);
  closesocket(sock);
  return;
 }
 else if(res==SOCKET_ERROR)
 {
        printf("Failed recv(),error code:%d\n",WSAGetLastError());
  shutdown(sock,SD_RECEIVE);
  closesocket(sock);
  return;
 }

 shutdown(sock,SD_RECEIVE);
 
 // printf("header length :%d  header: %s\n",res,request);//  打印收到的请求

 // 分析请求行(分离出WebServer域名)
    char   szAddrName[1024]={0};    // 域名
 char   szRequestLine[1024]={0}; // 请求行
 string  strHeader;              // 头部
 char *pdest;
    int pos,count=0;
 char ch =' ';
 ofstream fout;                 //  日志文件

 pdest = strchr(request, ch );  // 寻找空格第一次出现的位置
 if( pdest == NULL )
 {
  printf( "Result:\t%c not found,error HTTP request\n" );
  closesocket(sock);
  return;
 }
    pos = pdest - request+8; // +8  <==> + strlen("http://")+1;
 int i =pos;
    while(request[i]!='/'&&i<res)
    {
        szAddrName[i-pos] = request[i];
        i++;      
    }

    szAddrName[i-pos]='\0';   
 printf("WebServer address: %s\n\n",szAddrName);
     
    pos = pdest - request;          // 空格第一次出现的位置 
 pdest = strchr(request,'\n');   // 寻找\n第一次出现的位置
    count = pdest - request+1;
 strncpy(szRequestLine,request,count); // 得到请求行(末尾包含了"\r\n")
 strHeader=pdest+1;  // 得到头部

 //  做日志记录
 EnterCriticalSection(&CriticalSection);  // 进入临界区
 
 fout.open(szFileName,ios::app); // 追加的方式
    if(fout.fail())
 {
  printf("Input file(2004041155.txt) opening failed.\n");
  closesocket(sock);
  LeaveCriticalSection(&CriticalSection);
  return;
 }
 SYSTEMTIME st;
 GetLocalTime(&st);
 fout<<"Request line:\r\n("<<inet_ntoa(addrIE.sin_addr)<<")"<<szRequestLine<<st.wYear<<"-"<<st.wMonth<<"-"<<st.wDay<<"  "
  <<st.wHour<<":"<<st.wMinute<<":"<<st.wSecond<<endl;
   
   // fout.close();
    
   // 将请求行处理一下(使用相对URI连接WebServer)
   while(szRequestLine[i]!='/0'&&i<1024)
   {
     szRequestLine[++pos] = szRequestLine[i++];
   }
   szRequestLine[++pos]='\0';
   strcpy(request,szRequestLine);
   strcat(request,strHeader.c_str());  // 得到新请求(等于处理过后的请求行+ 头部)
   // cout<<"New request:"<<request<<endl;

 // 向WebServer提交请求
 SOCKADDR_IN   saServer;   
 LPHOSTENT     lphostent;     
 SOCKET        hsocket;
 int   nRet;   
 
 // 通过域名获得目标WebServer的IP 
 if(isalpha(szAddrName[0])) /* server address is a name*/    
 {   
  lphostent =  gethostbyname(szAddrName);   
 }   
 else     
 {  
  unsigned long addr  =  inet_addr(szAddrName);   
  lphostent = gethostbyaddr((const char*)&addr,4,AF_INET);   
 }   
  
 if(lphostent==NULL)
 {
  printf("Cannot resolve address,error code:%d\n",WSAGetLastError());
  closesocket(sock);
  fout.close();
  LeaveCriticalSection(&CriticalSection);
  return ;    
 }

 hsocket = socket(AF_INET,SOCK_STREAM,0); 
 if(hsocket==INVALID_SOCKET)
 {
       printf("Failed socket(),error code:%d\n",WSAGetLastError());
    closesocket(sock);
    fout.close();
    LeaveCriticalSection(&CriticalSection);
    return ;    
 }
 saServer.sin_family = AF_INET;   
 saServer.sin_port = htons(80); 
 saServer.sin_addr= *((LPIN_ADDR)*lphostent->h_addr_list);   
 
 nRet = connect(hsocket,(sockaddr*)&saServer,sizeof(saServer));   
 if(nRet==SOCKET_ERROR)   
 {   
  printf("Failed connect(),error code:%d\n",WSAGetLastError());
  closesocket(sock);
  closesocket(hsocket); 
  fout.close();
  LeaveCriticalSection(&CriticalSection);
  return;   
 } 
 
 //  向webserver转发IE请求 
 nRet = TCPsend(hsocket,request,strlen(request),0);

 char   dest[1000];   
 nRet=1; 
 int senlen=0;

    memset(dest,0,1000);
 nRet=recv(hsocket,dest,sizeof(dest)-1,0);  // 从webserver获取响应行
 if(nRet==0)   
 {
  printf("与webserver(%s)的连接关闭\n",szAddrName);
  shutdown(hsocket,SD_RECEIVE); //关闭接收操作
  closesocket(sock);
     closesocket(hsocket);
  fout.close();
  LeaveCriticalSection(&CriticalSection);
  return;
 }
 else  if(nRet==SOCKET_ERROR)
 {
  printf("Failed recv(),error code:%d\n",WSAGetLastError());
  shutdown(hsocket,SD_RECEIVE); //关闭接收操作
  closesocket(sock);
     closesocket(hsocket);
  fout.close();
  LeaveCriticalSection(&CriticalSection);
  return;
 }

 // 取出响应行,处理之...
 dest[nRet]='\0';
 char szRespondLine[1000]={0};
 char *pch;
 pdest = strchr(dest,'\n');  //'\n'第一 次出现的位置 
    count = pdest - dest+1;
    pdest++; //越过'\n'
 pch = strchr(pdest,'\n');  // '\n'第二次出现的位置 
    count +=(pch -pdest+1);
    strncpy(szRespondLine,dest,count);
    szRespondLine[count]='\0';
 cout<<szRespondLine;
    //  记录响应状态日志
 fout<<"Respond line:\r\n"<<szRespondLine;
 fout<<"-----------------------------------\r\n";
 fout.close();
    LeaveCriticalSection(&CriticalSection); // 退出临界区

 senlen = TCPsend(sock,dest,nRet,0);  //将数据转发给IE浏览器
 if(senlen==0)
 {
  printf("与浏览器(%s)的一个连接关闭...\n",inet_ntoa(addrIE.sin_addr));
  closesocket(sock);
     closesocket(hsocket);
  return;
 }
 else if(senlen==SOCKET_ERROR)
 {
  printf("Failed send(),error code:%d\n",WSAGetLastError());       
  closesocket(sock);
     closesocket(hsocket);
  return;
 }
 
 // 循环接收webserver发来的数据,直至该连接关闭...
 while(nRet>0)   
 {   
  memset(dest,0,1000);
  nRet=recv(hsocket,dest,sizeof(dest),0);  // 从webserver获取数据 
  if(nRet==0)   
  {
   printf("与webserver(%s)的连接关闭\n",szAddrName);
   shutdown(hsocket,SD_RECEIVE); //关闭接收操作
   break;
  }
  else  if(nRet==SOCKET_ERROR)
  {
     printf("Failed recv(),error code:%d\n",WSAGetLastError());       
     break;
  }

  senlen = TCPsend(sock,dest,nRet,0);  //将数据转发给IE浏览器
  if(senlen==0)
  {
            printf("与浏览器(%s)的一个连接关闭...\n",inet_ntoa(addrIE.sin_addr));
   break;
  }
  else if(senlen==SOCKET_ERROR)
  {
     printf("Failed send(),error code:%d\n",WSAGetLastError());       
     break;
  }

  // printf("%s\n",dest);

 } // end of while(nRet>0)  


 closesocket(sock);
 closesocket(hsocket);
}

int TCPsend(SOCKET s,const char *buf,int len,int flags)
{
 int n=0,sendCount=0;
 int length =len;
 if(buf==NULL)
  return 0;
 while(length>0)
 {
  n=send(s,buf+sendCount,length,flags); //发送数据,
  if(n==0)
  {
   break;
  }
  if(n==SOCKET_ERROR)//网络出现异常
  {
   printf("Failed send(),error code:%d\n",WSAGetLastError());
   break;
   
  }
  length-=n;
  sendCount+=n; 
 }

 return sendCount; // 返回已发送的字节数
}

int TCPrecv(SOCKET s,char *buf,int len,int flags)
{
 
 int nRev=0,recvCount=0;
 int length =len;

 if(buf==NULL)
  return 0;

 // 循环接收数据
 while(length>0)
 {
  nRev =recv(s,buf+recvCount,length,flags);
  if(nRev==0)
  {
   break;
  }
  if(nRev==SOCKET_ERROR)//网络出现异常
  {
   printf("Failed recv(),error code:%d\n",WSAGetLastError());
   break;
  }
  length-=nRev;
  recvCount+=nRev;
 }

 return recvCount; //返回接收到的字节数
}

 

/ / / / / / / / / / /  / / / / / / / / / / / / / /  / / / / / / / / / / / / / / / / / / / / / / / /

// sockinit.h

#include <Winsock2.h>
#pragma comment(lib,"Ws2_32.lib")

class CSockInit
{
public:
  CSockInit(BYTE minorVer =2,BYTE majorVer =2)
  {
   // 初始化Ws2_32.dll
   WORD wVersionRequested;
   WSADATA wsaData;
   int err;
   
   wVersionRequested = MAKEWORD( minorVer, majorVer); 
   
   err = WSAStartup( wVersionRequested, &wsaData );
   if (err != 0 ) 
   {
    exit(1);
   }

   if( LOBYTE( wsaData.wVersion ) != majorVer ||HIBYTE( wsaData.wVersion ) != minorVer )
   {
    WSACleanup();
    exit(1);
   }

  }

  ~CSockInit()
  {
    WSACleanup();
  }

};

// 线程参数结构体
struct stParam
{
 SOCKET   s;        //  连接状态的套接字
 sockaddr_in addr;  //  对方地址信息
 
};

//  自己实现TCP的收发函数
int TCPsend(SOCKET s,const char*buf,int len,int flags);
int TCPrecv(SOCKET s,char  *buf,int len,int flags);

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值