C++ Builder XE4
前置き
UDP通信にて<STX>
,<ETX>
,<BCC>
を含めたコードの送受信を試行している。
フォーマットとしては<STX>ペイロード<ETX><BCC>
。
http://qiita.com/7of9/items/a91a396f03fac9516814
その際に、<BCC>
として計算した値が0xE2
などの場合に別のコードに変わってしまうという症状に遭遇している。
関連しそうなのは以下。
http://qiita.com/7of9/items/7bab0cf9f4311f1ebbd1
ASCIIコード
http://www9.plala.or.jp/sgwr-t/c_sub/ascii.html
0x00から0x7Fまで定義されている。
IndyのTidUDPServerなどでコードページ932で受信した時(もしくはchar型変換時)に0x80以降のコードが化けているという状況。
ツールの動作確認
調査の前に、確実に動作するツールを確保しておく。
NonSoftさんのUDP/IPテストツールを使わせていただきます。
http://nonsoft.la.coocan.jp/Download/UdpTool/index.html
AAA<E2>
というコードを2つのOS上で送受信してみた。
(以下、0xE2
を<E2>
と記載)
送信側も受信側もUDP/IPテストツールを使用した。
受信側で以下となり、<E2>
のコードを含めて送受信は成功している。
接続 (6000 )
->受 192.168.0.31 (4000 )<41><41><41><e2>
以降の動作確認にUDP/IPテストツールを使うことで、不具合の絞込みが可能である。
コードページ932での受信 > 文字化け
//---------------------------------------------------------------------------
#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::FormCreate(TObject *Sender)
{
IdUDPServer1->DefaultPort = 6000;
IdUDPServer1->Active = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::IdUDPServer1UDPRead(TIdUDPListenerThread *AThread, const TIdBytes AData,
TIdSocketHandle *ABinding)
{
AnsiString rcvdStr;
Idglobal::_di_IIdTextEncoding encSJIS;
encSJIS = IndyTextEncoding(932);
int init_pos = 0;
rcvdStr = encSJIS->GetString(AData, init_pos, AData.Length);
int nop=1; // for breakpoint
}
//---------------------------------------------------------------------------
上記のプログラムに対してUDP/IPテストツールからAAA<E2>
を送信してみた。
受信文字列は<41><41><41><81>
となった。
<E2>
は<81>
に化けてしまっている。
対応案
<BCC>
の値にはunsigned char型の場合<00>
から<FF>
の任意の値が入る。<80>
以降のコードも受信できるようにするためには、いくつかの対応案が考えられる。
1. すべてをBCDに変換する
<80>
の場合、8(<38>)
と0(<30>)
に変換するというBCD(Binary Coded Decimal)表現を使う。
http://qiita.com/7of9/items/c9febc6d0403d8fd6c0e
これにより、送受信される文字列はつねにASCIIコードの値に収まる。
一方で、1バイトの文字を送受信するために2バイトが必要になるため、送受信データサイズが2倍になるという欠点はある。
こちらの方法は使用例をいくつか見たことがある。
2. <BCC>
だけをBCDにする
<STX>ペイロード<ETX><BCC>
という通信プロトコルの場合、文字化けが発生するのは<BCC>
の部分だけ。(<STX>
と<ETX>
はASCIIコードのため文字化けしない。)
<BCC>
の部分だけをBCDで変換するという案。
送受信データのサイズが抑えられる。
ただし、こちらの方法は標準的な方法ではないかもしれない。
(他の人の使用例を見た記憶がない)。
3. ペイロード,BCC値<CR><LF>
にする
<STX>ペイロード<ETX><BCC>
のように<STX>
と<ETX>
を使うことにこだわらない場合、以下のような形式にするということも考えられる。
ペイロード,<BCC(の数値)><CR><LF>
<BCC>
が<E2>
の場合、<E2>
はunsigned char型では226になる。以下のようにして送受信するという案。
ペイロード,226<CR><LF>
利点としては、受信側はカンマ区切りでペイロードと<BCC>
を分離できる。
欠点としては、<STX>
と<ETX>
がないため、送受信文字列の区切りが捉えにくくなる。UDP送受信でこれが問題になるかは未消化。
<CF><LF>
終端でカンマ区切り文字列を送受信する例はRS-232C通信などでは多く見てきた。
UDPでは以下の記事内のリンク先に例がある。
http://qiita.com/7of9/items/1c44f6df88733ee586c1
(追記 2017/03/21)
シリアル通信では以下の「Packet type: 220」に1F
のようなchecksumがある。
http://www.pvelectronics.co.uk/PA6B/PA6B_commands.pdf
4. コードページ932以外での送受信にする
ASCIIで受信する限りは文字化けが発生する。
ASCII(コードページ932)以外で受信するという案。
こちらについては調査不足。
1つの<BCC>
の文字化け対策のために、関連するコードをすべて変更するのが良い案かは思案中。
他の案
他にも方法があるかもしれない。
それらの方法の選択の視点は「標準的な方法かどうか」。
5. ToHex()
を使う案
@rryu さんのコメントをもとに見つけました。