Posted on 2009-01-04 22:47 
S.l.e!ep.¢% 阅读(4003) 
评论(0)  编辑 收藏 引用  
			 
			
		 
		
		有很多公司不能直接和Internet相连,必须通过代理和www连接,浏览、下载资料。
		代理服务器支持的协议也有所不同,有支持Sock、HTTP代理的这样我们做的客户端软件就需要支持这些代理,使用户能够通过这些代理透过防火墙和外网相连,一般Sock分为Sock4和Sock5,这里我们只实现Sock5协议。
		
		下面说明一下实现的方法。
		代理的实现方法实际就是Socket编程,只要通讯时遵循它们的协议就可以。首先理解它们的协议是关键。
		
				示意图:
		
		
		
		1.1    Sock5协议实现
		1.1.1        Sock5协议内容
		1.  客户端首先要与代理服务器连接,连接后要向代理发送版本号、认证方法、方法选择格式如下:
		
				版本号(1字节) |  供选择的认证方法(1字节)  |  方法序列(1-255个字节长度)
		如果你支持的版本为SOCK5那么版本号就为0x05;可供选择的方法是指你的协议支持几种认证方式,因为我们实现只支持一种所以就填0x01,如果你是两种就写0x02;认证方法序列包括(0x00为不需认证、0x02为需要用户名和密码认证,通常是这两种,如果想了解更多请参看Rfc1928)。
		这样组合你的报文应该为:0x05 0x01 0x00
		如果需要认证那么为:0x05 0x01 0x02
		2.  代理接收到客户端的请求,会向客户端返回信息,格式为:
		
				版本号 | 服务器选定的方法
		如果服务器支持验证方式,返回的报文为:
		0x05 0x02
		3.  接下来根据服务器的验证方式,发送验证信息了,报文格式为:
		0x01 | 用户名长度(1字节)| 用户名(长度根据用户名长度域指定) | 口令长度(1字节) | 口令(长度由口令长度域指定)
		4.  服务器接收信息后进行验证,返回如下格式:
		0x01 | 验证结果标志
		验证结果标志:0x00表示验证成功,其他值均为错误码
		1.1.2        Sock5协议实现
		根据上面所述的步骤和协议内容,就可以实现SOCK5代理了。下面我给出我写的实现SOCKT5代理的函数代码和注释,供参考(delphi6+win2K+sp4调试通过,代理服务器用的是CCProxy6.0)
		
				
						
								 function TBZSock5ProxyApi.SetProxy(ASocket: Pointer): Integer;
								function TBZSock5ProxyApi.SetProxy(ASocket: Pointer): Integer;
 var
var
 proxyUser, proxyPwd: String;
  proxyUser, proxyPwd: String;
 bIsValid: Boolean;
  bIsValid: Boolean;
 sock: 
								^
								TSocket;
  sock: 
								^
								TSocket;
 sockServer: TSockAddrIn;
  sockServer: TSockAddrIn;
 command: array [
								0
								..
								9
								] of Byte;
  command: array [
								0
								..
								9
								] of Byte;
 re, len, ulen, plen: Integer;
  re, len, ulen, plen: Integer;
 //
								  buffer: PByte;
								//
								  buffer: PByte;
								
										
										 buffer: array [
								0
								..
								1023
								] of Byte;
								
								  buffer: array [
								0
								..
								1023
								] of Byte;
 begin
