cppBuilder
indy
networkApp

C++ Builder XE4 > TCP > Clientからの送信と受信処理 > v0.1:whileでの受信タイムアウト, v0.2:ReadLn()での受信タイムアウト | 余談: 記事消した...

動作環境
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していない

qiita.png

B. 相手先がListenしている, 応答しない

qiita.png

C. 相手先がListenしている, 応答する

qiita.png

備考

  • 接続、送信、受信を一括にしているため、他の処理がブロックされる
  • 受信タイムアウトはGetTickcount()での処理
    • ReadTimeoutを使った処理は未消化

使用ツール

関連記事

余談: 記事消した

苦労して記載した記事を間違って消してしまった。

@ 生きるのが楽しくなる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);
}
//---------------------------------------------------------------------------