一つのC〓〓TCPの非同期プログラムで発生した問題
最近メンテナンス会社の一つであるsocketサービスは主に二つのsocket serverサービスを提供しています。両端に接続されたプログラムに対してデータの透明転送を行います。
プログラム実行中に問題が発生し、プログラムの端がGPRSデバイスであることは周知の通り、GPRSデバイスのネットワーク接続は非常に問題がないため、多くの「奇妙」な問題が発生します。実際のプロセスでは、プログラムが何時間も実行されると、無線端末のsocket serverが切断されてしまい、もう開けられなくなります。長い間探しましたが、見つけられませんでした。
Wirestharkで通信文をキャッチすると、一般的にTCPの三回握手時に発生する問題です。従来のTCPの3回の握手は、TCPの標識から簡単に見ることができます。SYN-SYN ACK-ACKは、実際に問題が発生した場合、SYN-RST ACKとしてマークされます。
サービス側は、クライアントの接続を積極的に拒否するために、リセットの標識を発行していることが明らかになった。
プログラムのserver部分コードは、通常のTCP非同期プログラム方式を採用しています。MSDNコードです。
acceptのコールバック関数として、コードの中でtry.ccachを使って異常を捕捉します。実際の問題はここにあるかもしれません。コードは以下の通りです。
プログラム実行中に問題が発生し、プログラムの端がGPRSデバイスであることは周知の通り、GPRSデバイスのネットワーク接続は非常に問題がないため、多くの「奇妙」な問題が発生します。実際のプロセスでは、プログラムが何時間も実行されると、無線端末のsocket serverが切断されてしまい、もう開けられなくなります。長い間探しましたが、見つけられませんでした。
Wirestharkで通信文をキャッチすると、一般的にTCPの三回握手時に発生する問題です。従来のTCPの3回の握手は、TCPの標識から簡単に見ることができます。SYN-SYN ACK-ACKは、実際に問題が発生した場合、SYN-RST ACKとしてマークされます。
サービス側は、クライアントの接続を積極的に拒否するために、リセットの標識を発行していることが明らかになった。
プログラムのserver部分コードは、通常のTCP非同期プログラム方式を採用しています。MSDNコードです。
// This server waits for a connection and then uses asynchronous operations to
// accept the connection with initial data sent from the client.
// Establish the local endpoint for the socket.
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000); // Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp ); // Bind the socket to the local endpoint, and listen for incoming connections.
listener.Bind(localEndPoint);
listener.Listen(100); while (true)
{ // Set the event to nonsignaled state.
allDone.Reset(); // Start an asynchronous socket to listen for connections and receive data from the client.
Console.WriteLine("Waiting for a connection..."); // Accept the connection and receive the first 10 bytes of data.
// BeginAccept() creates the accepted socket.
int receivedDataSize = 10;
listener.BeginAccept(null, receivedDataSize, new AsyncCallback(AcceptReceiveDataCallback), listener); // Wait until a connection is made and processed before continuing.
allDone.WaitOne();
}
} public static void AcceptReceiveDataCallback(IAsyncResult ar)
{ // Get the socket that handles the client request.
Socket listener = (Socket) ar.AsyncState; // End the operation and display the received data on the console.
byte[] Buffer; int bytesTransferred;
Socket handler = listener.EndAccept(out Buffer, out bytesTransferred, ar); // , socket
listener.BeginAccept(null, receivedDataSize, new AsyncCallback(AcceptReceiveDataCallback), listener);
}
問題の位置付けを経て、プログラムの非同期受信エコーに問題があったと判断できますが、実際にデバッグ情報を追加したところ、プログラムのポートが開けられなくなりました。再度、コールバック関数の操作を行います。打つ情報はありません。TCP非同期プログラミングは、一般的にペアの出現BeginnXXX...endXXXであり、再度コールバック関数によって具体的な処理を行う。acceptのコールバック関数として、コードの中でtry.ccachを使って異常を捕捉します。実際の問題はここにあるかもしれません。コードは以下の通りです。
public static void AcceptReceiveDataCallback(IAsyncResult ar)
{ // Get the socket that handles the client request.
Socket listener = (Socket) ar.AsyncState; // End the operation and display the received data on the console.
byte[] Buffer; int bytesTransferred; try{
Socket handler = listener.EndAccept(out Buffer, out bytesTransferred, ar);
} catch( 1 e){
... return;
} catch( 2 e){
... return;
} // , socket
listener.BeginAccept(null, receivedDataSize, new AsyncCallback(AcceptReceiveDataCallback), listener);
}
プログラムは実際にポートが開けられなくなる前に「異常1」/「異常2」に入ったことがあり、プログラムがリセットされた可能性が高いと判断し、再配達ができない。この時、すべてのポートが開いて操作すると、sockett.listenの列に入ります。accpetの列の内容はbegin.end操作では取り出せません。列がいっぱいになったら、socketの底のプロトコルスタックは新しいsocketの接続を拒否します。