C++ Builder XE4
TClientSocketにて通信断が発生する状況の対処を検討している。
TClientSocketからIndyのTIdTCPClientに変更をしようとしている。
-
ClientSocket->Socket->ReceiveBuf(void *Buf, int Count);
を -
IdTCPClient1->Socket->ReadBytes()
で置き換えようとした。
方法1の場合は、hello<0x00>world
という文字列が来た時には、worldまで受信できた。
方法2の場合は、hello<0x00>
までの受信となった。
受信方法を変更することになる。
ReadChar()試用
変更点
IndyのSocketにはReadChar()があるようだ。
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があっても、指定の長さを取得できることが分かった。
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()を使ってみた。
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を使うのが良い。