begin
 sock :
								=
								 ASocket;
  sock :
								=
								 ASocket;
 if
								 FProxyParam.GetServer 
								=
								 
								''
								 then
  
								if
								 FProxyParam.GetServer 
								=
								 
								''
								 then
 begin
  begin
 Result :
								=
								 
								0
								;
    Result :
								=
								 
								0
								;
 Exit;
    Exit;
 end
  end
 else
  
								else
								
										
										 begin
  begin
 Result :
								=
								 
								0
								;
    Result :
								=
								 
								0
								;
 sock
								^
								 :
								=
								 socket(AF_INET, SOCK_STREAM, 
								0
								);
    sock
								^
								 :
								=
								 socket(AF_INET, SOCK_STREAM, 
								0
								);
 if
								 sock
								^
								 
								=
								 INVALID_SOCKET then
    
								if
								 sock
								^
								 
								=
								 INVALID_SOCKET then
 begin
    begin
 Result :
								=
								 
								1
								;
      Result :
								=
								 
								1
								;
 Exit;
      Exit;
 end;
    end;
 
 
 sockServer.sin_family :
								=
								 AF_INET;
    sockServer.sin_family :
								=
								 AF_INET;
 sockServer.sin_port :
								=
								 htons(FProxyParam.GetPort);    
								//
								将整形数变为网络字节流
    sockServer.sin_port :
								=
								 htons(FProxyParam.GetPort);    
								//
								将整形数变为网络字节流
								
										
										 sockServer.sin_addr.S_addr :
								=
								 inet_addr(PChar(FProxyParam.GetServer));
								
								    sockServer.sin_addr.S_addr :
								=
								 inet_addr(PChar(FProxyParam.GetServer));
 
 
 //
								连接远程主机
    
								//
								连接远程主机
								
										
										 if
								 WinSock.connect(sock
								^
								, sockServer, SizeOf(sockServer)) 
								<>
								 
								0
								 then
								
								    
								if
								 WinSock.connect(sock
								^
								, sockServer, SizeOf(sockServer)) 
								<>
								 
								0
								 then
 begin
    begin
 Result :
								=
								 
								1
								;
      Result :
								=
								 
								1
								;
 Exit;
      Exit;
 end;
    end;
 
 
 bIsValid :
								=
								 FProxyParam.GetProxyValid;
    bIsValid :
								=
								 FProxyParam.GetProxyValid;
 
 
 //
								发送SOCK5协议指令
    
								//
								发送SOCK5协议指令
								
										
										 FillChar(command, 
								10
								, 
								0
								);
								
								    FillChar(command, 
								10
								, 
								0
								);
 command[
								0
								] :
								=
								 
								5
								;
    command[
								0
								] :
								=
								 
								5
								;
 if
								 bIsValid then
    
								if
								 bIsValid then
 command[
								1
								] :
								=
								 
								2
      command[
								1
								] :
								=
								 
								2
								
										
										 else
    
								else
								
										
										 command[
								1
								] :
								=
								 
								1
								;
      command[
								1
								] :
								=
								 
								1
								;
 if
								 bIsValid then
    
								if
								 bIsValid then
 command[
								2
								] :
								=
								 
								2
      command[
								2
								] :
								=
								 
								2
								
										
										 else
    
								else
								
										
										 command[
								2
								] :
								=
								 
								0
								;
      command[
								2
								] :
								=
								 
								0
								;
 
 
 //
								发送登陆指令
    
								//
								发送登陆指令
								
										
										 if
								 bIsValid then
								
								    
								if
								 bIsValid then
 re :
								=
								 WinSock.send(sock
								^
								, command, 
								4
								, 
								0
								)
      re :
								=
								 WinSock.send(sock
								^
								, command, 
								4
								, 
								0
								)
 else
    
								else
								
										
										 re :
								=
								 WinSock.send(sock
								^
								, command, 
								3
								, 
								0
								);
      re :
								=
								 WinSock.send(sock
								^
								, command, 
								3
								, 
								0
								);
 if
								 re 
								=
								 SOCKET_ERROR then
    
								if
								 re 
								=
								 SOCKET_ERROR then
 begin
    begin
 Result :
								=
								 
								1
								;
      Result :
								=
								 
								1
								;
 Exit;
      Exit;
 end;
    end;
 
 
 //
								接收返回的消息
    
								//
								接收返回的消息
								
										
										 fillchar(command, 
								10
								, 
								0
								);                         
								//
								接收前用0再次填充
								
								    fillchar(command, 
								10
								, 
								0
								);                         
								//
								接收前用0再次填充
								
										
										 re :
								=
								 WinSock.recv(sock
								^
								, command, 
								2
								, 
								0
								);
								
								    re :
								=
								 WinSock.recv(sock
								^
								, command, 
								2
								, 
								0
								);
 if
								 re 
								<>
								 
								2
								 then
    
								if
								 re 
								<>
								 
								2
								 then
 begin
    begin
 Result :
								=
								 
								1
								;
      Result :
								=
								 
								1
								;
 Exit;
      Exit;
 end;
    end;
 if
								 command[
								1
								]
								=
								$FF then
    
								if
								 command[
								1
								]
								=
								$FF then
 begin
    begin
 Result :
								=
								 
								1
								;
      Result :
								=
								 
								1
								;
 Exit;
      Exit;
 end;
    end;
 
 
 if
								 (not bIsValid) and (command[
								1
								]
								=
								0
								) then
    
								if
								 (not bIsValid) and (command[
								1
								]
								=
								0
								) then
 begin
    begin
 Exit;
      Exit;
 end;
    end;
 
 
 proxyUser :
								=
								 FProxyParam.GetUsername;
    proxyUser :
								=
								 FProxyParam.GetUsername;
 proxyPwd :
								=
								 FProxyParam.GetPassword;
    proxyPwd :
								=
								 FProxyParam.GetPassword;
 
 
 if
								 command[
								1
								] 
								<>
								 
								0
								 then
    
								if
								 command[
								1
								] 
								<>
								 
								0
								 then
 begin
    begin
 if
								 command[
								1
								] 
								<>
								 
								2
								 then
      
								if
								 command[
								1
								] 
								<>
								 
								2
								 then
 begin
      begin
 Result :
								=
								 
								1
								;
        Result :
								=
								 
								1
								;
 Exit;
        Exit;
 end;
      end;
 if
								 bIsValid then
      
								if
								 bIsValid then
 begin
      begin
 ulen :
								=
								 Length(proxyUser);
        ulen :
								=
								 Length(proxyUser);
 plen :
								=
								 Length(proxyPwd);
        plen :
								=
								 Length(proxyPwd);
 len :
								=
								 
								3
								 
								+
								 ulen 
								+
								 plen;
        len :
								=
								 
								3
								 
								+
								 ulen 
								+
								 plen;
 fillchar(buffer, 
								1024
								, 
								0
								);
        fillchar(buffer, 
								1024
								, 
								0
								);
 
 
 buffer[
								0
								] :
								=
								 
								5
								;
        buffer[
								0
								] :
								=
								 
								5
								;
 buffer[
								1
								] :
								=
								 ulen;
        buffer[
								1
								] :
								=
								 ulen;
 StrPCopy(@buffer[
								2
								], proxyuser);
        StrPCopy(@buffer[
								2
								], proxyuser);
 buffer[
								2
								 
								+
								 ulen] :
								=
								 plen;
        buffer[
								2
								 
								+
								 ulen] :
								=
								 plen;
 StrPCopy(@buffer[
								2
								 
								+
								 ulen 
								+
								 
								1
								], proxyPwd);
        StrPCopy(@buffer[
								2
								 
								+
								 ulen 
								+
								 
								1
								], proxyPwd);
 
 
 //
								发送验证信息
        
								//
								发送验证信息
								
										
										 re :
								=
								 send(sock
								^
								, buffer, len, 
								0
								);
								
								        re :
								=
								 send(sock
								^
								, buffer, len, 
								0
								);
 if
								 re 
								=
								 SOCKET_ERROR then
        
								if
								 re 
								=
								 SOCKET_ERROR then
 begin
        begin
 Result :
								=
								 
								1
								;
          Result :
								=
								 
								1
								;
 Exit;
          Exit;
 end;
        end;
 
 
 //
								接收验证返回信息
        
								//
								接收验证返回信息
								
										
										 fillchar(command, 
								10
								, 
								0
								);
								
								        fillchar(command, 
								10
								, 
								0
								);
 re :
								=
								 recv(sock
								^
								, command, 
								2
								, 
								0
								);
        re :
								=
								 recv(sock
								^
								, command, 
								2
								, 
								0
								);
 if
								 ((re
								<>
								2
								) or ((command[
								0
								]
								<>
								1
								) and (command[
								1
								]
								<>
								0
								 ))) then
        
								if
								 ((re
								<>
								2
								) or ((command[
								0
								]
								<>
								1
								) and (command[
								1
								]
								<>
								0
								 ))) then
 begin
        begin
 Result :
								=
								 
								1
								;
          Result :
								=
								 
								1
								;
 Exit;
          Exit;
 end;
        end;
 end   
								//
								if bisValid
      end   
								//
								if bisValid
								
										
										 else
								
								      
								else
								
										
										 begin
      begin
 Result :
								=
								 
								1
								;
        Result :
								=
								 
								1
								;
 Exit;
        Exit;
 end;                     
								//
      end;                     
								//
 end; 
								//
								 if command[1]<>0
								    end; 
								//
								 if command[1]<>0
								
										
										 end;  
								//
								end first if
								
								  end;  
								//
								end first if
								
										
										 
								
								 
 end;
