首页 实地培训 VIP专区 文章中心 下载中心 精品动画 安全服务 安全产品 企业文化
技术论坛
 -->开创网络首先基于以学员为中心的人性化教学方式。以案例式教学、互动式教学为主线。
 
  当前位置: 首页 > VIP专区 > VIP文章 > 工具使用 >
 
 
粗糙的C#版HTTP代理
发布者ID:3 作者: 发布时间:2008-08-05 08:51:51 来源: 点击:
 
By;云舒

前些时候一个无锡人在我博客留言,说我读再多的书,也改变不了我是个程序员的本质。或许它觉得它是在鄙视我,不过我到觉得它是在赞扬我——我真的算不上是个程序员,写代码的能力实在是太烂了——不过我会努力的。

说说这段代码的问题吧。首先是字符串切割的问题,要将客户端提交过来的GET,POST等原始请求切割,分离出主机名,端口,URL等数据。这里用正则匹配是最好的,遗憾的是我不擅长此道,所以使用了手动切割的办法,很笨重繁琐,但是毕竟它工作得很好。第二个问题是Keep-Alive的问题,这里我没有处理好。最开始我在http proxy里面修改客户端请求,强行将keep-alive修改为close,但是发现在某些站点的时候会出错。于是使用了类似select的方法读取数据,直到超时关闭两端的连接。我猜测,这里如果解析content-length会更好,但是略微繁琐了点,还是等我仔细阅读下RFC再看怎么修改吧。第三个是CONNECT方法的问题,这个到很简单,转发数据就行了,因此是这个代码中写得最好的一部分,用来登陆QQ还是不错的。

