【A3】Delphi/C++Builder テクニカルセッション「VCLネットワークプログラミング」
Indyの基礎（株）シリアルゲームズ
取締役／シニアエンジニア
細川 淳

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

1

アジェンダ
• Indy について
• TIdHTTP のプログラム• TIdTCPClient のプログラム

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

2

Indy について本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

3

Indy とは？
• Indy = Internet Direct• インターネットを使用するコンポーネント群• オープンソースで開発されている• 今では、Delphi 標準のネット用コンポーネントになっている• 1993年• VB 用のプロジェクトとして始まる• 1995年• Delphi 用の Indy プロジェクトが始まる

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

4

Indy の代表的なコンポーネント• TIdTCPClient• TCP を使用するコンポーネント• TIdHTTP• HTTP 通信を行うコンポーネント• TIdPOP3• POP3 でメールを取得するコンポーネント• TIdSMTP• SMTP でメールを送信するコンポーネント• 今回は• TIdHTTP• TIdTCPClient• について解説

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

5

Indy のユニット• コンポーネント毎に１つのユニット• たとえば TIdHTTP は IdHTTP.pas に定義されている• 押さえておきたい IdGlobal ユニット• ネットワークプログラムに便利な関数や定数が用意されている• 各プラットフォーム用に VCL と同名の関数を置き換えた物も多数定義されている

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

6

IdGlobal 定数•

HoursPerDay = 24;•MinsPerHour = 60;•SecsPerMin•

MSecsPerSec = 1000;•LF = #10;

•CR = #13;

•EOL = CR + LF; (sLineBreak)•

CHAR0 = #0;•BACKSPACE = #8;•TAB = #9;= 60;•CHAR32 = #32;

•WhiteSpace = [0..12, 14..32];•

IdHexDigits: array [0..15] of AnsiChar = ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');•

IdOctalDigits: array [0..7] of AnsiChar = ('0','1','2','3','4','5','6','7');

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

7

IdGlobal のクラス
• 有用なストリームクラス• TFileCreateStream• ファイルを新規作成するストリーム• TReadFileNonExclusiveStream• 他のアプリケーションがファイルを開いていても例外を出さずに、ファイルを読めるストリーム

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

8

IdGlobal ユニット
• InMainThread 関数• メインスレッドを実行中なら True

• IPv4ToDWord 関数• IPv4 形式の文字列を Cardinal にして返す• 192.168.0.1 → C0 A8 00 01 (3232235521)• Fetch• 指定された区切り文字まで文字列を出力する• Fetch('192.168.0.1', '.') → '192'
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

9

TIdHTTP を使ってみよう
スレッドを使って、非同期に Indy を使う！

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

10

TIdHTTP で HTML を取得する
procedure ReadFromURL(const iURL: String);
var
IdHTTP: TIdHTTP;
begin
IdHTTP := TIdHTTP.Create(nil);try
Memo1.Lines.Text := IdHTTP.Get(iURL);
ShowMessage('読み込み完了'); // Get完了まで実行されない
finally
IdHTTP.Free;end;end;
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

11

Indy のプログラミングについて• Indy は同期コンポーネント• 非同期で動作させるためには• スレッドを使用する• メッセージの処理だけなら TIdAntiFreeze でもよい

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

12スレッドを使用する１
THTTPGetEvent = procedure (Sender: TObject; const iHTML: String) of object;
TSampleThread = class(TThread)
privateFRunning: Boolean;
FURL: String;
FHTML: String;
FOnGet: THTTPGetEvent;privateprocedure CallOnGet;
protected
procedure Execute; override;
publicconstructor Create; reintroduce;
procedure SetURL(const iURL: String);
property Running: Boolean read FRunning;property OnGet: THTTPGetEvent read FOnGet write FOnGet;
end;

13

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

スレッドを使用する２procedure TSampleThread.Execute;Sleep(100);

var

end;

IdHTTP: TIdHTTP;

finally

begin

IdHTTP.Free;

FRunning := True;tryend;finally

IdHTTP := TIdHTTP.Create(nil);try

FRunning := False;
end;

IdHTTP.HandleRedirects := True; // リダイレクトの設定

end;

while (not Terminated) and (not Application.Terminated) do begin
if (FURL <> '') then begin
FHTML := IdHTTP.Get(FURL);
FURL := '';if (FHTML <> '') thenSynchronize(CallOnGet);
end;

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

14スレッドを利用する３procedure TForm1.btnGetClick(Sender: TObject);

procedure TForm1.SampleThreadGet(Sender: TObject; const iHTML: String);

