質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

WebSocket

WebSocketとは双方向・全二重コミュニケーションのためのAPIでありプロトコルのことを指します。WebSocketはHTML5に密接に結びついており、多くのウェブブラウザの最新版に導入されています。

ASP.NET

ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。

Q&A

解決済

2回答

7767閲覧

ClientWebSocket.CloseAsync メソッドが完了しない

退会済みユーザー

退会済みユーザー

総合スコア0

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

WebSocket

WebSocketとは双方向・全二重コミュニケーションのためのAPIでありプロトコルのことを指します。WebSocketはHTML5に密接に結びついており、多くのウェブブラウザの最新版に導入されています。

ASP.NET

ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。

0グッド

1クリップ

投稿2018/03/04 09:27

編集2018/03/05 13:29

環境

サーバーはlocalhostにて実行しており、サーバー側とクライアント側は同一のマシンです。

マシン

  • OS:Windows 10
  • デバッグ環境:Visual Studio Community 2017

サーバー側

  • フレームワーク:ASP.NET Core, .NET Core 2.0

クライアント側

  • フレームワーク:.NET Framework 4.6.1

問題

WebSocketを用いたサーバー・クライアント間のデータのやりとりを実装しています。
クライアント側はClientWebSocketを用いて実装していますが、ClientWebSocket.CloseAsyncを呼び出すとプログラムが進行しなくなってしまいます。以下にソースコードの関係の有りそうな箇所を掲載しますので、どこが間違っているか、もしくはCloseAsyncでプログラムが進行しなくなる時にありがちな事などご指摘いただけると助かります。

テスト用のプロジェクトでは再現しないため再現コードが書けずに申し訳ありませんが、再現するほうのプロジェクトでは以下のようなメソッド呼び出しでコネクションを閉じようとしています。大規模なコードであるため全ては載せられませんでした。

// クライアント側 if (WebSocket.State == WebSocketState.Open) { // このメソッド呼び出しが完了せず、プログラムが進行を停止する WebSocket.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, "正常終了", CancellationToken.None).Wait(); }

サーバー側は以下のように応答します。

// サーバー側 // コネクションが確立したときに呼び出され、データの受信を待機するメソッド public async Task ListenConnectionAsync(OpenRoom room) { var buffer = new byte[1024 * 4]; // 接続していればリクエストを待つ while (room.Receiver.State == System.Net.WebSockets.WebSocketState.Open) { var result = await room.Receiver.ReceiveAsync( buffer: new ArraySegment<byte>(buffer), cancellationToken: CancellationToken.None); // 通常のデータ受信 if (result.MessageType == System.Net.WebSockets.WebSocketMessageType.Text) { // CastEventData はサーバー・クライアント間でやりとりするデータ try { var message = CastEventData.Deserialize(buffer, result.Count); await room.ReceiveAsync(message); } catch(Exception e) { Console.WriteLine(e); } } // コネクションを閉じるリクエストを受信した場合 else if(result.MessageType == System.Net.WebSockets.WebSocketMessageType.Close) { // コネクションを閉じるリクエストに応答する await room.Receiver.CloseOutputAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, "正常終了", CancellationToken.None); break; } } } // CastEventData, CastDataはサーバー・クライアント間でやりとりするデータ public class OpenRoom { public string Name { get; set; } public CastData CastData { get; set; } public System.Net.WebSockets.WebSocket Receiver { get; set; } public List<System.Net.WebSockets.WebSocket> Senders { get; set; } internal Task ReceiveAsync(CastEventData message) { message.Event.Apply(CastData); // イベントに応じてサーバー上のデータを書き換える return Task.CompletedTask; } }

デバッグ内容

この問題について、ブレークポイントを置いてデバッグしています。クライアント側からコネクションを閉じるリクエストを送ると、サーバー側では「コネクションを閉じるリクエストに応答する」のコメントの部分が実行されました。これによってサーバーからクライアントへの応答もできているつもりなのですが、うまく行かず悩んでいるところです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2018/03/04 09:43

再現しないコードをいくら見ても解決しないのでは? 再現するプロジェクトでデバッグしてください。こちらではデバッガが使えないので。
退会済みユーザー

退会済みユーザー

2018/03/04 09:48

このソースコードは再現するプロジェクトのソースコードです。
Zuishin

2018/03/04 09:53

再現コードが書けないとあるので違うなら書き直してください。
umyu

2018/03/04 16:51 編集

WebSocketはあまり触ったことが無いので、こちらで。サーバー側のコネクションを閉じるリクエストを受信した場合の判定ですが、CloseもしくはCloseReceivedではないでしょうか? ■参考情報 WebSocket closing handshake https://wiki.suikawiki.org/n/WebSocket%20closing%20handshake
退会済みユーザー

退会済みユーザー

2018/03/05 13:26 編集

皆さんご返信ありがとうございます。クライアントからのリクエストをサーバーが受け取る前に、サーバー側のwhileループを抜けてしまってリクエストが受け取れていないのではないか、ということでしょうか?今回の問題では、実行するとサーバー側にある「コネクションを閉じるリクエストに応答する」のコメントの部分を通ったので、おそらくwhileループの条件の問題では無いのではないかと思っています(質問に書けていなくて申し訳ありません。追記します)。試しにwhileループの条件式に`Closed`や`CloseReceived`を含めてみましたが、効果はありませんでした。
guest

回答2

0

ベストアンサー

解決しました。

定期的な更新処理の呼び出しが必要な独自の同期コンテキストを使用した状況下で、更新処理なくTaskWaitしたためにデッドロックが発生していたようでした。

今回のプロジェクトはゲームプログラムであり、以下の記事に出てくるようなゲームループを利用した同期コンテキストを用いていました。

コルーチン用途にawaitを使うためのSynchronizationContext

この同期コンテキストは定期的に更新処理を呼び出す必要があり、問題のコードでWaitメソッドを使用した場所では定期的な更新処理が呼び出されていませんでした。独自の同期コンテキストを正しく更新して使うように変更すると、CloseAsyncメソッドが正しく完了するようになりました。

今回の問題は、Waitメソッドで待機したタスクは同期コンテキストへ投げられないだろうという勘違いが原因でした。

情報の不足した質問により大変失礼いたしました。皆様ご回答ありがとうございました。

投稿2018/03/07 14:00

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

メソッドがUIスレッド上で実行されていて、クライアント側のWait処理でTaskの完了を待っているためではないでしょうか?

https://msdn.microsoft.com/ja-jp/library/system.threading.tasks.task.wait(v=vs.110).aspx

投稿2018/03/06 01:46

編集2018/03/06 01:54
toro1

総合スコア90

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/03/07 12:39

クライアント側にてWaitメソッドの呼び出しをUIスレッドで行っているために、スレッドがブロックされてしまいプログラムが応答なしになってしまう問題があるということでしょうか?今回はスレッドが一定時間ブロックされることは許容できるのですが、問題はCloseAsyncメソッドがいつまで経っても完了せず、プログラムが進行しなくなってしまうことなのです。質問の情報が不足しており申し訳ありませんが、ご回答頂きありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問