決済端末と通信するいくつかのレガシーコードにバグがあります。
新しい支払いが開始される直前に、コードはSerialPortの内部読み取りバッファーをクリアしようとします。
コードを最小限に抑えました。.NETシリアルポートタイプを使用します。50msの読み取りタイムアウトが設定されています。次に、512バイトを読み取り、それ以上バイトが読み取られなくなるまで、またはTimeoutExceptionがスローされるまで読み取りを続けます。
大量のログを追加したところ、最初のRead(...)メソッドの呼び出しには、タイムアウトが50ミリ秒であっても、10〜15分かかることがありました。次に、TimeoutExceptionがスローされ、アプリケーションが続行されます。しかし、Read(...)中に、アプリケーションがハングします。
これは常に発生するとは限りません。Windows2000マシンは、何らかの理由でこのエラーが発生しやすいようです。
public class Terminal
{
private SerialPort _serialPort = new SerialPort();
public void ClearReadBuffer()
{
try
{
_serialPort.ReadTimeout = 50;
int length;
do
{
var buffer = new byte[512];
length = _serialPort.Read(buffer, 0, 512);
} while (length > 0);
}
catch (TimeoutException) {}
}
}
どんな助けでも大歓迎です。
PS:ほとんどのエラーレポートは、デバイスが一連の仮想COMポートをシミュレートするEdgePortに接続されているW2Kマシンから送信されます。そのドライバは、多数(8程度)のローカルCOMポートを作成します。
ただし、Windows 7からのレポートもあります。デバイスをPC(EdgePortなし)に直接接続した場合にも、問題を再現できます。ただし、それほど頻繁ではなく、遅延が発生した場合は10分ではなく、1〜2分程度です。
更新:これを修正するために多くのことを試みました。再現は困難でしたが、数千台のPCに配布されているため、現場で頻繁に発生しました。実際には、.NET2.0SerialPortタイプを別のオープンソースバージョンに置き換えました。1台のPCで問題なく動作し、実際に60〜70%の確率で再現できました。しかし、残念ながら、本番環境でのパイロットテスト中、問題は依然として発生し続けました。
決済端末のコードは数年前に書かれたもので、私はそれを別のアプリケーションに移植しました。移植中に、いくつかのコードをリファクタリングしましたが、元の機能を維持しました。端末と通信するとき、コードは次のようになります。
- スレッドプールから別のスレッドを起動します
- デバイスにメッセージを送信します
- 応答が受信されるか、タイムアウトが発生するまで、シリアルポートから読み取ります。
一方、メインスレッドには、Thread.Sleep(50)とApplication.DoEvents()呼び出しを含むwhileループがありました(うん!)。この「待機ループ」全体をリファクタリングして、WaitHandle(AutoResetEvent / ManualResetEvent)を利用しました。このハンドルが設定されるまで待っていました。問題なく動作しましたが、特定のPCでは、何かがトリガーされるまで、すべてのシリアルポート通信が数分間フリーズしていました。Application.DoEvents()の動作方法を再度有効にすると、問題は解消されました。
残念ながら、それはまだそこにあります、なぜそれがここで必要とされるのか、そしてなぜそれがそのような深刻な副作用を引き起こすのか私には謎です。アプリケーションは、他の5種類のシリアルポートデバイスをサポートします。これらのデバイスとの通信には、このようなものは必要ありませんでした。