Http 代理工具 实战 支持网页与QQ代理

前言:

有些公司不让员工上Q或封掉某些网站,这时候,干着急没办法,只能鄱墙。
如果上网搜代理IP,很少能用,用HTTP-Tunnel Client代理软件,免费的也是经常性的掉线。

正好手头上有N台服务器,如果直接在上面装个CCProxy,也显的太明显了。
于是自己写个代理软件放上去,一来包装一下好伪装,二来又有代理功能,看着挺好。

 

原理解说:

1:创建一个Socket进行本地端口监听-》一个死循环while语句

2:收到消息时,产生一个线程处理->多线程处理并发请求

3:产生一个新的Socket负责转发和接收

4:原来的Socket负责把新接收的消息发送回客户端

 

代码细说

说明:本次示例在控制台程序里运行。

 

一:Program.cs

 

1:简单函数原型

using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Diagnostics;
using  System.Net.Sockets;
using  System.Threading;
namespace  TcpProxy
{
    
///   <summary>
    
///  by 路过秋天
    
///   http://www.cnblogs.com/cyq1162
    
///   </summary>
     class  Program
    {
      
        
static   void  Main( string [] args)
        {
                Listen(
808 ); // 起始监听808和CCProxy一样。
         }
        
static   void  Write( string  msg) // 简化消息输出
        {
            Console.WriteLine(msg);
        }

        
static   void  Listen( int  port) // 开始监听
         {
           
        }
        
static   void  ReListen(TcpListener listener) // 监听失败,需要重新换端口监听
        {
           
        }
    }
}

 

2:开始监听

        static   void  Listen( int  port) // 开始监听
        {
            Write(
" 准备监听端口: "   +  port);
            System.Net.IPAddress ipp 
=  System.Net.IPAddress.Parse( " 0.0.0.0 " ); // 监听本地任意IP
            TcpListener tcplistener  =   new  TcpListener(ipp, port);
            
// 端口复用,xp下可以复用[可抢占IIS80端口],win2003下无效。
            tcplistener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress,  true );
            
try
            {
                tcplistener.Start();
            }
            
catch  (Exception err)
            {
                Write(err.Message);
                Write(
" 该端口已被占用,请更换端口号!!! " );
                ReListen(tcplistener);
// 监听失败,切换端口监听
            }
            
// 下面还有代码,暂时省略

        }

 

3:监听失败,切换端口监听

        static   void  ReListen(TcpListener listener) // 监听失败,需要重新换端口监听
        {
            
if  (listener  !=   null )
            {
                listener.Stop();
                listener 
=   null ;
            }
            Write(
" 请输入监听端口号: " );
            
string  newPort  =  Console.ReadLine();
            
int  port;
            
if  ( int .TryParse(newPort,  out  port))
            {
                Listen(port);
            }
            
else
            {
                ReListen(listener);
            }
        }

 

4:开始监听,进入死循环

        static   void  Listen( int  port) // 开始监听
        {
          
// 上面代码省略......

            Write(
" 成功监听端口: "   +  port);
            Socket socket;
            
while  ( true )
            {
              socket 
=  tcplistener.AcceptSocket(); // 获取传送和接收数据的Scoket实例 
                Proxy proxy  =   new  Proxy(socket); // Proxy类实例化 
              Thread thread  =   new  Thread( new  ThreadStart(proxy.Run)); // 创建线程 
                thread.Start(); // 启动线程 
            }
        }

 

作者:路过秋天

博客:http://cyq1162.cnblogs.com/

 

 

二:Proxy.cs

 

Proxy简单函数原型:

using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Net;
using  System.Net.Sockets;
using  System.IO;

namespace  TcpProxy
{
    
///   <summary>
    
///  by 路过秋天
    
///   http://www.cnblogs.com/cyq1162
    
///   </summary>
     public   class  Proxy
    {
        Socket clientSocket;
// 接收和返回
         byte [] read  =   null ; // 存储来自客户端请求数据包
         byte [] sendBytes  =   null ; // 存储中转请求发送的数据
         byte [] recvBytes  =   null ; // 存储中转请求返回的数据
         bool  isConnect  =   false ;
        
byte [] qqSendBytes = new   byte [ 4096 ]; // QQ发送缓冲
         byte [] qqRecvBytes  =   new   byte [ 4096 ]; // QQ接收缓冲
         int  sendLength  =   0 , recvLength  =   0 ; // 实际发送和接收长度
         public  Proxy(Socket socket) // 初始化
        {
            clientSocket 
=  socket;
            recvBytes 
=   new  Byte[ 1024   *   1024 ];
            clientSocket.ReceiveBufferSize 
=  recvBytes.Length;
            clientSocket.SendBufferSize 
=  recvBytes.Length;
        }
        
        
public   void  Run(){} // 主运行代码
        
// 从请求头里解析出url和端口号
         private   string  GetUrl( string  clientmessage,  ref   int  port){}
        
// 接收客户端的HTTP请求数据
         private   int  ReadMessage( byte [] readByte,  ref  Socket s,  ref  IPAddress ipAddress,  ref   string  host,  ref   int  port){}
        
// 关闭socket
         private   void  CloseSocket(Socket socket){}
        
private   void  CloseSocket(Socket socket,  bool  shutdown){}
        
// QQ代理测试返回
         private   byte [] QQokProxyData(){}
        
// firfox默认会发送一些请求,很烦,所以加过滤
         private   bool  Filter( string  url){ }
        private void Write(string msg)
        {
            System.Console.WriteLine(msg);
        }

    }
}

 