begin

begin

SampleThread.SetURL(edtURL.Text);
end;

memoHTML.Lines.Text := iHTML;end;

procedure TForm1.FormCreate(Sender: TObject);
begin
SampleThread := TSampleThread.Create;
SampleThread.OnGet := SampleThreadGet;
end;procedure TForm1.FormDestroy(Sender: TObject);
begin
SampleThread.Terminate;while (SampleThread.Running) do
Sleep(100);
SampleThread.Free;end;

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

15

実行結果

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

16

HTTP で画像を取得• 画像も Get で取得可能• Get の TIdStream を引数に取るバージョンを使う• procedure Get(AURL: string; AResponseContent: TIdStream); overload;• いままでの Get を使う場合、戻り値が String なため、String からバイナリを起こす必要がある

17

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

画像を取得する１
procedure TSampleThread.Execute;varwhile (not Terminated) and (not Application.Terminated) do begin
if (FURL <> '') then begin

IdHTTP: TIdHTTP;

FStream.Clear;// クリアしてbegin
FRunning := True;

IdHTTP.Get(FURL, FStream); // 取得

try
FStream := nil;

FURL := '';IdHTTP := nil;try

if (FStream.Size > 0) then begin // 取得していたら// 結果受け取り用ストリーム

FStream.Position := 0;

FStream := TMemoryStream.Create;

Synchronize(CallOnGet);
end;

IdHTTP := TIdHTTP.Create(nil);

end;

IdHTTP.HandleRedirects := True;
Sleep(100);
end;
finally
IdHTTP.Free;
FStream.Free;
end; // 以下略

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

18

画像を取得する２
procedure TForm1.SampleThreadGet(Sender: TObject;
const iStream: TMemoryStream);var
Magic: String;
IsImage: Boolean;
HTML: String;procedure AssignImage(
const iMagic: String;
const iGraphicClass: TGraphicClass);
var
Graphic: TGraphic;
begin
if (Copy(Magic, 1, Length(iMagic)) = iMagic) then begin
Graphic := iGraphicClass.Create;
tryGraphic.LoadFromStream(iStream);
IsImage := True;
imgImage.Picture.Assign(Graphic);
finally
Graphic.Free;
end;end;end;

19

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

画像を取得する３
begin

if (IsImage) then begin

