none
Winsockで送信バッファの空き状況を確認する方法 RRS feed

  • 質問

  • お世話になります tdeg といいます。

     

    アプリケーションで帯域制御をしたいのですが、
    Winsockのsend()にて送信する前に送信バッファの空き状況を確認する
    方法はありませんでしょうか?

    send()の戻り値で、送信バイト数以下(またはWSAEWOULDBLOCK)になる前に
    知りたいのですが。。

     

    以上、よろしくお願いいたします。

     

    2008年10月22日 2:48

回答

  • レスつきませんね。もっと詳しい人がいると思いますが自分はこう考えます。

     

     簡単に言ってしまえば「そんな事を知っても意味がない」ということです。

     

     RS-232C等と違ってソケットは排他に使用することはできません。複数のアプリの共有資源であり、

    残りバッファの問い合わせの直後に他のアプリが送信リクエストしてしまう可能性があるのです。

    この後、戻された残りバッファ数通りの送信をリクエストしても、ソケットはエラーを戻すかもしれません。

     これでは、自分の「リクエスト」の結果を見てから身の振り方を工夫しなければならないことに、

    何の変わりもありませんよねぇ。

     つまり、ソケットの送信は送信エラーになるまで送るしかないのであって、それが

    ほとんど唯一の正しい使い方であると言えるのです。

     

     

    2008年10月22日 8:52
  • 試してないですがgetsockoptで取れるかもしれません。

    あとはWSAIoctlも可能性があります。
    2008年10月22日 12:12
  • もう、質問者の方はお読みになってないかもしれませんが。

     

    1.帯域制御をしたいのか

    2.なんらかの事情でsend関数がブロックすることを回避したいのか

     

    1,2を明確にするともっと有効な回答が得られるのではないのでしょうか?

     

    1であれば、タスクマネージャにあるネットワーク使用率を参照することで、解決するかもしれません。

     

    2であれば、新たに送信専用のスレッドを起動することにより解決するかもしれません。

     

     

    2008年10月24日 5:07