Run主函数

 

A:分解请求头,获取要请求的IP,端口

            #region  获取客户端请求数据
            Write(
" -----------------------------请求开始--------------------------- " );

            read 
=   new   byte [clientSocket.Available];
            IPAddress ipAddress 
=  IPAddress.Any;
            
string  host  =   "" ; // 主机
              int  port  =   80 ; // 端口

             
int  bytes  =  ReadMessage(read,  ref  clientSocket,  ref  ipAddress,  ref  host,  ref  port);
            
if  (bytes  ==   0 )
            {
                Write(
" 读取不到数据! " );
                CloseSocket(clientSocket);
                
return ;
            }
            
#endregion

 

Run函数分解:ReadMessage函数

ReadMessage函数
  // 接收客户端的HTTP请求数据
         private   int  ReadMessage( byte [] readByte,  ref  Socket s,  ref  IPAddress ipAddress,  ref   string  host,  ref   int  port)
        {
            
try
            {

                
int  bytes  =  s.Receive(readByte, readByte.Length,  0 );
                Write(
" 收到原始请求数据: "   +  readByte.Length);
                
string  header  =  Encoding.ASCII.GetString(readByte);


                host 
=  GetUrl(header,  ref  port);
                
if  (Filter(host))
                {
                    Write(
" 系统过滤: "   +  host);
                    
return   0 ;
                }
                Write(header);
                ipAddress 
=  Dns.GetHostAddresses(host)[ 0 ];
                
if  ( ! isConnect)
                {
                    header 
=  header.Replace( " http:// "   +  host,  "" );
                }
                sendBytes 
=  Encoding.ASCII.GetBytes(header);
                System.Threading.Thread.Sleep(
50 );
                Write(
" 转发请求数据: "   +  sendBytes.Length);
                Write(Encoding.ASCII.GetString(sendBytes));
                
return  bytes;
            }
            
catch
            {
                System.Threading.Thread.Sleep(
300 );
                
return   0 ;
            }
        }

 

ReadMessage函数分解:GetUrl