Magic := StringOfChar(#0, 4);// 画像だったらMove(iStream.Memory^, PChar(Magic)^, Length(Magic));

memoHTML.Lines.Text := '';pageHTML.ActivePage := tabImage;AssignImage('GIF', TGIFImage);end

AssignImage(#$ff#$d8#$ff#$e0, TJpegImage);else begin

AssignImage(#$89'PNG', TPngObject);// 画像じゃなかったら
imgImage.Picture.Assign(nil);
SetLength(HTML, iStream.Size);Move(iStream.Memory^, PChar(HTML)^, Length(HTML));
memoHTML.Lines.Text := HTML;
pageHTML.ActivePage := tabSource;
end;
end;

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

20

実行結果

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

21

HTTPの引数• Get• Get の場合は URL の後ろに自分で引数を付ける• IdHTTP.Get(URL + '?aaa=bbb&cc=dd');• Post• Post の場合は TIdMultiPartFormDataStream を使用する

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

22

Post を使う
while (not Terminated) and (not Application.Terminated) do begin
if (FURL <> '') and (FParams.Count > 0) then begin

FURL := '';FParams.Clear;

FStream.Clear;
if (FStream.Size > 0) then begin
FStream.Position := 0;Param := TIdMultiPartFormDataStream.Create;

Synchronize(CallOnPost);

tryfor i := 0 to FParams.Count - 1 do begin
Name := FParams[i];

end;
end;

Param.AddFormField(Name, FParams.Values[Name]);end;

Sleep(100);end;

IdHTTP.Post(FURL, FParams, FStream);
finally
Param.Free;end;

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

23

実行結果

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

24

Basic認証
Basic 認証のかかっているページにアクセスするには• TIdHTTP.Reuqest.UserName• TIdHTTP.Reuqest.Password• TIdHTTP.BasicAuthentication
を使用する

FIdHTTP.BasicAuthentication := True;
FIdHTTP.Request.UserName := 'ユーザー名';
FIdHTTP.Request.Password := 'パスワード';

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

25

Proxy を使う
• Proxy は TIdHTTP.ProxyParams に設定する
ProxyParams.ProxyServer := プロクシサーバのＵＲＬ;
ProxyParams.ProxyPort := プロクシサーバのポート番号;
ProxyParams.ProxyUsername := プロクシのユーザー名;
ProxyParams.ProxyPassword := プロクシのパスワード;// パスワードが必要な場合 BasicAuthentication を True にする
ProxyParams.BasicAuthentication := (ProxyParams.ProxyUsername <> '');

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

26

カスタムヘッダ
HTTP ヘッダに ヘッダを追加するには
TIdHTTP.Request.CustomHeadersを使用する
CustomHeaders.Values['X-PoweredBy'] := 'CodeGear Delphi';

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

27

タイムアウトの制御• タイムアウト値の制御は TIdHTTP.IOHandler を使用する
FHTTP := TIdHTTP.Create;with FHTTP do begin
if (IOHandler = nil) then
IOHandler := TIdIOHandler.MakeDefaultIOHandler(FHTTP);
IOHandler.Open; // 簡便な初期化方法
IOHandler.Close;
IOHandler.InputBuffer.Capacity := 1024 * 1024;
IOHandler.ReadTimeout := 60; // タイムアウト秒
end;
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

28

TIdTCPClient を使ってみよう

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

29

TIdTCPClient とは• TCP (Transmission Control Protocol) のクライアント• TCP (トランスポート層）の上に HTTP などのプロトコルが乗っている• TIdTCPClient を使うと TCP を使ったサーバと通信できる

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

30

TIdTCPClient のプロパティ・メソッド• Host プロパティ• サーバのアドレスを指定する• Port プロパティ• 接続先ポートの番号を指定する• Connect メソッド
• 接続する• IOHandler プロパティ• 接続後のデータの読み出しなどに使用する
31

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

TIdTCPClient を使ってみる
procedure ConnectServer(if (Size > 0) then // サイズが０じゃなかったら読み込む
Read := TCPClient.IOHandler.InputBuffer.Extract(Size)
else
Break;

const iHost: String;
const iPort: Integer);
var

end;

TCPClient: TIdTCPClient;

finally

Size: Integer;

TCPClient.Disconnect;

Read: String;

// 切断

end;

begin
TCPClient := TIdTCPClient.Create;

finally
TCPClient.Free;

try
TCPClient.Host := iHost;// サーバー設定

TCPClient.Port := iPort;// ポート番号設定

TCPClient.Connect;

// 接続

end;
ShowMessage(Read);// 読み込んだ結果の表示

end;

try
while (True) do begin// サーバからの戻り値のサイズ
Size := TCPClient.ReadFromStack(False, 100, False);

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

procedure TForm1.FormCreate(Sender: TObject);
beginConnectServer('127.0.0.1', 3786);
end;

32

TIdTCPClient でデータを送信• 前述のようにサーバと接続後• TIdTCPClient.Write メソッドでデータを送信できる• Write メソッドには様々なバリエーションがある– 文字列– ストリーム– 整数型（SmallInt, Int64, Cardinal など）

TCPClient.Write('TEST'#0)
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

33

タイムアウトの制御• TIdTCPClient のタイムアウトは２種類• プロパティに設定するだけで良い• ConnectTimeout プロパティ• コネクト時のタイムアウト （ミリ秒）• ReadTimeout プロパティ• 読み出し時のタイムアウト （ミリ秒）

TCPClient.ReadTimeout := 60 * 1000; // ６０秒待つ

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

34

Indy を使ってTIdHTTP, TIdTCPClient を使ったアプリケーション

35

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

TIdHTTP で実現される接続形態
サーバ

要求

サーバ

応答

接続は維持されない
その後クライアントクライアント一度の要求毎に切断するため、毎回ユーザー認証の必要がある。
（クッキーを使ってセッションを維持することも当然ある）

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

36

TIdTCPClient で実現される接続形態
サーバ

要求

サーバ

応答

要求

応答

切断するまで接続が維持される

その後クライアント

クライアント接続が維持される（サーバと繋がりっぱなし）ため、ログイン時の最初だけユーザー認証を行えばよい。

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

37

TIdHTTP を使ったアプリケーション• Twitter (ミニブログ）用アプリ• TIdHTTP を使って Twitter API を叩く• Post でこちらの欲しい情報を指定し、戻り値を使う

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

38

TIdTCPClient を使ったアプリケーション• メッセンジャー• TIdTCPClient を使ってサーバと常に接続されている• サーバからはメンバーのログインや、ログアウト、チャットの情報が流れてくる• それを TIdTCPClient で取得し表示する• TIdTCPClient.Write を使ってチャットデータを送信する
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

39