2017-02-14 C++ Builder XE4 / Socket > ReceiveBuf()では0x00を途中に含めた指定長さの文字列を受信する / ReadBytes()では0x00までの長さの文字列だけを受信する > ReadChar()試用 / ReadString() + memcpy()使用 > ReadStream()使用


動作環境
C++ Builder XE4

TClientSocketにて通信断が発生する状況の対処を検討している。
TClientSocketからIndyのTIdTCPClientに変更をしようとしている。

  1. ClientSocket->Socket->ReceiveBuf(void *Buf, int Count);
  2. IdTCPClient1->Socket->ReadBytes()で置き換えようとした。

方法1の場合は、hello<0x00>worldという文字列が来た時には、worldまで受信できた。
方法2の場合は、hello<0x00>までの受信となった。

受信方法を変更することになる。

ReadChar()試用

変更点

IndyのSocketにはReadChar()があるようだ。

Unit1.cpp
    char zbuf[50] = { 0 };
    char code;
    for(int idx=0; idx < len; idx++) {
        code = IdTCPClient1->Socket->ReadChar();
        zbuf[idx] = code;
    }

上記で読込みができた。

トラブル

ReceiveBuf()を使っていた時は、20文字の読取りに対してGetTickCount()では0となるほどの速度で処理される。

一方で、ReadChar()を使うとfor()で20回まわして同じ処理で2940msecかかるようだ。
20文字でなく18文字までの読取りでは0msecで処理される。

20文字の読込みでは11文字目まではReceiveBuf()とReadChar()で同じ。12文字目以降は以下のように異なっている。

  • ReceiveBuf()受信
    • 0xE0 0x00 0xEA 0x00 0x00 0x00 0x00 0x00 0x0F
  • ReadChar()受信
    • 0xFD 0xFD 0x00 0x00 0x00 0x00 0x0F 0x4C 0x43

本来0x0Fで終端するはずが、ReadChar()では18文字目で終端文字列を読込んでしまっている。
この部分がおかしい。

ReadString()使用

ReadString() + memcpy()

ReadChar()の繰返しでは読み落としが生じていると推測した。
別の方法としてReadString()を使うことにした。

ReadString()で受信するString型文字列をchar []型に変換する方法としてstrncpy()を試した。この場合、0x00が途中にあるとそこまでのコピーしかされない。

色々試したところ、memcpy()だと途中に0x00があっても、指定の長さを取得できることが分かった。

Unit1.cpp
    AnsiString str = IdTCPClient1->IOHandler->ReadString(len);
    char zbuf[50] = { 0 };
    //strncpy(zbuf, str.c_str(), len);
    memcpy(zbuf, str.c_str(), len);

これでReceiveBuf()と同じ受信処理となった。

トラブル

上記で受信できたと思ったが、20文字中の2文字 (0xE0, 0xEA)が(0x3F, 0x3F)として認識されてしまう問題が生じている。

ReadStream()使用

ReadString()では128以上のコードが本来のコードでなく変換されてしまうことが分かった。
http://qiita.com/7of9/items/7bab0cf9f4311f1ebbd1

ReadString()でなく、ReadStream()を使ってみた。

Unit1.cpp
    TMemoryStream *strm = new TMemoryStream();
    IdTCPClient1->IOHandler->ReadStream(strm, len);
    strm->Seek(0, 0); // 先頭に移動
    char zbuf[50] = { 0 };
    strm->ReadData(zbuf, len);
    delete strm;

こちらでは20文字すべて、ReceiveBuf()使用時と同じになった。

strmの定義にはunique_ptrを使うのが良い。