精華區beta PLT 關於我們 聯絡資訊
下面的程式示範如何使用 WinSock 以及 HTTP 通訊協定來取得一網頁 內容。因為這個程式是用 TThread 寫成的,所以在取得網頁的過程中 不會讓前端的介面停住。 你可以在下面的網址取得此程式之所有檔案(必須用 Delphi 4 編譯)。 http://lsd.cc.ntu.edu.tw/upload/winsocksamp.zip (4K) 重點部分code: =================================================== TClientThread = class(TThread) public Socket: TSocket; Cont: string; Url: string; Status: string; public constructor Create(Url: string); procedure ShowContent; procedure ShowError; procedure UpdateStatus; procedure Execute; override; end; { TClientThread } procedure TClientThread.Execute; const Port = 80; var ServerName: string; Path: string; sa: TSockAddrIn; IP: Cardinal; entry: PHostEnt; Content: string; PP: Integer; Bytes: Integer; ContentLength: Integer; ContentType: string; procedure SendStr(S: string); // 送出字串 begin if send(Socket, PChar(S)^, Length(S), 0) < Length(S) then Abort; end; function GetLine: string; // 取得一以 CR/LF 結束的行 var C: Char; begin Result := ''; while True do begin case recv(Socket, c, 1, 0) of 0: Exit; SOCKET_ERROR: Abort; end; if c = ^M then // ^M 代表 CR (Carriage Return, 第13個ASCII字元) Continue; if c = ^J then // ^J 代表 LF (Line Feed, 第10個ASCII字元) Exit; Result := Result + c; end; end; procedure GetHeader; // 取得 HTTP 的 header information // 此 header 內包含此文件的長度(ContentLength) var S: string; P: Integer; Left, Right: string; begin while True do begin S := GetLine; if S = '' then Exit; P := Pos(':', S); Left := LowerCase(Copy(S, 1, P - 1)); Right := Copy(S, P + 2, Length(S)); if Left = 'content-length' then ContentLength := StrToInt(Right) else if Left = 'content-type' then ContentType := Right; end; end; procedure ParseUrl; var I: Integer; begin if Copy(Url, 1, 7) <> 'http://' then Abort; Delete(Url, 1, 7); I := Pos('/', Url); // 尋找第一個 '/' if I = 0 then // 沒有 '/',所以Server就是url了 begin ServerName := Url; Path := '/'; end else begin ServerName := Copy(Url, 1, I-1); Path := Copy(Url, I, Length(Url)); end; end; begin FreeOnTerminate := True; // 設定結束時釋放 Thread 占的空間 try ParseUrl; Socket := WinSock.socket(AF_INET, SOCK_STREAM, 0); // 分配一個socket try FillChar(sa, SizeOf(sa), 0); // 將 TSockAddrIn 結構體清成zero sa.sin_family := AF_INET; // 設定 family(固定為af_inet) sa.sin_port := htons(Port); // 將port由host byte order轉成network byte order IP := inet_addr(PChar(ServerName)); // 試試看 ServerName 能不能直接轉成 IP if IP = INADDR_NONE then // 如果不能的話則透過 DNS 查詢 begin Status := '找尋主機中...'; Synchronize(UpdateStatus); entry := gethostbyname(PChar(ServerName)); // 查詢主機 if entry = nil then // 若查不到則 Abort Abort; Move(entry^.h_addr^^, IP, SizeOf(IP)); // 取得 IP end; sa.sin_addr.s_addr := IP; // 將 IP 填入 TSockAddrIn 結構體 Status := '主機找到,發出要求中...'; Synchronize(UpdateStatus); if connect(Socket, sa, SizeOf(sa)) = SOCKET_ERROR then // 試試連結 Abort; // 不能連結則 Abort // 下面送出 HTTP 的要求字串 SendStr('GET ' + Path + ' HTTP/1.1'^M^J); SendStr('Accept: */*'^M^J); SendStr('User-Agent: My app'^M^J); SendStr(^M^J); Status := '取得文件中...'; Synchronize(UpdateStatus); GetHeader; // 取得 HTTP 的 header information // 此 header 內包含此文件的長度(ContentLength) SetLength(Content, ContentLength); // 設定字串長度為 ContentLength PP := 1; // 設定『游標』位置。之所以需要游標位置是因為 WinSock 的 recv // 並非會等到所有資料傳完才返回,而是當其內部 buffer 滿的時候 // 就返回,此時你必須記錄目前位置,以便下次繼續讀取 while True do // 取得資料的主回圈 begin // 取得資料,此函數傳回的值為: // 0: 代表資料擷取結束,或遠端主機正常結束 // 大於 0:傳回資料 byte 數 // SOCKET_ERROR:發生錯誤! Bytes := recv(Socket, Content[PP], ContentLength, 0); if Bytes = 0 then Break; if Bytes = SOCKET_ERROR then Abort; Inc(PP, Bytes); // 將『游標』前移 if PP >= ContentLength then // 邊界狀況 Break; Status := '取得文件中...%' + IntToStr(Round(PP / ContentLength * 100)) + '(' + IntToStr(PP) + '位元組已收到)'; Synchronize(UpdateStatus); end; Cont := Content; // 網頁內容已經成功的傳回,存在 Content 變數中 Synchronize(ShowContent); // show 出 content! Status := '完成...'; Synchronize(UpdateStatus); finally closesocket(Socket); end; except on E: EAbort do begin Synchronize(ShowError); Status := '取消...'; Synchronize(UpdateStatus); end; end; end; procedure TClientThread.ShowContent; begin Form1.Memo1.Lines.Text := Cont; end; procedure TClientThread.ShowError; begin Form1.Memo1.Lines.Text := '發生錯誤!' end; procedure TClientThread.UpdateStatus; begin Form1.StatusBar1.SimpleText := Status; end; constructor TClientThread.Create(Url: string); begin Self.Url := Url; inherited Create(False); end; -- Chaos is the best description of the constant state of human society. Therefore, dynamic balance is required for us to to survive when vital fault occurrs. So in the society, we don't chase for peace and order, but existence and survival, instead. -- ※ 發信站: 批踢踢實業坊(ptt.twbbs.org) ◆ From: T232-004.dialup