6

完了ポートを使用して非同期 I/O を利用する Windows NT で tcp サーバーを作成しています。TcpSocket クラス、TcpServer クラス、および読み取りが完了したときの onRead() など、I/O 操作が完了したときに呼び出すいくつかの (仮想関数) コールバックがあります。また、接続が確立されたときの onOpen() と、接続が閉じられたときの onEof() などもあります。私は常にソケットの保留中の読み取りを持っているので、ソケットが効果的にデータを取得する場合 (読み取りはサイズ > 0 で完了する)、代わりにクライアントがクライアント側からソケットを閉じる場合(読み取りはサイズ == 0 で完了) onEof() を呼び出し、サーバーはクライアントが closesocket(server_socket); でソケットを閉じるタイミングを認識します。その側から。

すべて正常に動作しますが、次のことに気付きました。

closesocket(client_socket); を呼び出すと、クライアント側ではなく、接続のサーバー側のエンドポイントで (linger {true, 0} を設定するかどうかに関係なく)、保留中の読み取りはエラーとして完了します。つまり、読み取りサイズは == になるだけではありません。 0 だけでなく、GetLastError() もエラーを返します: 64 または 'ERROR_NETNAME_DELETED'。私はこれについてウェブで多くのことを検索しましたが、興味深いものは何も見つかりませんでした。

それから私は自問しました:しかし、これは本当のエラーですか?つまり、これは本当にエラーと見なすことができますか?

問題は、サーバー側では、closesocket(client_socket); のときに onError() コールバックが呼び出されることです。onEof() の代わりに。だから私はこれを考えました:

この「ERROR_NETNAME_DELETED」「エラー」が受信されたときに、 onError() の代わりに onEof() を呼び出すとどうなりますか? それはいくつかのバグや未定義の動作を導入しますか? 私がこの質問をするようになったもう 1 つの重要な点は次のとおりです。

'ERROR_NETNAME_DELETED' でこの読み取り完了を受け取ったときに、OVERLAPPED 構造、特に基礎となるドライバーの NTSTATUS エラー コードを含むoverlapped->Internal パラメーターを確認しました。NTSTATUS エラー コード [ http://www.tenox.tc/links/ntstatus.html ] のリストを見ると、エラーである「ERROR_NETNAME_DELETED」が NTSTATUS 0xC000013B によって生成されていることがはっきりとわかりますが、 「STATUS_LOCAL_DISCONNECT」と呼ばれます。まあ、それはエラーの名前のようには見えません。エラーである「ERROR_IO_PENDING」のように見えますが、正しい動作のステータスでもあります。

では、OVERLAPPED 構造体の Internal パラメータをチェックするとどうなるでしょうか。これが == から 'STATUS_LOCAL_DISCONNECT' の場合、onEof() コールバックの呼び出しが実行されます。物事を台無しにしますか?

さらに、closesocket(client_socket); を呼び出す前に DisconnectEx() を呼び出すと、サーバー側から言う必要があります。そのエラーは表示されません。しかし、 DisconnectEx() を呼び出したくないのはどうですか? たとえば、サーバーがシャットダウンしていて、DisconnectEx() のすべての完了を待機したくないが、接続されているすべてのクライアントを閉じたい場合などです。

4

3 に答える 3

3

エラー状態をどのように扱うかは完全にあなた次第です。あなたの場合、このエラー状態は完全に予想されるものであり、予想される状態として扱うことは完全に安全です。

この性質の別の例は、API 関数を呼び出すが、提供するバッファーの大きさがわからない場合です。したがって、十分な大きさになることを期待するバッファーを提供します。ただし、API 呼び出しが失敗した場合は、最後のエラーが であることを確認しますERROR_INSUFFICIENT_BUFFER。これは予想されるエラー状態です。その後、より大きなバッファーで再試行できます。

于 2013-01-24T11:14:25.903 に答える
2

エラー状態をどのように処理するかはユーザー次第ですが、問題はコード内の潜在的な問題 (論理エラーから未定義の動作まで) の兆候です。

最も重要なポイントは、 の後で SOCKET ハンドルに触れないことですclosesocket。EOFで何をしますか?EOF を検出した場合、私たちの側では論理的ですが、それはハンドラーでclosesocket実行できないことです。既に発生しており、ハンドルが無効であるためです。ERROR_NETNAME_DELETEDclosesocket

保留中の読み取りが の直前に (実際のデータが利用可能で) 完了しclosesocket、アプリケーションが の直後に closesocketそれを検出した場合に何が起こるかを想像することも有益です。着信データを処理し、... 同じソケット ハンドルを使用してクライアントに応答を送信しますか? そのハンドルで次の読み取りをスケジュールしますか? それはすべて間違っており、ERROR_NETNAME_DELETEDそれについてあなたに話すことはありません.

直前の非常に不幸な瞬間に、保留中の読み取りが EOF で完了するとどうなりclosesocketますか? 通常のOnEofコールバックが発生し、そのコールバックが発生したclosesocket場合、それは再び間違っています。

closesocketあなたが説明した問題は、あるスレッドで実行され、別のスレッドが I/O の完了を待っている場合、より深刻な問題を示唆している可能性があります。最初のスレッドが呼び出している間に、別のスレッドがWSARecv/を呼び出していないことを確信していますか? winsock はほとんどの場合動作しているように見えますが、これは未定義の動作です。ReadFileclosesocket

要約すると、ソケットハンドルが閉じられたために役に立たないことに気付いていない場合、読み取りの完了 (または失敗) を処理するコードは正しくありません。の後closesocket、保留中の I/O 完了を待つと便利ですOVERLAPPED。そうしないと構造を再利用できないためです。しかし、ソケットがまだ開いている状態で、通常の操作中に発生したかのようにこの種の完了を処理しても意味がありません(エラー/ステータス コードは関係ありません)。

于 2013-01-24T11:31:52.597 に答える
0

間違ったメソッドを呼び出しています。WSAGetLastError()を呼び出す必要があります。Winsock API呼び出し後のGetLastError()の結果は無意味です。

于 2013-01-24T22:29:28.250 に答える