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

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

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

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

Q&A

解決済

2回答

12461閲覧

SocketのRecieveメソッドにおいて、強制的にRecieveを中断(または停止)したい

ryo_se

総合スコア68

C#

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

0グッド

1クリップ

投稿2016/06/22 04:05

C#でソケットサーバープログラムを作成しております。

SocketクラスのReceiveメソッドは、電文が来るまでブロッキングする動作を行いますが、
これを強制的に中断か停止させる場合はどのようにすればよいのでしょうか?

また中断or停止した時に、クライアントとの接続は切れないようにしたいです。

調べてみたところ参考になるような文献が少なく、ご教示をいただきたく存じます。

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

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

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

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

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

guest

回答2

0

ベストアンサー

クライアントとの接続を維持しつつ、読みだしブロック中(データが着信していない)のReceive()を中止する方法は、Socketの標準機能では提供されていません。

クライアントとの接続を維持したままReceivee()の中止・中断するという処理は、クライアントアプリケーションから見た現象として2つのパターンがあり、このどちらにするのかを決めておく必要があります。つまり、サーバーが受信処理を中断していることをクライアントアプリケーション側で検出できるようにするかどうか、ということです。

例えば、サーバー側での受信中断が実はサーバー側での受信データ破棄という実装である場合、クライアントアプリケーションの送信処理には何の影響もありません。このため、サーバー側が受信中断中(受信データ読み捨て中)であることを、クライアントアプリは知ることができません。

そうではなく、サーバー側での受信中断がReceive()の呼び出し停止である場合、クライアント側でのSend()呼び出しは、いつかブロック(未受信のデータがソケットバッファを埋め尽くしてしまうため)してしまいます。これにより、クライアントアプリはサーバー側での中断をしることができます。

前者の実装は簡単だと思うので説明を割愛します。

後者の実装では、受信を中止したタイミングで読み出しブロック中のReceive()はキャンセルできないため、これはこのまま放置するしかないと思われます。そしてもし、受信再開までにクライアントからのデータがまったく来なければそれはそれでOKですよね。もし受信再開前にデータが来たら、それはサーバー側でバッファに保存、そしてその直後のReceive()呼び出しを取りやめる、とような処理を実装しなければならないでしょう。

ご参考になれば。

投稿2016/06/22 14:08

tkanda

総合スコア2425

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

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

ryo_se

2016/06/22 15:29

ご回答ありがとうございます。 私が実装したいのは後者のパターンになります。 今回、接続が確立した状態で受信の可否をチェンジできる受信停止モードを設ける必要があります。(クライントからの電文送信で送信エラーを疑似的に生み出すために設けています) 何かしらのフラグで受信停止状態に切り替え、Receiveを実行しないようにしても、 Receiveでブロックしている状態では一度はクラインアントからの受信を正常に受ける必要があると思います(これがおっしゃられている「受信を中止したタイミングで読み出しブロック中のReceive()はキャンセルできない」に該当するかと思います) サーバー側でバッファに保存ということは、クライアント側からしてみれば一度は送信に成功するということになるかと思いますが、できればその挙動は避けたいです(Receiveで受け取った後にバッファに保存する処理のコードがイメージわかないのですが・・)。 別の手段でも構いませんので、これを受信停止モードにした直後から行うことはできないでしょうか?C#によるRecieveのノンブロッキングを探したりもしてみたのですが、良い文献が見つからず・・。
tkanda

2016/06/22 22:26

