• <label id="pxtpz"><meter id="pxtpz"></meter></label>
      1. <span id="pxtpz"><optgroup id="pxtpz"></optgroup></span>

        當前位置:雨林木風下載站 > 技術開發教程 > 詳細頁面

        用Delphi設計自己的代理服務器

        用Delphi設計自己的代理服務器

        更新時間:2019-07-04 文章作者:未知 信息來源:網絡 閱讀次數:

            筆者在編寫一個上網計費軟件時,涉及到如何對局域網中各工作站上網計費問題。一般來講,這些工作站通過代理服務器上網,而采用現成的代理服務器軟件時,由于代理服務器軟件是封閉的系統,很難編寫程序獲取實時的上網計時信息。因此,考慮是否能編寫自己的代理服務器,一方面解決群體上網,另一方面又解決上網的計費問題呢?
            經過實驗性編程,終于圓滿地解決了該問題。現寫出來,與各位同行分享。

        1、 思路
        當前流行的瀏覽器的系統選項中有一個參數,即“通過代理服務器連接”,經過編程測
        試,當局域網中一臺工作站指定了該屬性,再發出Internet請求時,請求數據將發送到所指定的代理服務器上,以下為請求數據包示例:
                         GET http://home.microsoft.com/intl/cn/ HTTP/1.0
                         Accept: */*
                         Accept-Language: zh-cn
                         Accept-Encoding: gzip, deflate
                         User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT)
                         Host: home.microsoft.com
                         Proxy-Connection: Keep-Alive
        其中第一行為目標URL及相關方法、協議,“Host”行指定了目標主機的地址。
        由此知道了代理服務的過程:接收被代理端的請求、連接真正的主機、接收主機返回的數據、將接收數據發送到被代理端。
        為此可編寫一個簡單的程序,完成上述網絡通信重定向問題。
        用Delphi設計時,選用ServerSocket作為與被代理工作站通信的套接字控件,選用ClientSocket動態數組作為與遠程主機通信的套接字控件。
        編程時應解決的一個重要問題是多重連接處理問題,為了加快代理服務的速度和被代理端的響應速度,套接字控件的屬性應設為非阻塞型;各通信會話與套接字動態綁定,用套接字的SocketHandle屬性值確定屬于哪一個會話。
        通信的銜接過程如下圖所示:

                                          代理服務器
                                          
                                          Serversocket
                                (1)          接  收
                 被代理端                   發  送                        遠程主機
                                (6)        (2)      (5)
                 Browser                  ClientSocket       (4)            Web Server
                                            接  收
                                            發  送          (3)


        (1)、被代理端瀏覽器發出Web請求,代理服務器的Serversocket接收到請求。
        (2)、代理服務器程序自動創建一個ClientSocket,并設置主機地址、端口等屬性,然后連接遠程主機。
        (3)、遠程連通后激發發送事件,將Serversocket接收到的Web請求數據包發送到遠程主機。
        (4)、當遠程主機返回頁面數據時,激發ClientSocket的讀事件,讀取頁面數據。
        (5)、代理服務器程序根據綁定信息確定屬于ServerSocket控件中的哪一個Socket應該將從主機接收的頁面信息發送到被代理端。
        (6)、ServerSocket中的對應Socket將頁面數據發送到被代理端。

        2、 程序編寫
        使用Delphi設計以上通信過程非常簡單,主要是ServerSocket、ClientSocket的相關事
        件驅動程序的程序編寫。下面給出作者編寫的實驗用代理服務器界面與源程序清單,內含簡要功能說明:

        unit main;

        interface

        uses
          Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
          ExtCtrls, ScktComp, TrayIcon, Menus, StdCtrls;

        type
           session_record=record
              Used: boolean;                       {會話記錄是否可用}
              SS_Handle: integer;                  {代理服務器套接字句柄}
              CSocket: TClientSocket;              {用于連接遠程的套接字}
              Lookingup: boolean;                  {是否正在查找服務器}
              LookupTime: integer;                 {查找服務器時間}
              Request: boolean;                    {是否有請求}
              request_str: string;                 {請求數據塊}
              client_connected: boolean;           {客戶機聯機標志}
              remote_connected: boolean;           {遠程服務器連接標志}
        end;

        type
          TForm1 = class(TForm)
            ServerSocket1: TServerSocket;
            ClientSocket1: TClientSocket;
            Timer2: TTimer;
            TrayIcon1: TTrayIcon;
            PopupMenu1: TPopupMenu;
            N11: TMenuItem;
            N21: TMenuItem;
            N1: TMenuItem;
            N01: TMenuItem;
            Memo1: TMemo;
            Edit1: TEdit;
            Label1: TLabel;
            Timer1: TTimer;
            procedure Timer2Timer(Sender: TObject);
            procedure N11Click(Sender: TObject);
            procedure FormCreate(Sender: TObject);
            procedure FormClose(Sender: TObject; var Action: TCloseAction);
            procedure N21Click(Sender: TObject);
            procedure N01Click(Sender: TObject);
            procedure ServerSocket1ClientConnect(Sender: TObject;
              Socket: TCustomWinSocket);
            procedure ServerSocket1ClientDisconnect(Sender: TObject;
              Socket: TCustomWinSocket);
            procedure ServerSocket1ClientError(Sender: TObject;
              Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
              var ErrorCode: Integer);
            procedure ServerSocket1ClientRead(Sender: TObject;
              Socket: TCustomWinSocket);
            procedure ClientSocket1Connect(Sender: TObject;
              Socket: TCustomWinSocket);
            procedure ClientSocket1Disconnect(Sender: TObject;
              Socket: TCustomWinSocket);
            procedure ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket;
              ErrorEvent: TErrorEvent; var ErrorCode: Integer);
            procedure ClientSocket1Write(Sender: TObject;
              Socket: TCustomWinSocket);
            procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
            procedure ServerSocket1Listen(Sender: TObject;
              Socket: TCustomWinSocket);
            procedure AppException(Sender: TObject; E: Exception);
            procedure Timer1Timer(Sender: TObject);
          private
            { Private declarations }
          public
            Service_Enabled: boolean;           {代理服務是否開啟}
            session: array of session_record;      {會話數組}
            sessions: integer;                  {會話數}
            LookUpTimeOut: integer;           {連接超時值}
            InvalidRequests: integer;            {無效請求數}
          end;

        var
          Form1: TForm1;

        implementation

        {$R *.DFM}

        file://系統啟動定時器,啟動窗顯示完成后,縮小到System Tray…
        procedure TForm1.Timer2Timer(Sender: TObject);
        begin
           timer2.Enabled:=false;     {關閉定時器}
           sessions:=0;               {會話數=0}
           Application.OnException := AppException;     {為了屏蔽代理服務器出現的異常}
           invalidRequests:=0;           {0錯誤}
           LookUpTimeOut:=60000;      {超時值=1分鐘}
           timer1.Enabled:=true;         {打開定時器}
           n11.Enabled:=false;           {開啟服務菜單項失效}
           n21.Enabled:=true;           {關閉服務菜單項有效}
           serversocket1.Port:=988;      {代理服務器端口=988}
           serversocket1.Active:=true;    {開啟服務}
           form1.hide;                 {隱藏界面,縮小到System Tray上}
        end;

        file://開啟服務菜單項…
        procedure TForm1.N11Click(Sender: TObject);
        begin
           serversocket1.Active:=true;    {開啟服務}
        end;


        file://停止服務菜單項…
        procedure TForm1.N21Click(Sender: TObject);
        begin
           serversocket1.Active:=false;      {停止服務}
           N11.Enabled:=True;
           N21.Enabled:=False;
           Service_Enabled:=false;           {標志清零}
        end;


        file://主窗口建立…
        procedure TForm1.FormCreate(Sender: TObject);
        begin
           Service_Enabled:=false;
           timer2.Enabled:=true;        {窗口建立時,打開定時器}
        end;

        file://窗口關閉時…
        procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
        begin
           timer1.Enabled:=false;          {關閉定時器}
           if Service_Enabled then
              serversocket1.Active:=false;   {退出程序時關閉服務}
        end;

        file://退出程序按鈕…
        procedure TForm1.N01Click(Sender: TObject);
        begin
           form1.Close;                     {退出程序}
        end;

        file://開啟代理服務后…
        procedure TForm1.ServerSocket1Listen(Sender: TObject;
          Socket: TCustomWinSocket);
        begin
           Service_Enabled:=true;            {置正在服務標志}
           N11.Enabled:=false;
           N21.Enabled:=true;
        end;

        file://被代理端連接到代理服務器后,建立一個會話,并與套接字綁定…
        procedure TForm1.ServerSocket1ClientConnect(Sender: TObject;
          Socket: TCustomWinSocket);
        var
        i,j: integer;
        begin
           j:=-1;
           for i:=1 to sessions do               {查找是否有空白項}
              if not session[i-1].Used and not session[i-1].CSocket.active then
                 begin
                    j:=i-1;                      {有,分配它}
                    session[j].Used:=true;       {置為在用}
                    break;
                 end
              else
                 if not session[i-1].Used and session[i-1].CSocket.active then
                       session[i-1].CSocket.active:=false;
           if j=-1 then
              begin                              {無,新增一個}
                 j:=sessions;
                 inc(sessions);
                 setlength(session,sessions);
                 session[j].Used:=true;                        {置為在用}
                 session[j].CSocket:=TClientSocket.Create(nil);
                 session[j].CSocket.OnConnect:=ClientSocket1Connect;
                 session[j].CSocket.OnDisconnect:=ClientSocket1Disconnect;
                 session[j].CSocket.OnError:=ClientSocket1Error;
                 session[j].CSocket.OnRead:=ClientSocket1Read;
                 session[j].CSocket.OnWrite:=ClientSocket1Write;
                 session[j].Lookingup:=false;
              end;
           session[j].SS_Handle:=socket.socketHandle;    {保存句柄,實現綁定}
           session[j].Request:=false;                    {無請求}
           session[j].client_connected:=true;            {客戶機已連接}
           session[j].remote_connected:=false;           {遠程未連接}
           edit1.text:=inttostr(sessions);
        end;

        file://被代理端斷開時…
        procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject;
          Socket: TCustomWinSocket);
        var
        i,j,k: integer;
        begin
           for i:=1 to sessions do
              if (session[i-1].SS_Handle=socket.SocketHandle) and session[i-1].Used then
                 begin
                    session[i-1].client_connected:=false;   {客戶機未連接}
                    if session[i-1].remote_connected then
                       session[i-1].CSocket.active:=false   {假如遠程尚連接,斷開它}
                    else
                       session[i-1].Used:=false;           {假如兩者都斷開,則置釋放資源標志}
                    break;
                 end;
           j:=sessions;
           k:=0;
           for i:=1 to j do                        {統計會話數組尾部有幾個未用項}
              begin
                 if session[j-i].Used then
                    break;
                 inc(k);
              end;
           if k>0 then                          {修正會話數組,釋放尾部未用項}
              begin
                 sessions:=sessions-k;
                 setlength(session,sessions);
              end;
           edit1.text:=inttostr(sessions);
        end;

        file://通信錯誤出現時…
        procedure TForm1.ServerSocket1ClientError(Sender: TObject;
          Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
          var ErrorCode: Integer);
        var
        i,j,k: integer;
        begin
           for i:=1 to sessions do
              if (session[i-1].SS_Handle=socket.SocketHandle) and session[i-1].Used then
                 begin
                    session[i-1].client_connected:=false;   {客戶機未連接}
                    if session[i-1].remote_connected then
                       session[i-1].CSocket.active:=false   {假如遠程尚連接,斷開它}
                    else
                       session[i-1].Used:=false;           {假如兩者都斷開,則置釋放資源標志}
                    break;
                 end;
           j:=sessions;
           k:=0;
           for i:=1 to j do
              begin
                 if session[j-i].Used then
                    break;
                 inc(k);
              end;
           if k>0 then
              begin
                 sessions:=sessions-k;
                 setlength(session,sessions);
              end;
           edit1.text:=inttostr(sessions);
           errorcode:=0;
        end;

        file://被代理端發送來頁面請求時…
        procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
          Socket: TCustomWinSocket);
        var
        tmp,line,host: string;
        i,j,port: integer;
        begin
           for i:=1 to sessions do                 {判斷是哪一個會話}
              if session[i-1].Used and (session[i-1].SS_Handle=socket.sockethandle) then
                  begin
                     session[i-1].request_str:=socket.ReceiveText;  {保存請求數據}
                     tmp:=session[i-1].request_str;                 {存放到臨時變量}
                     memo1.lines.add(tmp);
                     j:=pos(char(13)+char(10),tmp);                 {一行標志}
                     while j>0 do                       {逐行掃描請求文本,查找主機地址}
                        begin
                           line:=copy(tmp,1,j-1);                  {取一行}
                           delete(tmp,1,j+1);                      {刪除一行}
                           j:=pos('Host',line);                    {主機地址標志}
                           if j>0 then
                              begin
                                 delete(line,1,j+5);               {刪除前面的無效字符}
                                 j:=pos(':',line);
                                 if j>0 then
                                    begin
                                       host:=copy(line,1,j-1);
                                       delete(line,1,j);
                                       try
                                          port:=strtoint(line);
                                       except
                                          port:=80;
                                       end;
                                    end
                                 else
                                    begin
                                       host:=trim(line);                 {獲取主機地址}
                                       port:=80;
                                    end;
                                 if not session[i-1].remote_connected then  {假如遠征尚未連接}
                                    begin
                                       session[i-1].Request:=true;      {置請求數據就緒標志}
                                       session[i-1].CSocket.host:=host;  {設置遠程主機地址}
                                       session[i-1].CSocket.port:=port;     {設置端口}
                                       session[i-1].CSocket.active:=true;   {連接遠程主機}
                                       session[i-1].Lookingup:=true;        {置標志}
                                       session[i-1].LookupTime:=0;          {從0開始計時}
                                    end
                                 else
                                    {假如遠程已連接,直接發送請求}
                                    session[i-1].CSocket.socket.sendtext(session[i-1].request_str);                                    
                                 break;                        {停止掃描請求文本}
                              end;
                           j:=pos(char(13)+char(10),tmp);           {指向下一行}
                        end;
                     break;                    {停止循環}
                  end;
        end;

        file://當連接遠程主機成功時…
        procedure TForm1.ClientSocket1Connect(Sender: TObject;
          Socket: TCustomWinSocket);
        var
        i: integer;
        begin
           for i:=1 to sessions do
              if (session[i-1].CSocket.socket.sockethandle=socket.SocketHandle) and session[i-1].Used then
                 begin
                    session[i-1].CSocket.tag:=socket.SocketHandle;
                    session[i-1].remote_connected:=true;   {置遠程主機已連通標志}
                    session[i-1].Lookingup:=false;         {清標志}
                    break;
                 end;
        end;


        file://當遠程主機斷開時…
        procedure TForm1.ClientSocket1Disconnect(Sender: TObject;
          Socket: TCustomWinSocket);
        var
        i,j,k: integer;
        begin
           for i:=1 to sessions do
              if (session[i-1].CSocket.tag=socket.SocketHandle) and session[i-1].Used then
                 begin
                    session[i-1].remote_connected:=false;       {置為未連接}
                    if not session[i-1].client_connected then
                       session[i-1].Used:=false       {假如客戶機已斷開,則置釋放資源標志}
                    else
                       for k:=1 to serversocket1.Socket.ActiveConnections do
                          if (serversocket1.Socket.Connections[k-1].SocketHandle=session[i-1].SS_Handle) and session[i-1].used then
                             begin
                                serversocket1.Socket.Connections[k-1].Close;
                                break;
                             end;
                    break;
                 end;
           j:=sessions;
           k:=0;
           for i:=1 to j do
              begin
                 if session[j-i].Used then
                    break;
                 inc(k);
              end;
           if k>0 then                        {修正會話數組}
              begin
                 sessions:=sessions-k;
                 setlength(session,sessions);
              end;
           edit1.text:=inttostr(sessions);
        end;

        file://當與遠程主機通信發生錯誤時…
        procedure TForm1.ClientSocket1Error(Sender: TObject;
          Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
          var ErrorCode: Integer);
        var
        i,j,k: integer;
        begin
           for i:=1 to sessions do
              if (session[i-1].CSocket.tag=socket.SocketHandle) and session[i-1].Used then
                 begin
                    socket.close;
                    session[i-1].remote_connected:=false;       {置為未連接}
                    if not session[i-1].client_connected then
                       session[i-1].Used:=false        {假如客戶機已斷開,則置釋放資源標志}
                    else
                       for k:=1 to serversocket1.Socket.ActiveConnections do
                          if (serversocket1.Socket.Connections[k-1].SocketHandle=session[i-1].SS_Handle) and session[i-1].used then
                             begin
                                serversocket1.Socket.Connections[k-1].Close;
                                break;
                             end;
                    break;
                 end;
           j:=sessions;
           k:=0;
           for i:=1 to j do
              begin
                 if session[j-i].Used then
                    break;
                 inc(k);
              end;
           errorcode:=0;
           if k>0 then                        {修正會話數組}
              begin
                 sessions:=sessions-k;
                 setlength(session,sessions);
              end;
           edit1.text:=inttostr(sessions);
        end;

        file://向遠程主機發送頁面請求…
        procedure TForm1.ClientSocket1Write(Sender: TObject;
          Socket: TCustomWinSocket);
        var
        i: integer;
        begin
           for i:=1 to sessions do
              if (session[i-1].CSocket.tag=socket.SocketHandle) and session[i-1].Used then
                 begin
                    if session[i-1].Request then
                       begin
                          socket.SendText(session[i-1].request_str);   {假如有請求,發送}
                          session[i-1].Request:=false;                 {清標志}
                       end;
                    break;
                 end;
        end;

        file://遠程主機發來頁面數據時…
        procedure TForm1.ClientSocket1Read(Sender: TObject;
          Socket: TCustomWinSocket);
        var
        i,j: integer;
        rec_bytes: integer;                  {傳回的數據塊長度}
        rec_Buffer: array[0..2047] of char;  {傳回的數據塊緩沖區}
        begin
           for i:=1 to sessions do
              if (session[i-1].CSocket.tag=socket.SocketHandle) and session[i-1].Used then
                 begin
                    rec_bytes:=socket.ReceiveBuf(rec_buffer,2048);    {接收數據}
                    for j:=1 to serversocket1.Socket.ActiveConnections do
                       if serversocket1.Socket.Connections[j-1].SocketHandle=session[i-1].SS_Handle then
                          begin
                             serversocket1.Socket.Connections[j-1].SendBuf(rec_buffer,rec_bytes);  {發送數據}
                             break;
                          end;
                    break;
                 end;
        end;

        file://“頁面找不到”等錯誤信息出現時…
        procedure TForm1.AppException(Sender: TObject; E: Exception);
        begin
          inc(invalidrequests);
        end;

        file://查找遠程主機定時…
        procedure TForm1.Timer1Timer(Sender: TObject);
        var
        i,j: integer;
        begin
           for i:=1 to sessions do
              if session[i-1].Used and session[i-1].Lookingup then    {假如正在連接}
                 begin
                    inc(session[i-1].LookupTime);
                    if session[i-1].LookupTime>lookuptimeout then     {假如超時}
                       begin
                          session[i-1].Lookingup:=false;
                          session[i-1].CSocket.active:=false;         {停止查找}
                          for j:=1 to serversocket1.Socket.ActiveConnections do
                             if serversocket1.Socket.Connections[j-1].SocketHandle=session[i-1].SS_Handle then
                                begin
                                   serversocket1.Socket.Connections[j-1].Close;  {斷開客戶機}
                                   break;
                                end;
                       end;
                 end;
        end;
        end.

        3、 后記
        由于這種設計思路僅僅在被代理端和遠程主機之間增加了一個重定向功能,被代理端原
        有的緩存技術等特點均保留,因此效率較高。經過測試,利用1個33.6K的Modem上網時,三到十個被代理工作站同時上網,仍有較好的響應速度。由于被代理工作站和代理服務器工作站之間的連接一般通過高速鏈路,因此瓶頸主要出現在代理服務器的上網方式上。
        通過上述方法,作者成功開發了一套完善的代理服務器軟件并與機房計費系統完全集
        成,實現了利用一臺工作站完成上網代理、上網計費、用機計費等功能。 有編程經驗的朋友完全可以另行增加代理服務器功能,如設定禁止訪問站點、統計客戶流量、Web訪問列表等等。

        溫馨提示:喜歡本站的話,請收藏一下本站!

        本類教程下載

        系統下載排行

        主站蜘蛛池模板: 亚洲人成色7777在线观看不卡 | 国产精品免费_区二区三区观看 | 日本亚洲色大成网站www久久| 日本在线免费观看| 久久久综合亚洲色一区二区三区 | 国产成人高清精品免费观看| 亚洲免费在线观看| 国产黄在线观看免费观看不卡| 免费一级特黄特色大片在线 | 亚洲国产综合无码一区二区二三区 | 午夜私人影院免费体验区| 亚洲中文字幕无码亚洲成A人片| 成人毛片手机版免费看| 亚洲熟妇AV一区二区三区浪潮| 麻豆国产精品入口免费观看| 精品国产亚洲第一区二区三区| 国产成人精品123区免费视频| 无遮挡国产高潮视频免费观看| 伊人婷婷综合缴情亚洲五月| 久久精品免费电影| 亚洲人成7777| 亚洲精品国产高清嫩草影院| 永久在线观看免费视频| 亚洲最大成人网色| 手机看片久久国产免费| 成人免费乱码大片A毛片| 中文字幕亚洲综合久久2| 亚色九九九全国免费视频| 亚洲成av人片天堂网无码】| 亚洲阿v天堂在线2017免费| 青青操免费在线视频| 亚洲一级毛片免观看| 亚洲福利精品一区二区三区| 99re6在线精品免费观看| ass亚洲**毛茸茸pics| 亚洲av无码乱码在线观看野外 | 久久亚洲精品国产精品| 女人被男人躁的女爽免费视频| 一个人晚上在线观看的免费视频| 77777_亚洲午夜久久多人| 免费大黄网站在线观|