end;
 上面的函数中有一个FproxyParam变量,它是代理参数的值对象,声明如下:
上面的函数中有一个FproxyParam变量,它是代理参数的值对象,声明如下:
 TProxyParam 
								=
								 
								class
  TProxyParam 
								=
								 
								class
								
										
										 private
  
								private
								
										
										 FUsername,        
								//
								代理验证用户名
    FUsername,        
								//
								代理验证用户名
								
										
										 FPassword,        
								//
								代理验证密码
								
								    FPassword,        
								//
								代理验证密码
								
										
										 FServer: String;    
								//
								代理服务器地址
								
								    FServer: String;    
								//
								代理服务器地址
								
										
										 FPort: Integer;     
								//
								代理服务器端口号
								
								    FPort: Integer;     
								//
								代理服务器端口号
								
										
										 FIsValid: Boolean;  
								//
								是否验证     如果代理服务器是验证的,那么此值应该为true
								
								    FIsValid: Boolean;  
								//
								是否验证     如果代理服务器是验证的,那么此值应该为true
								
										
										 
								
								 
 procedure Clear;
    procedure Clear;
 public
  
								public
								
										
										 constructor Create;
    constructor Create;
 procedure SetUsername(AUsername: String);
    procedure SetUsername(AUsername: String);
 procedure SetPassword(APassword: String);
    procedure SetPassword(APassword: String);
 procedure SetServer(AServer: String);
    procedure SetServer(AServer: String);
 procedure SetPort(APort: Integer);
    procedure SetPort(APort: Integer);
 procedure SetProxyValid(AValid: Boolean);
    procedure SetProxyValid(AValid: Boolean);
 
 
 function GetUsername: String;
    function GetUsername: String;
 function GetPassword: String;
    function GetPassword: String;
 function GetServer: String;
    function GetServer: String;
 function GetPort: Integer;
    function GetPort: Integer;
 function GetProxyValid: Boolean;
    function GetProxyValid: Boolean;
 end;
  end;
 
						 
				 
		 
		使用此函数:
		假设已经创建了一个ClientSocket1(TclientSocket对象),它的IP和Port设置为代理服务器的IP和Port,那么就有下面的代码:
		
				
						
								 ......
								......
 Var