GetUrl函数
        // 从请求头里解析出url和端口号
         private   string  GetUrl( string  clientmessage,  ref   int  port)
        {
            
if  (clientmessage.IndexOf( " CONNECT " !=   - 1 )
            {
                isConnect 
=   true ;
            }
            
int  index1  =  clientmessage.IndexOf( '   ' );
            
int  index2  =  clientmessage.IndexOf( '   ' , index1  +   1 );
            
if  ((index1  ==   - 1 ||  (index2  ==   - 1 ))
            {
                
return   "" ;
            }
            
string  part1  =  clientmessage.Substring(index1  +   1 , index2  -  index1).Trim();
            
string  url  =   string .Empty;
            
if  ( ! part1.Contains( " http:// " ))
            {
                
if  (part1.Substring( 0 1 ==   " / " )
                {
                    part1 
=   " 127.0.0.1 "   +  part1;
                }
                part1 
=   " http:// "   +  part1;
            }
            Uri uri 
=   null ;
            
try
            {
                uri 
=   new  Uri(part1);
            }
            
catch
            {
                
return   "" ;
            }
            url 
=  uri.Host;
            port 
=  uri.Port;
            
return  url;
        }

 

ReadMessage函数分解:Filter

Filter函数
        private   bool  Filter( string  url)
        {
            
switch  (url.ToLower())
            {
                
case   " fffocus.cn " :
                    
return   true ;
            }
            
return   false ;
        }

 

Run函数分解:CloseSocket函数

CloseSocket函数
         private   void  CloseSocket(Socket socket)
        {
            CloseSocket(socket, 
true );
        }
        
private   void  CloseSocket(Socket socket,  bool  shutdown)
        {
            
if  (socket  !=   null )
            {
                
if  (shutdown)
                {
                    socket.Shutdown(SocketShutdown.Both);
                }
                socket.Close();
            }
        }

 

B:创建中转Socket及建立连接

           #region  创建中转Socket及建立连接
            IPEndPoint ipEndpoint 
=   new  IPEndPoint(ipAddress, port);
            Socket IPsocket 
=   new  Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            
try
            {
                IPsocket.Connect(ipEndpoint); Write(
" -----Socket 建立连接! IP地址: "   +  ipAddress  +   " 网址:http:// "   +  host);
            }
            
catch  (Exception err)
            {
                Write(
" 连接失败 : "   +  err.Message);
                Write(
" 退出请求!!! " );
                CloseSocket(IPsocket, 
false );
                
return ;
            }  
            
#endregion

 

 

C:QQ代理测试及网页转发

             if  (isConnect) // QQ链接方式
            {
                
byte [] qqOkData  =  QQokProxyData();
                clientSocket.Send(qqOkData, 
0 , qqOkData.Length,  0 );
            }
            
else // 正常网页,直接转发
            {
                IPsocket.Send(sendBytes, 
0 );
            }

 

函数分解:QQokProxyData

         private   byte [] QQokProxyData()
        {
            
string  data  =   " HTTP/1.0 200 Connection established " ; // 返回建立成功";
             return  System.Text.Encoding.ASCII.GetBytes(data);
        }

 

D:针对QQ需要进行重复来回的发送与接收

QQ转发处理
           #region  QQ发送/接收中转请求
             
int  length  =   0 , count  =   0 ;
            
if  (isConnect)
            {

                System.Threading.Thread.Sleep( 400 ); // 关键时延
                
// 循环发送客户端请求,接收服务器返回
                DateTime start  =  DateTime.Now;
                
while  ( true )
                {
                    
if  (IPsocket.Available  ==   0   &&  clientSocket.Available  ==   0 )
                    {
                        
if  (((TimeSpan)(DateTime.Now  -  start)).TotalMinutes  >   15 )
                        {
                            
break ; // 掉线重拔处理
                        }
                    }
                    
else
                    {
                        start 
=  DateTime.Now;
                    }
                        
                    
                    
try
                    {
                        
while  (clientSocket.Available  !=   0 )
                        {
                            sendLength 
=  clientSocket.Receive(qqSendBytes, qqSendBytes.Length,  0 );
                            IPsocket.Send(qqSendBytes, sendLength, 
0 );
                            Write(
" 发送字节数:  "   +  sendLength.ToString());
                        }

                        System.Threading.Thread.Sleep(
500 );
                        
while  (IPsocket.Available  !=   0 )
                        {
                            recvLength 
=  IPsocket.Receive(qqRecvBytes, qqRecvBytes.Length,  0 );
                            clientSocket.Send(qqRecvBytes, recvLength, 
0 );
                            Write(
" 接收字节数:  "   +  recvLength.ToString());
                        }
                    }
                    
catch
                    {
                    }
                }
            }
            
else
            {
                
try
                {
                    
do
                    {
                        length 
=  IPsocket.Receive(recvBytes, count, IPsocket.Available,  0 );
                        count 
=  count  +  length;
                        Write(
" 接收转发请求返回的数据中... "   +  length);
                        System.Threading.Thread.Sleep(
200 ); // 关键点,请求太快数据接收不全
                    }
                    
while  (IPsocket.Available  >   0 );
                    clientSocket.Send(recvBytes, 
0 , count,  0 );
                }
                
catch (Exception err)
                {
                    Write(err.Message);
                }
            }
            
#endregion

 

E:结束请求,关闭客户端Socket

             #region  结束请求,关闭客户端Socket
            Write(
" 接收完成。返回客户端数据... "   +  count);
            CloseSocket(IPsocket);
            CloseSocket(clientSocket);
            recvBytes 
=   null ;
            Write(
" 本次请求完成,已关闭连接... " );
            Write(
" -----------------------------请求结束--------------------------- " );
            
#endregion

 

结言:

本QQ代理软件在服务器上运行长达三个多月,使用过程未发现异常退出情况。当然前提就我一个人在用了 ~ 哈哈 ~

 

 

附以前写的几篇文章:

1:简单实现Http代理工具

2:简单实现Http代理工具--端口复用与QQ代理

3:简单实现Http代理工具--完善支持QQ代理

4:C# 控制台程序 不显示在任务栏 只在进程中显示

 

看本篇的时候也请支持一下我的开源框架:CYQ.Data 轻量数据层之路 框架开源系列 索引

 

你可能感兴趣的:(http)