说实话,我不喜欢HTTP这种太宽松的协议,感觉灵活得让我难以把握。直接看代码吧,我加了很多debug信息,真的要用就去掉好了。调用这个类很简单,看main函数的实现就好了。为了方便贴代码,我写的时候就把三个类写到一个文件里面去了。顺便要说的是,虽然有类,但是没有任何面向对象的东西——这也再次证明,其实我算不上一个程序员,最多是个代码爱好者。
 
 using System;
 
 using System.Net;
 
 using System.Net.Sockets;
 
 using System.Text;
 
 using System.IO;
 
 using System.Threading;
 
 using System.Collections;
 
 
 
 namespace HttpProxy
 
 {
 
 public class HttpProxy
 
 {
 
 int ProxyPort;
 
 
 
 /// <summary>
 
 /// 代理服务器入口类构造函数
 
 /// </summary>
 
 /// <param name="Port">Http Proxy监听的端口</param>
 
 public HttpProxy( int Port)
 
 {
 
 ProxyPort = Port;
 
 }
 
 
 
 /// <summary>
 
 /// 启动Http代理服务器
 
 /// </summary>
 
 public void Start( )
 
 {
 
 TcpListener tcplistener = null;
 
 try
 
 {
 
 // 开始监听端口
 
 tcplistener = new TcpListener(Dns.GetHostAddresses(Dns.GetHostName())[0], ProxyPort);
 
 tcplistener.Start();
 
 Console.WriteLine("侦听端口号: " + ProxyPort.ToString());
 
 }
 
 catch (Exception e)
 
 {
 
 Console.WriteLine("启动代理服务器失败: " + e.Message);
 
 }
 
 
 
 while (true)
 
 {
 
 try
 
 {
 
 // 接受客户端连接
 
 Socket socket = tcplistener.AcceptSocket();
 
 
 
 HttpSession Session = new HttpSession(socket);
 
 
 
 // 启动新线程,处理连接
 
 Thread thread = new Thread(new ThreadStart(Session.Start));
 
 thread.Start();
 
 }
 
 catch( Exception e )
 
 {
 
 Console.WriteLine("接受客户端连接异常: " + e.Message );
 
 }
 
 }
 
 
 
 }
 
 }
 
 
 
 public class HttpSession
 
 {
 
 // 客户端socket
 
 Socket ClientSocket;
 
 
 
 // 设定编码
 
 Encoding ASCII = Encoding.ASCII;
 
 
 
 /// <summary>
 
 /// 构造函数
 
 /// </summary>
 
 /// <param name="socket">客户端socket</param>
 
 public HttpSession(Socket socket)
 
 {
 
 this.ClientSocket = socket;
 
 }
 
 
 
 public void Start()
 
 {
 
 // 客户端缓冲区,读取客户端命令
 
 Byte[] ReadBuff = new byte[1024 * 10];
 
 
 
 try
 
 {
 
 int Length = ClientSocket.Receive(ReadBuff);
 
 
 
 // 没有读到数据
 
 if (0 == Length)
 
 {
 
 Console.WriteLine("从客户端读取命令错误");
 
 ClientSocket.Shutdown(SocketShutdown.Both);
 
 ClientSocket.Close();
 
 
 
 return;
 
 }
 
 }
 
 // 读取出现异常
 
 catch (Exception e)
 
 {
 
 Console.WriteLine("读取客户端异常: " + e.Message);
 
 }
 
 
 
 // 来自客户端的HTTP请求字符串
 
 string ClientMsg = ASCII.GetString(ReadBuff);
 
 
 
 // 根据rnrn截取请求行
 
 string Line = ClientMsg.Substring(0, ClientMsg.IndexOf("rn"));
 
 string[] CmdArray = Line.Split(' ');
 
 
 
 // GET http://www.test.com:80/index.php HTTP/1
 
 // CONNECT www.test.com:443 HTTP/1
 
 string Cmd = CmdArray[0];
 
 string RawUrl = CmdArray[1];
 
 
 
 Console.WriteLine("原始请求: ", Line);
 
 
 
 // CONNECT请求
 
 if (Cmd == "CONNECT")
 
 {
 
 DoConnect(RawUrl);
 
 }
 
 // GET,POST和其他
 
 else
 
 {
 
 DoOther(RawUrl, ClientMsg);
 
 }
 
 }
 
 
 
 /// <summary>
 
 /// 处理CONNECT命令,此处作用是支持QQ,MSN,以及多级代理串联等
 
 /// </summary>
 
 /// <param name="RawUrl"></param>
 
 private void DoConnect( string RawUrl )
 
 {
 
 string[] Args = RawUrl.Split( ':' );
 
 
 
 string Host = Args[0];
 
 int Port = int.Parse(Args[1]);
 
 
 
 Socket ServerSocket = null;
 
 try
 
 {
 
 IPAddress[] IpList = Dns.GetHostEntry(Host).AddressList;
 
 Console.WriteLine("尝试连接:", IpList[0], Port);
 
 
 
 ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 
 ServerSocket.Connect(IpList[0], Port);
 
 }
 
 catch (Exception e)
 
 {
 
 Console.WriteLine("连接真实服务器异常: " + e.Message);
 
 }
 
 
 
 // 连接真实服务器成功
 
 if (ServerSocket.Connected)
 
 {
 
 ClientSocket.Send( ASCII.GetBytes("HTTP/0 200 Connection establishedrnrn") );
 
 }
 
 else
 
 {
 
 ClientSocket.Shutdown(SocketShutdown.Both);
 
 ClientSocket.Close();
 
 }
 
 
 
 // 开始转发数据
 
 ForwardTcpData(ClientSocket, ServerSocket);
 
 }
 
 
 
 /// <summary>
 
 /// 处理GET,POST等命令。使用了POLL,在代理服务器中强制去掉了Keep-Alive能力
 
 /// </summary>
 
 /// <param name="RawUrl"></param>
 
 /// <param name="ClientMsg"></param>
 
 public void DoOther(string RawUrl, string ClientMsg)
 
 {
 
 RawUrl = RawUrl.Substring(0 + "http://".Length);
 
 
 
 int Port;
 
 string Host;
 
 string Url;
 
 
 
 // 下面是分割处理请求,此处应该用正则匹配,不过我不擅长,因此手动切割,—_—!
 
 int index1 = RawUrl.IndexOf(':');
 
 // 没有端口
 
 if (index1 == -1)
 
 {
 
 Port = 80;
 
 
 
 int index2 = RawUrl.IndexOf('/');
 
 // 没有目录
 
 if (index2 == -1)
 
 {
 
 Host = RawUrl;
 
 Url = "/";
 
 }
 
 else
 
 {
 
 Host = RawUrl.Substring(0, index2);
 
 Url = RawUrl.Substring(index2);
 
 }
 
 }
 
 else
 
 {
 
 int index2 = RawUrl.IndexOf('/');
 
 // 没有目录
 
 if (index2 == -1)
 
 {
 
 Host = RawUrl.Substring(0, index1);
 
 Port = IntParse(RawUrl.Substring(index1 + 1));
 
 Url = "/";
 
 }
 
 else
 
 {
 
 // /出现在:之前,则说明:后面的不是端口
 
 if (index2 < index1)
 
 {
 
 Host = RawUrl.Substring(0, index2);
 
 Port = 80;
 
 }
 
 else
 
 {
 
 Host = RawUrl.Substring(0, index1);
 
 Port = IntParse(RawUrl.Substring(index1 + 1, index2 - index1 - 1));
 
 }
 
 Url = RawUrl.Substring(index2);
 
 }
 
 }
 
 
 
 Console.WriteLine("Host is:, Port is:, Url is:", Host, Port, Url);
 
 
 
 IPAddress[] address = null;
 
 try
 
 {
 
 IPHostEntry IPHost = Dns.GetHostEntry(Host);
 
 address = IPHost.AddressList;
 
 
 
 Console.WriteLine("Web服务器IP地址: " + address[0]);
 
 }
 
 catch( Exception e )
 
 {
 
 Console.WriteLine( "解析服务器地址异常: " + e.Message );
 
 }
 
 
 
 Socket IPsocket = null;
 
 try
 
 {
 
 // 连接到真实WEB服务器
 
 IPEndPoint ipEndpoint = new IPEndPoint(address[0], Port);
 
 IPsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 
 IPsocket.Connect(ipEndpoint);
 
 
 
 // 对WEB服务器端传送HTTP请求命令,将原始HTTP请求中HTTP PROXY部分包装去掉
 
 string ReqData = ClientMsg;
 
 
 
 // 改写头中的URL,从http://www.test.com/index.php改为/index.php
 
 ReqData = ReqData.Replace("http://"+RawUrl, Url);
 
 
 
 // 按照rn切分HTTP头
 
 string[] ReqArray = ReqData.Split(new string[1]{"rn"}, StringSplitOptions.None);
 
 ReqData = "";
 
 
 
 // 改写Keep-Alive等字段
 
 for (int index = 0; index < ReqArray.Length; index++)
 
 {
 
 /*
 
 if (ReqArray[index].StartsWith("Accept-Encoding:"))
 
 {
 
 ReqArray[index] = "Accept-Encoding: deflate";
 
 }
 
 */
 
 if (ReqArray[index].StartsWith("Proxy-Connection:"))
 
 {
 
 ReqArray[index] = ReqArray[index].Replace("Proxy-Connection:", "Connection:");
 
 //ReqArray[index] = "Connection: close";
 
 }
 
 /*
 
 else if (ReqArray[index].StartsWith("Keep-Alive:"))
 
 {
 
 ReqArray[index] = "";
 
 }
 
 */
 
 // 修改后的字段组合成请求
 
 if( ReqArray[index] != "" )
 
 {
 
 ReqData = ReqData + ReqArray[index] + "rn";
 
 }
 
 }
 
 ReqData = ReqData.Trim();
 
 byte[] SendBuff = ASCII.GetBytes(ReqData);
 
 IPsocket.Send(SendBuff);
 
 }
 
 catch( Exception e )
 
 {
 
 Console.WriteLine( "发送请求到服务器异常: " + e.Message );
 
 }
 
 
 
 // 使用Poll来判断完成,某些站点会出问题
 
 while (true)
 
 {
 
 Byte[] RecvBuff = new byte[1024 * 20];
 
 
 
 try
 
 {
 
 if( !IPsocket.Poll( 15 * 1000 * 1000, SelectMode.SelectRead ) )
 
 {
 
 Console.WriteLine("HTTP超时,关闭连接");
 
 break;
 
 }
 
 }
 
 catch (Exception e)
 
 {
 
 Console.WriteLine("Poll: " + e.Message);
 
 break;
 
 }
 
 
 
 int Length = 0;
 
 try
 
 {
 
 Length = IPsocket.Receive(RecvBuff);
 
 
 
 if (0 == Length)
 
 {
 
 Console.WriteLine("服务端关闭");
 
 break;
 
 }
 
 Console.WriteLine("从服务端收到字节", Length);
 
 }
 
 catch (Exception e)
 
 {
 
 Console.WriteLine("Recv: " + e.Message);
 
 break;
 
 }
 
 try
 
 {
 
 Length = ClientSocket.Send(RecvBuff, Length, 0);
 
 Console.WriteLine("发送字节到客户端", Length);
 
 }
 
 catch (Exception e)
 
 {
 
 Console.WriteLine("Send: " + e.Message);
 
 }
 
 }
 
 
 
 /*
 
 // 根据接收字节数来判断完成,某些站点会出问题
 
 try
 
 {
 
 while (true)
 
 {
 
 Byte[] RecvBuff = new byte[1024 * 10];
 
 int Length = IPsocket.Receive(RecvBuff);
 
 
 
 if (Length <= 0)
 
 {
 
 Console.WriteLine("从服务端接收数据完成");
 
 break;
 
 }
 
 Console.WriteLine("从服务端收到字节", Length);
 
 
 
 Length = ClientSocket.Send(RecvBuff, Length, 0);
 
 Console.WriteLine("发送字节到客户端", Length);
 
 }
 
 }
 
 catch (Exception e)
 
 {
 
 Console.WriteLine(e.Message);
 
 }
 
 */
 
 try
 
 {
 
 ClientSocket.Shutdown(SocketShutdown.Both);
 
 ClientSocket.Close();
 
 IPsocket.Shutdown(SocketShutdown.Both);
 
 IPsocket.Close();
 
 }
 
 catch (Exception e)
 
 {
 
 //Console.WriteLine(e.Message);
 
 }
 
 }
 
 
 
 /// <summary>
 
 /// 在客户端和服务器之间中转数据
 
 /// </summary>
 
 /// <param name="client">客户端socket</param>
 
 /// <param name="server">服务端socket</param>
 
 private void ForwardTcpData(Socket client, Socket server)
 
 {
 
 ArrayList ReadList = new ArrayList(2);
 
 
 
 while (true)
 
 {
 
 ReadList.Clear();
 
 ReadList.Add(client);
 
 ReadList.Add(server);
 
 
 
 try
 
 {
 
 Socket.Select(ReadList, null, null, 1 * 1000 * 1000);
 
 }
 
 catch (SocketException e)
 
 {
 
 Console.WriteLine("Select error: " + e.Message);
 
 break;
 
 }
 
 
 
 // 超时
 
 if (ReadList.Count == 0)
 
 {
 
 //Console.WriteLine("Time out");
 
 continue;
 
 }
 
 
 
 // 客户端可读
 
 if (ReadList.Contains(client))
 
 {
 
 byte[] Recv = new byte[1024 * 10];
 
 int Length = 0;
 
 
 
 try
 
 {
 
 Length = client.Receive(Recv, Recv.Length, 0);
 
 if (Length == 0)
 
 {
 
 Console.WriteLine("Client is disconnect.");
 
 break;
 
 }
 
 Console.WriteLine(" Recv bytes from client", Length);
 
 }
 
 catch (Exception e)
 
 {
 
 Console.WriteLine("Read from client error: " + e.Message);
 
 break;
 
 }
 
 
 
 try
 
 {
 
 Length = server.Send(Recv, Length, 0);
 
 Console.WriteLine(" Write bytes to server", Length);
 
 }
 
 catch (Exception e)
 
 {
 
 Console.WriteLine("Write data to server error: " + e.Message);
 
 break;
 
 }
 
 }
 
 
 
 // 真实服务端可读
 
 if (ReadList.Contains(server))
 
 {
 
 byte[] Recv = new byte[1024 * 10];
 
 int Length = 0;
 
 
 
 try
 
 {
 
 Length = server.Receive(Recv, Recv.Length, 0);
 
 if (Length == 0)
 
 {
 
 Console.WriteLine("Server is disconnect");
 
 break;
 
 }
 
 Console.WriteLine("Recv bytes from server", Length);
 
 }
 
 catch (Exception e)
 
 {
 
 Console.WriteLine("Read from server error: " + e.Message);
 
 break;
 
 }
 
 
 
 try
 
 {
 
 Length = client.Send(Recv, Length, 0);
 
 Console.WriteLine(" Write bytes to client", Length);
 
 }
 
 catch (Exception e)
 
 {
 
 Console.WriteLine("Write data to client error: " + e.Message);
 
 break;
 
 }
 
 }
 
 }
 
 
 
 try
 
 {
 
 client.Shutdown(SocketShutdown.Both);
 
 server.Shutdown(SocketShutdown.Both);
 
 client.Close();
 
 server.Close();
 
 }
 
 catch (Exception e)
 
 {
 
 Console.WriteLine(e.Message);
 
 }
 
 finally
 
 {
 
 Console.WriteLine("转发完毕");
 
 }
 
 }
 
 }
 
 
 
 public class MainClass
 
 {
 
 
 
 public static void Main()
 
 {
 
 const int Port = 8080;
 
 
 
 HttpProxy Proxy = new HttpProxy(Port);
 
 Proxy.Start();
 
 }
 
 }
 
 }


 
 
 
 
   
 
   
     
  • 课程体系
  • 课堂实景
  • 培训课程
  • 学费介绍
  • 巧用mstsc命令登录到console的方法
  • 课程介绍
  • 团队介绍
  • Linkideo免费Vpn
  • 就业前景
  • 汇款方式
  •  
     
     
    无标题文档
    一流开创网一流开创网一流开创网一流开创网一流开创网一流开创网一流开创网
      关于我们 | 联系方法 | 招聘信息 | 加入会员 | 诚征代理 | 广告服务 | 欢迎投稿 | 友情链接  
      版权所有:吉林省开创科技信息有限公司  
      服务热线:0431-84714442
    Copyright 2001 - 2007 All Rights Reserved