Var
 Re:Integer;
Re:Integer;
 Begin
Begin
 Re:
								=
								SetProxy(@(ClientSocket.Socket.SocketHandle)) ;  
								//
								如果验证成功,那么就可以用返回的//socket进行相应的通讯
Re:
								=
								SetProxy(@(ClientSocket.Socket.SocketHandle)) ;  
								//
								如果验证成功,那么就可以用返回的//socket进行相应的通讯
								
										
										 If Re
								=
								0
								 then
								
								If Re
								=
								0
								 then 
 Begn
Begn
 ShowMessage(‘Sock5 is ok’);         
								//
								代理服务器就会有连接的数据流显示
  ShowMessage(‘Sock5 is ok’);         
								//
								代理服务器就会有连接的数据流显示
								
										
										 End
								
								End
 Else begin
Else begin
 ShowMessage(‘Sock5 is error’);
  ShowMessage(‘Sock5 is error’);
 End;
End;
 End;
End;
 ……
……
 
						 
				 
		 
		上面的例子如果设置代理成功,那么代理服务器就会有连接的数据流显示。表示与代理服务器有数据交换,如果没有成功则务数据流显示。
		 
		 
		                                                               李孟岩
		                                                                      mengyan_yl@sohu.com
		                                                                                    2005-07-05实现Sock5代理