動作環境
C++ Builder XE4
処理概要
- TCPサーバーに接続する
- TCPサーバーに文字列を送信する
- TCPサーバーからの応答を受信する
- タイムアウト付き
code v0.1 (Whileでの受信待ち)
備考: E_ipadrとE_portはPasswordCharを設定している
Unit1.h
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <IdBaseComponent.hpp>
#include <IdComponent.hpp>
#include <IdTCPClient.hpp>
#include <IdTCPConnection.hpp>
#include <Vcl.ComCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE で管理されるコンポーネント
TEdit *E_ipadr;
TButton *Button1;
TIdTCPClient *IdTCPClient1;
TLabel *Label1;
TLabel *Label2;
TEdit *E_port;
TLabel *Label3;
TEdit *E_sendText;
TStatusBar *StatusBar1;
void __fastcall Button1Click(TObject *Sender);
private: // ユーザー宣言
void __fastcall commTCP(int timeout_msec);
public: // ユーザー宣言
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Unit1.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::commTCP(int timeout_msec)
{
// 1. Connect
IdTCPClient1->Host = E_ipadr->Text;
int portNum = StrToIntDef(E_port->Text, -1);
if (portNum < 0) {
String wrnmsg = L"Error: Invalid port number:" + E_port->Text;
MessageDlg(wrnmsg, mtError, TMsgDlgButtons() << mbOK, 0);
return; // fail to get portNum
}
IdTCPClient1->Port = portNum;
try {
IdTCPClient1->Connect();
} catch (...) {
String wrnmsg = L"Failed to connect to " + E_ipadr->Text;
MessageDlg(wrnmsg, mtError, TMsgDlgButtons() << mbOK, 0);
return;
}
// 2. send
String sndTxt = E_sendText->Text;
IdTCPClient1->IOHandler->WriteLn(sndTxt);
// 3. receive
String rcvd;
DWORD startTim = GetTickCount();
while(1) {
if (GetTickCount() - startTim > timeout_msec) {
String wrnmsg = L"Error: Receive timeout";
MessageDlg(wrnmsg, mtError, TMsgDlgButtons() << mbOK, 0);
break;
}
if (IdTCPClient1->IOHandler->InputBufferIsEmpty() == false) {
Application->ProcessMessages();
Sleep(100);
continue;
}
if (IdTCPClient1->IOHandler->CheckForDataOnSource(100) == false) {
Application->ProcessMessages();
continue;
}
//int size = IdTCPClient1->IOHandler->InputBuffer->Size;
rcvd = IdTCPClient1->IOHandler->ReadLn();
String infmsg = L"Received: " + rcvd;
MessageDlg(infmsg, mtInformation, TMsgDlgButtons() << mbOK, 0);
break;
}
IdTCPClient1->IOHandler->InputBuffer->Clear();
IdTCPClient1->Disconnect();
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
IdTCPClient1->ConnectTimeout = 2000; // msec
IdTCPClient1->ReadTimeout = 2000; // msec
commTCP(/*timeout_msec=*/1000);
}
//---------------------------------------------------------------------------
実行例
A. 相手先がListenしていない
B. 相手先がListenしている, 応答しない
C. 相手先がListenしている, 応答する
備考
- 接続、送信、受信を一括にしているため、他の処理がブロックされる
- 受信タイムアウトはGetTickcount()での処理
- ReadTimeoutを使った処理は未消化
使用ツール
-
NonSoft - TCP/IPテストツール
- AとBの実行例について使用させていただきました
-
自作のTCP/IP echo server
- Cの実行例について使用しました
関連記事
- C++ Builder XE4, 10.2 Tokyo >TIdTCPClientを使った送信例
- c++ builder XE4 > tcpサーバ / tcpクライアント
- C++ Builder XE4, 10.2 Tokyo > TCPサーバ > echo server > 1対1通信 | (1対N通信)
余談: 記事消した
苦労して記載した記事を間違って消してしまった。
@ 生きるのが楽しくなる15の習慣 by 日野原重明さん
p81 トーマス・カーライルの名著「フランス革命史」
そこで事件は起こりました。友人の家のお手伝いさんが、それを書き損じの原稿と勘違いし、ストーブで燃やしてしまったのです。
それを知ったカーライルはひどく落胆し、やはり「もう同じものは書けない」と気力も失ってしまったとのことでした。
ところが、彼の妻は立派でした。夫に同情しながらも、きっぱりこう言ったそうです。
「このまま書かないですむくらいなら、最初から書く必要がなかったものなのよ」
カーライルはこの言葉に奮起しました。なぜなら、それはカーライルにとってどんなことをしても書かなければならないテーマだったからです。彼は再び机に向かい、そして誕生した本は、160年以上の年月を経た今なお、世界の名著とたたえられています。
code v0.2 > ReadLn()での受信待ち
ReadLn()において受信タイムアウトを指定できるようだ。
Unit1.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
/*
v0.2 2018/01/11
- ReadLn()の引数での受信タイムアウト処理
v0.1 2018/01/11
- whileでの受信タイムアウト処理
*/
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::commTCP(int timeout_msec)
{
// 1. Connect
IdTCPClient1->Host = E_ipadr->Text;
int portNum = StrToIntDef(E_port->Text, -1);
if (portNum < 0) {
String wrnmsg = L"Error: Invalid port number:" + E_port->Text;
MessageDlg(wrnmsg, mtError, TMsgDlgButtons() << mbOK, 0);
return; // fail to get portNum
}
IdTCPClient1->Port = portNum;
try {
IdTCPClient1->Connect();
} catch (...) {
String wrnmsg = L"Failed to connect to " + E_ipadr->Text;
MessageDlg(wrnmsg, mtError, TMsgDlgButtons() << mbOK, 0);
return;
}
// 2. send
String sndTxt = E_sendText->Text;
IdTCPClient1->IOHandler->WriteLn(sndTxt);
// 3. receive
String rcvd;
rcvd = IdTCPClient1->IOHandler->ReadLn(L"", timeout_msec);
if (rcvd.Length() > 0) {
String infmsg = L"Received: " + rcvd;
MessageDlg(infmsg, mtInformation, TMsgDlgButtons() << mbOK, 0);
} else {
String wrnmsg = L"Error: Receive timeout";
MessageDlg(wrnmsg, mtError, TMsgDlgButtons() << mbOK, 0);
}
IdTCPClient1->IOHandler->InputBuffer->Clear();
IdTCPClient1->Disconnect();
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
IdTCPClient1->ConnectTimeout = 2000; // msec
IdTCPClient1->ReadTimeout = 2000; // msec
commTCP(/*timeout_msec=*/1000);
}
//---------------------------------------------------------------------------