Acceptで止まるのをどうにかしたい〜VB2008

  • 2012.12.04 Tuesday
  • 12:57


追記に解決方法を上げましたので、そこまですっ飛ばしてください。


この方法が正しいやり方だとは思いませんが、とりあえず、覚え書き

例えば、ザックリこんなプログラムを書いてみる
 
Dim bsSocket As Sockets.Socket = New Sockets.Socket(Sockets.AddressFamily.InterNetwork, Sockets.SocketType.Stream, Sockets.ProtocolType.Tcp)
Dim acSocket As Sockets.Socket = Nothing
‌ 
Dim bsEndPos As IPEndPoint = New IPEndPoint(IPAddress.Parse("192.168.0.1"), 12345I)
‌ 
Call bsSocket.Bind(bsEndPos)
Call bsSocket.Listen(1I)
‌ 
acSocket = bsSocket.Accept '接続を待つ

おそらく、Acceptでダンマリになってしまうと思う。

相手から接続、例えば、こんな感じのソースを書いてみると、
 
Dim bsSocket As Sockets.Socket = New Sockets.Socket(Sockets.AddressFamily.InterNetwork, Sockets.SocketType.Stream, Sockets.ProtocolType.Tcp)
‌ 
Call bsSocket.Connect("192.168.0.1"12345I) '接続する

これを起動して、Connectしたタイミングで、上ソースのAcceptが解除される。


これらは全体的に正しい動きである。


さて、ここから本題、
例えば、FTPのActiveのような感じで、クライアントから接続した後に、
逆にサーバから接続されるようなプログラムを作った場合、
ごく希にAcceptでダンマリになってしまう。

自分から自分のテストの時はほぼ問題ないが、
自分からテストサーバとか通信が長くなれば長くなるほどロスが出てくる。

おそらく、処理速度が追いついていないせいで、
サーバは接続したつもり、クライアントは待ちぼうけというような状態になってしまう。

こういった場合はAcceptでダンマリされても困る。


解決としては、サーバプログラムで、接続を数ミリ秒でも遅らせるような仕掛けを作るか、
クライアント側で別スレッドでAcceptを監視して、一定時間来なければ、Acceptをタイムアウトするような感じにすればいいと考える。

が、スレッド自体が面倒なので、できればそんな手間はかけたくない場合もある。

それこそコネクトに失敗したのだから、Try Catchでエラーでも発生してくれた方が、まだうれしい。


そんなときは、Blockingを無効にしてみるとよいかもしれない(これが正しいかどうかは分からないが)。
もし、Acceptが受け取れなくても、そのまま次のステップへ移行してくれる。
 
bsSocket.Blocking = False
‌ 
Try
    acSocket = bsSocket.Accept
Catch ex As Exception
End Try

ただし、Try Catchを入れているのは、うまくいけば、そのまま接続できるのだが、
うまくいかなかった場合は必ず次のエラーが出てしまうためである。

TCPのAccept


考え方まで



■追記(解決編)
コメントいただきましたように、Select文で書くのがそもそも正しいやり方です。
(C++では普通に書いていることがあるのに、なんでVBでは書いたことが無かったのだろうかと恥ずかしながら思う・・・。.Netのぬるま湯につかりすぎた?)

ただ、MSDNのリファレンスには欠点があると感じたので、それを補足して上のソースを改良したものを書いてみました。
 
