C++ Builder XE4 > TCP > Clientからの送信と受信処理 > v0.3: TTimerによる受信リトライ
18885 ワード
動作環境
C++ Builder XE4
- v0.1, v0.2: C++ Builder XE4 > TCP > Clientからの送信と受信処理 > v0.1:whileでの受信タイムアウト, v0.2:ReadLn()での受信タイムアウト | 余談: 記事消した...
処理概要
- TCPサーバーに接続する
- TCPサーバーに文字列を送信する
- TCPサーバーからの応答を受信する
- タイムアウト付き
- 受信リトライ付き
v0.3の変更点
v0.1, v0.2においては、commTCP()の処理において受信完了(またはタイムアウト)まで処理を抜けることはなかった。
他の処理との並列性が失われる。
v0.3では以下のようにした。
- commTCP()では以下を行う
- 接続
- 送信
- 受信用TTimer起動
- 受信用TTimerで以下を行う
- 受信処理 (リトライ3回)
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>
#include <Vcl.ExtCtrls.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;
TTimer *ReceiveTimer;
void __fastcall Button1Click(TObject *Sender);
void __fastcall ReceiveTimerTimer(TObject *Sender);
private: // ユーザー宣言
void __fastcall commTCP(int timeout_msec);
void __fastcall disconnectTcp(void);
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;
//---------------------------------------------------------------------------
/*
*** 留意事項 (2018/01/11) ***
- [ReceiveTimer]のTagを受信リトライの回数として使用
*/
/*
v0.3 2018/01/11
- TTimerによる受信処理 (リトライ付き)
+ disconnectTcp()追加
+ [ReceiveTimer]追加
v0.2 2018/01/11
- ReadLn()の引数での受信タイムアウト処理
v0.1 2018/01/11
- whileでの受信タイムアウト処理
*/
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
ReceiveTimer->Enabled = false;
}
//---------------------------------------------------------------------------
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用TTimerの起動
ReceiveTimer->Tag = 3; // Retry
ReceiveTimer->Interval = 100; // msec
ReceiveTimer->Enabled = true;
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
IdTCPClient1->ConnectTimeout = 2000; // msec
IdTCPClient1->ReadTimeout = 2000; // msec
commTCP(/*timeout_msec=*/1000);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ReceiveTimerTimer(TObject *Sender)
{
// *** InputBufferIsEmpty()使用ではサーバー応答ありでもReadLn()まで到達することはなかった
// if (IdTCPClient1->IOHandler->InputBufferIsEmpty()) {
// ReceiveTimer->Tag -= 1;
// if (ReceiveTimer->Tag == 0) {
// ReceiveTimer->Enabled = false;
// disconnectTcp();
// //
// String wrnmsg = L"Error: Receive timeout";
// MessageDlg(wrnmsg, mtError, TMsgDlgButtons() << mbOK, 0);
// }
// return;
// }
String rcvd = IdTCPClient1->IOHandler->ReadLn(L"", 10); // msec
//String rcvd = IdTCPClient1->IOHandler->ReadLn();
if (rcvd == NULL || rcvd.Length() == 0) {
ReceiveTimer->Tag -= 1;
if (ReceiveTimer->Tag == 0) {
ReceiveTimer->Enabled = false;
disconnectTcp();
//
String wrnmsg = L"Error: Receive timeout";
MessageDlg(wrnmsg, mtError, TMsgDlgButtons() << mbOK, 0);
}
return;
}
ReceiveTimer->Enabled = false;
disconnectTcp();
String infmsg = L"Received: " + rcvd;
MessageDlg(infmsg, mtInformation, TMsgDlgButtons() << mbOK, 0);
}
void __fastcall TForm1::disconnectTcp(void)
{
IdTCPClient1->IOHandler->InputBuffer->Clear();
IdTCPClient1->Disconnect();
}
//---------------------------------------------------------------------------
備考
- InputBufferIsEmpty()を使ったリトライではサーバーが応答しても受信できなかった (失敗)
- ReceiveTimerのTagプロパティを受信リトライ回数として使ったが、ソースリーディングでは分かりにくい可能性がある
- きちんとした名前のprivate変数にした方が良いだろう
実行例
(v0.1, v0.2と同じ)
A. 相手先がListenしていない
B. 相手先がListenしている, 応答しない
C. 相手先がListenしている, 応答する
使用ツール
-
NonSoft - TCP/IPテストツール
- AとBの実行例について使用させていただきました
-
自作のTCP/IP echo server
- Cの実行例について使用しました
関連記事
Author And Source
この問題について(C++ Builder XE4 > TCP > Clientからの送信と受信処理 > v0.3: TTimerによる受信リトライ), 我々は、より多くの情報をここで見つけました https://qiita.com/7of9/items/79df813461ec68e717ef著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .