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

```txt:動作環境
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](https://qiita-image-store.s3.amazonaws.com/0/32870/d811d47a-bfc3-c1bc-2e48-3329c5c9ef0d.png)

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

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/415353d4-6b44-119d-6b31-0046a6e56720.png)

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

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/c6ee129e-9eba-4349-a587-15ae7f6a72d7.png)

## 備考

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

## 使用ツール

- [NonSoft - TCP/IPテストツール](http://nonsoft.la.coocan.jp/Download/SocketTool/index.html)
    - AとBの実行例について使用させていただきました
- [自作のTCP/IP echo server](https://qiita.com/7of9/items/d9367b0e2c8c844faa8e)
    - Cの実行例について使用しました

## 関連記事

- [C++ Builder XE4, 10.2 Tokyo >TIdTCPClientを使った送信例](https://qiita.com/7of9/items/0f09d499de5ec4fec428)
- [c++ builder XE4 > tcpサーバ / tcpクライアント](https://qiita.com/7of9/items/51bd31318c6e1332adbf)
- [C++ Builder XE4, 10.2 Tokyo > TCPサーバ > echo server > 1対1通信 | (1対N通信)](https://qiita.com/7of9/items/8925cce71cbf43448d55)

## 余談: 記事消した

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

@ 生きるのが楽しくなる15の習慣 by 日野原重明さん
> p81 トーマス・カーライルの名著「フランス革命史」
> そこで事件は起こりました。友人の家のお手伝いさんが、それを書き損じの原稿と勘違いし、ストーブで燃やしてしまったのです。
それを知ったカーライルはひどく落胆し、やはり「もう同じものは書けない」と気力も失ってしまったとのことでした。
ところが、彼の妻は立派でした。夫に同情しながらも、きっぱりこう言ったそうです。
「このまま書かないですむくらいなら、最初から書く必要がなかったものなのよ」
カーライルはこの言葉に奮起しました。なぜなら、それはカーライルにとってどんなことをしても書かなければならないテーマだったからです。彼は再び机に向かい、そして誕生した本は、１６０年以上の年月を経た今なお、世界の名著とたたえられています。

### code v0.2 > ReadLn()での受信待ち

- [Delphi Indy ReadLn with timeout](https://stackoverflow.com/questions/19371049/delphi-indy-readln-with-timeout)

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);
}
//---------------------------------------------------------------------------
```