Dim bsSocket As Sockets.Socket = New Sockets.Socket(Sockets.AddressFamily.InterNetwork, Sockets.SocketType.Stream, Sockets.ProtocolType.Tcp)
Dim acSocket As Sockets.Socket = Nothing
Dim rdList As List(Of Socket) = New List(Of Socket)
‌ 
Dim bsEndPos As IPEndPoint = New IPEndPoint(IPAddress.Parse("192.168.0.1"), 12345I)
‌ 
Call bsSocket.Bind(bsEndPos)
Call bsSocket.Listen(1I)
‌ 
Call rdList.Add(bsSocket) 'リストに追加
Call Socket.Select(rdList, Nothing, Nothing, 3000I) 'セレクトで読み取りの監視
‌ 
If rdList.Count > 0I Then '読み取り可能である
'もし読み取りできないソケットが存在するとrdListから削除されてしまう。
'今回の例では1つのソケットをAddしているので、読み取りができなかった場合0となって、以下でエラーが発生してしまうので、リストのカウントを確認する。
‌ 
'bsSocket.Acceptでも良いと思うが、実際に接続のチェックを受けたのはrdList内のソケットなので、rdListからAcceptを受け取る
'複数あればFor Eachでもよいかと
  acSocket = rdList.First.Accept '接続を待つ
Else
 'クライアントからの読み取りに失敗した場合の処理
  acSocket = Nothing
End If
‌ 
Call rdList.Clear()
rdList = Nothing

これで、相手からの接続が正しく受け取れたかAcceptの前に確認できる。


上の例では受信でしたが、送信の場合は、2番目の引数checkWriteに入れればよい
ただし、checkReadとは別のリストにしておかないと消されてしまう(Count=0)可能性がある。


詳しい仕様はMSのページで

Socket.Blocking プロパティ
Socket.Select メソッド


関連投稿
IISのFTPが接続できない
Windows7 の IISマネージャが出てこない
IISのFTPでリネームできない
 
コメント
.netはよく知りませんが、Socket classにSelectがあるので、それを使われては?Selectってのは、AcceptやReadを制限時間を設けた上でBlockingしながら監視する関数です。本格的なServerを作成する際は、selectという関数を使って、一定時間毎にProcessを終了すべきか、Clientの接続が到着したか調べる事があります。
  • anony
  • 2013/01/26 5:15 PM
コメントありがとうございます。
出先なので詳しくかけませんが、.netでもselect文は用意されています。
(実際に書いたこともあります)

ちょっと誤解を生む記事かもしれませんので、
帰ってから、それについても補足させていただきます。
  • rockecco
  • 2013/01/26 6:50 PM
anonyさま
書いたことがあったのはC++の方で、VBでは一度も書いたことがないことが判明しました(お恥ずかしい)。

VBのSelect文のリファレンスを読んで、追記したいと思います。
ご指摘、ありがとうございました。

リファレンス
http://msdn.microsoft.com/ja-jp/library/system.net.sockets.socket.select%28v=vs.90%29.aspx
  • rockecco
  • 2013/01/31 11:34 AM
コメントする








    
この記事のトラックバックURL
トラックバック

calendar

S M T W T F S
     12
3456789
10111213141516
17181920212223
24252627282930
31      
<< December 2017 >>

search this site.

よく使う、検索される投稿

categories

アマゾン

楽天

selected entries

archives

recent comment

  • WHEA-Logger イベント17 エラーが全く解消されない
    rockecco (12/06)
  • WHEA-Logger イベント17 エラーが全く解消されない
    通りすがり (12/05)
  • Windows7 の IISマネージャが出てこない
    kimu (12/01)
  • あの、クラスとかメソッドとかプルダウンできるバーって〜Visual Studio 2015
    rockecco (11/30)
  • あの、クラスとかメソッドとかプルダウンできるバーって〜Visual Studio 2015
    にま (11/30)
  • ダイソンHot+Coolを買ってみた
    rockecco (11/13)
  • ダイソンHot+Coolを買ってみた
    naoyuki (11/11)
  • 豊セ祭2017情報ゲット
    Panda NEKO No.1 (10/28)
  • ミラクルミラクルなりきりコスチュームの在庫を確認してみる
    rockecco (10/16)
  • ミラクルミラクルなりきりコスチュームの在庫を確認してみる
    チョコ (10/15)

recent trackback

profile


※当ブログはリンクフリーですが、 取材や雑誌等で掲載される場合は、事前にお知らせください

others

mobile

qrcode

powered

無料ブログ作成サービス JUGEM