由 TIdTCPServer 提供客戶端安裝程序 WEB 下載的實現方法

相信衆多的 TCP 網絡服務器,均是由 TIdTCPServer 來提供服務,我的《網絡存儲服務器》也是。

即然是TCP服務器,若是需要相應的客戶端來支持,那麼,由這個服務端口(TCP)直接來提供客戶端安裝程序 WEB 下載是不是變得很方便?再也不用另加一個TIdHTTPServer來做了, 當然,若是需要提供 WEB 管理,還得麻煩他老人家。

HTTP協議:

在 IE 由 http://127.0.0.1:3001 連接到這個端口(Port=3001)時,發過來以下內容:

GET / HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322)
Host: 127.0.0.1:3001
Connection: Keep-Alive
Cookie: iscookies=0

 只有第一行纔是我所需要的並且是有用的,後面的可省略,假定下載的客戶端安裝程序文件名是 ClientSetup.exe, 此時提供下載時,IE 會有不知道是什麼文件的麻煩,因此必須將客戶端IE地址改成 http://127.0.0.1:3001/ClientSetup.exe 他才能知道是什麼文件與文件名,通常是採用JAVA網頁轉向技術(見後面的代碼)。 

GET /ClientSetup.exe HTTP/1.1
......  下面的省略

其他服務器應迴應 IE 的內容,見下面:

//   TIdTCPServer * IdTcpServer 的 OnExecute 事件處理函數
void __fastcall TForm1::IdTCPServerExecute(TIdContext *AContext)
{
    AnsiString ReadLn;
    TIdIOHandler 
* IOHandler = AContext->Connection->IOHandler;
    
try
    
{
        
while (IdTCPServer->Active)
        
{
            ReadLn = IOHandler->ReadLn();
            
if (ReadLn.Length())
            
if (ReadLn.Pos("GET "== 1)
            
{
  
              ReadLn.SetLength(ReadLn.Pos(" HTTP/")-1);
                ReadLn.Delete(
14);   //  刪除前面的 GET 與空格

                
while (IOHandler->ReadLn().Length());   //  讀取完後面的內容

                
if (!FileExixts("ClientSetup.exe"))
                
{
                    AnsiString HTML  
= "<HTML> <HEAD> ";
                    HTML 
+= "<script LANGUAGE="JavaScript"> ";
                    HTML 
+= "function closeit() {";
                    HTML 
+= "setTimeout("self.close()",10000)";      // 
                    HTML += "} ";
                    HTML += "closeit()";
                    HTML 
+= "</script> ";
                    HTML 
+= "</HEAD> ";
                    HTML 
+= "<BODY>客戶端安裝程序不存在,正在關閉窗口...</BODY> ";
                    HTML 
+= "</HTML>";

                    ReadLn  
= "HTTP/1.1 200 OK ";
                    ReadLn 
+= "Server: Goldray Network Storage Server 2.0 ";
                    ReadLn 
+= "Date: " + DateTimeToHTTPFormat(Now()) + " ";
                    ReadLn 
+= "Content-Length: " + IntToStr(HTML.Length()) + " ";
                    ReadLn 
+= "Content-Type: text/html ";
                    ReadLn 
+= "Expires: " + DateTimeToHTTPFormat(Now()) + " ";
                    ReadLn 
+= "Set-Cookie: path=/ ";
                    ReadLn 
+= "Cache-control: no-cache";

                    IOHandler
->WriteLn(ReadLn);
                    IOHandler
->WriteLn();

                    Sleep(
100);

                    IOHandler
->WriteLn(HTML);
                    IOHandler
->WriteLn();
                }

                else
if (ReadLn.UpperCase() == "/CLIENTSETUP.EXE")
                
{
                        ReadLn  
= "HTTP/1.1 200 OK ";
                        ReadLn 
+= "Server: Goldray Network Storage Server 2.0 ";
                        ReadLn 
+= "Date: " + DateTimeToHTTPFormat(Now()) + " ";
                        ReadLn 
+= "Content-Type: application/octet-stream ";
                        ReadLn 
+= "Accept-Ranges: bytes ";
                        ReadLn 
+= "Last-Modified: ";
                        ReadLn 
+= DateTimeToHTTPFormat(FileDateToDateTime(FileAge("ClientSetup.exe")));
                        ReadLn 
+= " ";
                        ReadLn 
+= "Content-Length: " + IntToStr(FileGetSize("ClientSetup.exe"));

                        IOHandler
->WriteLn(ReadLn);   //  發送HTTP前導
                        IOHandler->WriteLn();
                        IOHandler
->WriteFile("ClientSetup.exe");

                        Sleep(
100);
                        AContext
->Connection->Disconnect();    //  斷開吧,已經不需要了
                        break;
                }

                
else   //  若 IE 不是 http://......./ClientSetup.exe 讀取的,總是轉向這裏
                {
                    AnsiString HTML  
= "<HTML> <HEAD> ";
                    HTML 
+= "<script LANGUAGE="JavaScript"> ";
                    HTML 
+= "<!-- ";
                    HTML 
+= "window.location="http://";
                    HTML 
+= AContext->Binding()->IP + ":";
                    HTML 
+= IntToStr(AContext->Binding()->Port) + "/ClientSetup.exe" ";
                    HTML 
+= "// --> ";
                    HTML 
+= "</script> ";
                    HTML 
+= "</HEAD> ";
                    HTML 
+= "<BODY>正在轉向客戶端下載,請稍候...</BODY> ";
                    HTML 
+= "</HTML>";

                    ReadLn  
= "HTTP/1.1 200 OK ";
                    ReadLn 
+= "Server: Goldray Network Storage Server 2.0 ";
                    ReadLn 
+= "Date: " + DateTimeToHTTPFormat(Now()) + " ";
                    ReadLn 
+= "Content-Length: " + IntToStr(HTML.Length()) + " ";
                    ReadLn 
+= "Content-Type: text/html ";
                    ReadLn 
+= "Expires: " + DateTimeToHTTPFormat(Now()) + " ";
                    ReadLn 
+= "Set-Cookie: path=/ ";
                    ReadLn 
+= "Cache-control: no-cache";

                    IOHandler
->WriteLn(ReadLn);
                    IOHandler
->WriteLn();

                    Sleep(
100);

                    IOHandler
->WriteLn(HTML);
                    IOHandler
->WriteLn();
                }

            }

            
else
            
{
                
//   ...  這裏纔是你的 TCP 服務器指令處理過程
            }

        }

    }

    
catch (...)
    
{
        Sleep(
100);
    }

}

當然,實際使用時建議文件名使用含路徑的全稱, 使用變量是不錯的選擇。

哦,還小了兩個函數,一併附上:

//   HTML 迴應時間必須是 GMT 格式
AnsiString DateTimeToHTTPFormat(TDateTime ADateTime)
{
    TSystemTime GMTTime;
    TFileTime DT;
    DateTimeToSystemTime(ADateTime, GMTTime);
    SystemTimeToFileTime(
&GMTTime, &DT);
    LocalFileTimeToFileTime(
&DT, &DT);
    FileTimeToSystemTime(
&DT, &GMTTime);

    AnsiString wdays[
7= {"Sun""Mon""Tue""Wed""Thu""Fri""Sat"};

    AnsiString monthnames[
12= {"Jan""Feb""Mar""Apr""May",
        
"Jun",  "Jul""Aug""Sep""Oct""Nov""Dec"}
;

    AnsiString Result 
= wdays[GMTTime.wDayOfWeek];
    Result 
+= "";

    TVarRec argd[
1= {GMTTime.wDay};
    TVarRec args[
4= {GMTTime.wYear, GMTTime.wHour, GMTTime.wMinute, GMTTime.wSecond};

    Result 
+= Format("%.2d ", argd, 0);
    Result 
+= monthnames[GMTTime.wMonth-1];
    Result 
+= Format(" %.4d %.2d:%.2d:%.2d", args, 3);

    Result 
+= " GMT";

    
return Result;
}

下面的代碼,網上到處都是啦,也就來湊一下字數吧

//   取文件大小
#include <stdio.h>
__int64 __fastcall FileGetSize(AnsiString AFileName)
{
    __int64 fSize 
= 0;
    
if (FileExists(AFileName))
    
{
        FILE 
* fp = fopen(AFileName.c_str(),"rb");
        
if(fp)
        
{
            fseek(fp,
0,SEEK_END);
            fSize 
= ftell(fp);
            fclose(fp);
        }

    }

    
return fSize;
}

以上程序在 C++Builder 2006 + Indy10 下通過。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章