LoginSignup
9
7

More than 5 years have passed since last update.

Delphi 素朴なソケット通信をしたい

Posted at

はじめに

測定装置など、ハードウェアとの通信にTCP/IPを用いる場合は、ソケットの挙動について、細かな制御が必要がある場合が未だあるように思います。

Delphi XE のTTcpServerは、文字列受信後に直ぐにコネクションを閉じてしまう為、今回はIndy の TIdTCPServer を使用してみました。

(備忘録です)

サンプル

フォーム

フォームデザイン.png

ソースコード

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.

結果

汎用のソケット通信ツールを使用して、接続したり、文字を送ってみてください。
こんな感じ.png

上記例は、汎用ツール側で、
 1.接続→「あいうえお」送信→切断
 2.接続→「かきくけこ」送信→アプリ終了
を行いました。
2.の後に、もう再度接続する事も可能です。

ポイント

Usesの追加

「IdGlobal」は手動Usesに追加する必要があるようです。

受信時の処理

こちらの記事を参考に、TIdTCPServerのExcecuteイベントにて、受信バッファをバイト列で取出し、Stringに変換します。
http://www.watercolor-city.net/ct_delphi/delphi_tiburon/doc_unicode/bintostring.htm

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)を指定します。

クライアントの区別

AContext.Binding.PeerIP で IP を取得できるようです。

(自分への)宿題

「接続中の任意のクライアントに、自発的にデータ送信する」場合の処理が、まだ調べられていません。どこからアクセスするのか・・英語・・ニガテ・・。

9
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7