はじめに
測定装置など、ハードウェアとの通信にTCP/IPを用いる場合は、ソケットの挙動について、細かな制御が必要がある場合が未だあるように思います。Delphi XE のTTcpServerは、文字列受信後に直ぐにコネクションを閉じてしまう為、今回はIndy の TIdTCPServer を使用してみました。
(備忘録です)
サンプル
フォーム
![フォームデザイン.png](https://qiita-image-store.s3.amazonaws.com/0/33684/24a57384-4f3c-5fcf-047e-07dc26479966.png)ソースコード
```delphi unit Unit1;interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdContext, Vcl.StdCtrls,
IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer, Vcl.ExtCtrls, IdGlobal;
type
TForm1 = class(TForm)
LabeledEdit1: TLabeledEdit;
IdTCPServer1: TIdTCPServer;
CheckBox1: TCheckBox;
Memo1: TMemo;
procedure IdTCPServer1Execute(AContext: TIdContext);
procedure IdTCPServer1Exception(AContext: TIdContext;
AException: Exception);
procedure IdTCPServer1Disconnect(AContext: TIdContext);
procedure IdTCPServer1Connect(AContext: TIdContext);
procedure CheckBox1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
FEncoding:TEncoding;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
FEncoding := TEncoding.GetEncoding(932);
Memo1.Clear;
LabeledEdit1.Text := IntToStr(IdTCPServer1.DefaultPort);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if IdTCPServer1.Active then
IdTCPServer1.Active := False;
FEncoding.Free;
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if CheckBox1.Checked then begin
CheckBox1.Caption := 'Start';
IdTCPServer1.DefaultPort := StrToInt(LabeledEdit1.Text);
IdTCPServer1.Active := True;
end else begin
CheckBox1.Caption := 'Stop';
IdTCPServer1.Active := False;
end;
end;
procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
begin
Memo1.Lines.Add('Connect:'+AContext.Binding.PeerIP);
end;
procedure TForm1.IdTCPServer1Disconnect(AContext: TIdContext);
begin
Memo1.Lines.Add('Disconnect:'+AContext.Binding.PeerIP);
end;
procedure TForm1.IdTCPServer1Exception(AContext: TIdContext;
AException: Exception);
begin
Memo1.Lines.Add(AException.Message);
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
Text: String;
Bytes: TIdBytes;
begin
AContext.Connection.IOHandler.ReadBytes(Bytes,-1);
try
Text := FEncoding.GetString(Bytes);
Memo1.Lines.Add(Text);
AContext.Connection.IOHandler.Write('OK');
except
Memo1.Lines.Add('文字列変換に失敗しました');
end;
end;
end.
<h2>結果</h2>
汎用のソケット通信ツールを使用して、接続したり、文字を送ってみてください。
![こんな感じ.png](https://qiita-image-store.s3.amazonaws.com/0/33684/020e8a1a-3f8f-b51e-9ec2-6e05a47a9352.png)
上記例は、<b>汎用ツール側</b>で、
1.接続→「あいうえお」送信→切断
2.接続→「かきくけこ」送信→アプリ終了
を行いました。
2.の後に、もう再度接続する事も可能です。
<h1>ポイント</h1>
<h2>Usesの追加</h2>
「IdGlobal」は手動Usesに追加する必要があるようです。
<h2>受信時の処理</h2>
こちらの記事を参考に、TIdTCPServerのExcecuteイベントにて、受信バッファをバイト列で取出し、Stringに変換します。
http://www.watercolor-city.net/ct_delphi/delphi_tiburon/doc_unicode/bintostring.htm
```delphi
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
Text: String;
Bytes: TIdBytes;
begin
AContext.Connection.IOHandler.ReadBytes(Bytes,-1);
try
Text := FEncoding.GetString(Bytes);
Memo1.Lines.Add(Text);
AContext.Connection.IOHandler.Write('OK');
except
Memo1.Lines.Add('文字列変換に失敗しました');
end;
end;
ReadBytesの2番目の引数は、取得バイト数で、-1に設定すると「前回の取得から全て」となるようです。便利!
今回は、TEncoding のインスタンスをFormCreate内で作成しています。
コードページには、932(Microsoft コードページ Shift-JIS)を指定します。