すべての返信

  • レスつきませんね。もっと詳しい人がいると思いますが自分はこう考えます。

     

     簡単に言ってしまえば「そんな事を知っても意味がない」ということです。

     

     RS-232C等と違ってソケットは排他に使用することはできません。複数のアプリの共有資源であり、

    残りバッファの問い合わせの直後に他のアプリが送信リクエストしてしまう可能性があるのです。

    この後、戻された残りバッファ数通りの送信をリクエストしても、ソケットはエラーを戻すかもしれません。

     これでは、自分の「リクエスト」の結果を見てから身の振り方を工夫しなければならないことに、

    何の変わりもありませんよねぇ。

     つまり、ソケットの送信は送信エラーになるまで送るしかないのであって、それが

    ほとんど唯一の正しい使い方であると言えるのです。

     

     

    2008年10月22日 8:52
  • ご指摘ありがとうございます。

     

    一応、アプリは自分しかいない環境を想定しています。

    なのでネットワークも占有できると考えています。

     

    確かに、「意味がない」のかも知れませんが、

    "データが詰まってから何とかするのではなく、詰まる前に何とかできないか?"

    と考えたしだいです。(実際、うまくいくかわかりませんが。。。。)

     

    やはり、こうゆうAPIは用意されていないのでしょうか?

     

    2008年10月22日 11:11
  • 試してないですがgetsockoptで取れるかもしれません。

    あとはWSAIoctlも可能性があります。
    2008年10月22日 12:12
  • 確認してみます。

    ありがとうございました。

    2008年10月23日 0:05
  • getsockopt(),WSAIoctlを確認しました。以下のパラメータがあるようです。

    SO_SNDBUFは"送信バッファの最大値"を返してくれますが、空きのバイト

    数はわかりません。ちなみに自分の環境(WinXpSP2)では8192が返りました。

     

    やはり"送信エラー"で判断するしかないようです。

     

            ・getsockopt()のパラメータ
                SO_ACCEPTCONN
                SO_BROADCAST
                SO_BSP_STATE
                SO_CONDITIONAL_ACCEPT
                SO_CONNECT_TIME
                SO_DEBUG
                SO_DONTLINGER
                SO_DONTROUTE
                SO_ERROR
                SO_EXCLUSIVEADDRUSE
                SO_GROUP_ID
                SO_GROUP_PRIORITY
                SO_KEEPALIVE
                SO_LINGER
                SO_MAX_MSG_SIZE
                SO_OOBINLINE
                SO_PORT_SCALABILITY
                SO_PROTOCOL_INFO
                SO_RCVBUF
                SO_REUSEADDR
                SO_SNDBUF
                SO_TYPE
                PVD_CONFIG

            ・WSAIoctlのパラメータ
                SIO_ADDRESS_LIST_CHANGE
                SIO_ADDRESS_LIST_QUERY
                SIO_ASSOCIATE_HANDLE
                SIO_BASE_HANDLE
                SIO_BSP_HANDLE
                SIO_BSP_HANDLE_SELECT
                SIO_BSP_HANDLE_POLL
                SIO_CHK_QOS
                SIO_ENABLE_CIRCULAR_QUEUEING
                SIO_FIND_ROUTE
                SIO_FLUSH
                SIO_GET_BROADCAST_ADDRESS
                SIO_GET_EXTENSION_FUNCTION_POINTER
                SIO_GET_GROUP_QOS
                SIO_GET_INTERFACE_LIST
                SIO_GET_INTERFACE_LIST_EX
                SIO_GET_QOS
                SIO_IDEAL_SEND_BACKLOG_CHANGE
                SIO_IDEAL_SEND_BACKLOG_QUERY
                SIO_KEEPALIVE_VALS
                SIO_MULTIPOINT_LOOPBACK
                SIO_MULTICAST_SCOPE
                SIO_QUERY_RSS_SCALABILITY_INFO
                SIO_QUERY_WFP_ALE_ENDPOINT_HANDLE
                SIO_RCVALL
                SIO_RCVALL_IGMPMCAST
                SIO_RCVALL_MCAST
                SIO_ROUTING_INTERFACE_CHANGE
                SIO_ROUTING_INTERFACE_QUERY
                SIO_SET_COMPATIBILITY_MODE
                SIO_SET_GROUP_QOS
                SIO_SET_QOS
                SIO_TRANSLATE_HANDLE
                SIO_UDP_CONNRESET

     

    2008年10月23日 1:34
  • >確かに、「意味がない」のかも知れませんが、

    >"データが詰まってから何とかするのではなく、詰まる前に何とかできないか?"

    >と考えたしだいです。(実際、うまくいくかわかりませんが。。。。)

    >

    >やはり、こうゆうAPIは用意されていないのでしょうか?

     

    BSDやSocketをデザインした人たちは

    「意味の無いものは用意しない」という方針だったようです。

    2008年10月23日 4:03
  • もう、質問者の方はお読みになってないかもしれませんが。

     

    1.帯域制御をしたいのか

    2.なんらかの事情でsend関数がブロックすることを回避したいのか

     

    1,2を明確にするともっと有効な回答が得られるのではないのでしょうか?

     

    1であれば、タスクマネージャにあるネットワーク使用率を参照することで、解決するかもしれません。

     

    2であれば、新たに送信専用のスレッドを起動することにより解決するかもしれません。

     

     

    2008年10月24日 5:07
  • ご意見ありがとうございます。

     

    どちらかといえば帯域制御でしょうか。
    通信回線(ソケット)の実質帯域にあわせてデータ量を
    変更したいと考えています。

     

    ネットワーク使用率は、ネットワークの論理的な速度を
    基本にして使用率を計算しているようなので、実質的な
    帯域を示していないように思えます。

     

    送信バッファの空き具合をモニタすれば実質的な帯域が
    判断できると考えました。

     

    送信ウインドサイズも取得できれば、もっと詳しい
    (相手端末の処理速度も含んだ)帯域が判断できるとも
    考えていますがいかがでしょうか?

     

     

    2008年10月24日 9:35
  • >どちらかといえば帯域制御でしょうか。
    >通信回線(ソケット)の実質帯域にあわせてデータ量を
    >変更したいと考えています。

     

    「相手のレスポンスに依存するスピードで送信」

    したいということになるのでしょうか。

     

    もうご存知かもしれませんが、以下にWinSockの「比較的」詳しい説明があります。

    初心者の章は飛ばして、4章あたりが参考になるかもしれません。

    DDKを持っていれば、7章の「WsControl() 内部解析 」は利用価値があるかもしれません。

     

    Winsock Programmer's FAQ http://www.kt.rim.or.jp/~ksk/wskfaq-ja/

    2008年10月24日 10:09
  • ありがとうございます。

    確認してみます。

    2008年10月24日 10:46
  • 「WsControl() 内部解析」は、"フックDLLを使って..."など難しいことばかりで
    ついていけませんでした。

     

    "帯域制御"については、無線環境での使用から考えるようになりました。
    無線LANでは電波状況によってスピードが変化するようです。

    特にFOMAでは「384kbpsから64kbpsまで変化する」ということを聞きました。

     

    一応、「メッセージ長+データのパケットをTCPで送信する」と考えていますので
    1パケットの送信でエラー(送信サイズが要求サイズよりも短い場合)、
    パケットの途中から送信しなければならなくなります。(捨てるわけには行かない)

    送信バッファの空きがわかれば事前にデータ量を減らせると考えたのですが。。。

    2008年10月24日 12:35
  • 詳しい事情を知らないので勘違いしているかも知れませんが、デリミタでメッセージ長が確実に抽出できると仮定して、

    TCPのパケットと関係なくアプリ層で『メッセージ長+データ』のアプリ層パケットを再構築してはいけないのでしょうか?

    (TCP/IPは、アプリ側がTCP/IPのパケット内容をコントロールするような仕組みになっていないと思うのですが...)

     

     

    2008年10月27日 2:44
  • ご意見ありがとうございます。


    送信するデータはバイナリなので、デリミタは考えていませんでした。
    デリミタが使用できるようにエスケープシーケンスのコード変換
    もありますが、CPU負荷の点から難しいと考えています。

    2008年10月27日 4:16
  • TFTPプロトコル http://dictionary.rbbtoday.com/Details/term2355.html で、UDPを使うという手もあるかもしれません。

    最近あまりネットワーク関係をプログラムしていないので、記憶が曖昧ですが、TCP/IPパケットに比べUDPはかなり

    小さかった記憶があります。 (結果的にトラフィックを下げることになると思います。)

    通信信頼性はTFTPの簡単なプロトコルと、物理層のW-CDMA、無線LAN等の誤り訂正能力(あったかな?)に頼ったほうが良いかもしれません。

    2008年10月27日 5:26
  • ご意見、ありがとうございます。


    確かにTCP->UDPにすれば、データ量が減るので詰まりにくくなりそうですが
    減る量は知れていると思います。

     

    "データが詰まってしまったらゆっくり送る"のではなく、
    "詰まらないようにデータを少なくしたい"のです。

     

    UDPにすれば回線が詰まった場合にUDPパケットが捨てられ
    結果的にデータが少なくなりますが、勝手に捨てられるのは
    困るのでTCPを選択しました。

    2008年10月27日 6:44
  • 全体像が把握できていなので、推測で申し訳ないですが、

    輻輳発生の問題回避とすると、だんだん相手は細い回線になっているようなので、間に入るルーター、スイッチ等でQoS制御すれば解決するかもしれません。

    2008年10月27日 7:38
  • Qosは前に少し調べたのですが、"自分の使用する帯域を要求する規格"と
    理解しました。もしルータ等に予約できたとしても無線状況が変化したら
    どうしようもないのではないでしょうか?
    間違っていたらご指摘ください。

    2008年10月27日 8:09
  • winsock(というか、MicrosoftのTCPスタック)では、SO_SNDBUFを0にした状態で

    オーバーラップsendすれば、ACKが戻ってくるまで送信中、という挙動に

    なったはずです(ここはうろ覚え)。

     

    そうすると、「現在オーバーラップ送信中のn個のデータのうち、

    k番目のsendがまだ完了していないので、帯域はナンボ」みたいな

    処理・判断を入れれば良いんじゃないでしょうか。

     

    2008年11月5日 13:21
  • Winsockではありませんがiphlpapiを使えば輻輳ウィンドウの状態を確認する事が可能です。

    サンプルはこちらのURL

    http://msdn.microsoft.com/en-us/library/bb485738(VS.85).aspx

     

    2010年6月9日 7:58