送信されるデータのサイズによりますが、サーバー側で受信を停止(Receive()を取りやめ)してもクライアント側のSend()がすぐにブロックするとは限りません。「ソケットバッファ」が埋め尽くされるまで、Send()は支障なく実行できてしまうことに注意が必要です。 またこのソケットバッファは実際にはクライアント側の送信バッファとサーバー側の受信バッファとして、それぞれTCP層の内部に実装されており、一般に相手側のバッファサイズをもう一方が事前に調べる手段はありません。従って、サーバー側で受信を停止してからどれくらいのサイズのデータを送ることができるかを知ることはできないのです。 ryo_seさんのアプリケーションの場合、サーバー側で受信を停止したことをクライアント側でただちに検出する必要があるので、そのような場合はサーバーからクライアントへ「停止」や「再開」を示す小さなデータを送信するのが一般的な解決策だと思います。 また、現在Receive()でブロック中のサーバーアプリケーションからそのようなフロー制御データをSend()することはできないので、全体的に非同期処理系(BeginReceive, EndReceive, BeginSend, EndSendなど)を使うように設計を見直す必要があるでしょう。あるいは、マルチスレッド化する必要があるかもしれません。 また、サーバーからクライアントへのデータ送信が通常行われていない(クライアントからサーバーへの一方通行の通信だけをおこなうシステムである)場合、このような「停止」「再開」データはクライアントへすぐに届きますから特に問題はないでしょう。 ただし、サーバーからクライアントへも常にデータが送られている場合、「停止」「再開」データを「帯域外データ(Out-Of-Band=OOB Data)」として送る必要があるかもしれません。サーバーがSend()したデータがクライアント側に読み取られていない(クライアントの受信バッファ内に滞留している)状況が想定される場合、「停止」「再開」データをOOBフラグをセットしたSend()で送ることで、それ以前に送った(未受信の)データを飛び越えてクライアント側でこれを受信することができます。
ryo_se

2016/06/22 23:41

基本的にsend中に受信を停止するということは今回の仕様では考慮しませんので、そのあたりは大丈夫です。 ですがクライアント側はできあいのものを使用するため、停止や再開といったステータスを送っても対処させることができません。 非同期処理かマルチスレッドでの受信対応をしたいと思いますが、よろしければサンプルをご紹介していただいてもよろしいでしょうか?
tkanda

2016/06/23 00:20

繰り返しになりますが、クライアントとの接続を維持したまま、既に呼び出してしまったReceive()やBeginReceive()を中止することはできません。 従いまして、残念ながらサンプルコードの心当たりはありません。 Receive()を無条件に呼び出さない方向でご検討されるしかないように思われます。
tkanda

2016/06/23 02:24

「Receive()を無条件に呼び出さない」方法についてのヒントですが、Receive()を呼び出す前に、ソケットに受信データが存在していることを確認する手段がありますのでこれを利用してはいかがでしょうか。 (1)、Socket.Select メソッド は、引数で指定したソケットで読み取り可能な受信データ長が1バイト以上になる、または相手側でソケットがクローズされるまでブロックします。 (2) Socket.Available プロパティは読み取り可能な受信データ長を取得できます。
tkanda

2016/06/23 02:28

ただし、このようにしてReceive()の呼び出しを制御することでサーバー側で読み出しを停止できたとしても、クライアント側のSend()がただちにブロックするわけではありませんので、その点ご注意ください。 前にも書きましたが、サーバー側で読み出しが停止された後も、ソケットバッファ(サーバー側のTCP受信バッファとクライアント側のTCP送信バッファ)が一杯になるまで、Send()は成功します。
ryo_se

2016/06/23 10:03

ありがとうございます!ご教示していただいた方法で思い通りの実装をすることができました。 こんなことができたとは目から鱗です、大変勉強になりました。
guest

0

tkandaさんの言うとおり、Receiveを止める=接続を切るなので、接続を維持してブロッキングさせずに他の処理をするのであれば、別スレッドに処理を分ける他ありません。
メインの処理をブロッキングしたくないという意図であれば、Socket受信の処理を別スレッド化しReceiveさせ続けて受信内容をどこかに移しておき、メインスレッド側から「Receiveの終わったデータを受け取る」という形になるかと思います。

投稿2016/06/23 00:45

masaya_ohashi

総合スコア9206

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問