こんにちは。System.Net.Socketsについて質問です。
UDPヘッダを含んだパケットを送るプログラムを作っていて、以下のコードでWindows XPでは正常に動作するのですが、Vista上で使用すると
SetSocketOptionのところでエラー(WSAEINVAL,10022,Invalid argument)を吐いてしまいます。
---ここから---
txSocket = New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
txSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, 1)
---ここまで---
開発に利用しているのは、VS.NET2008,Express ed.です。
並行して質問です。
上記のコードは、『他アプリケーションにBindされているLocal Endpointから、UDPパケットを飛ばす』ことを目的にかかれたものです。
同様の効果を得る命令として、以下がより効果的であることに気づきました。
Dim u As New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.udp)
Dim localIPEndPoint = New IPEndPoint(IPAddress.Any, 6112)
u.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1)
u.Bind(localIPEndPoint)
u.SendTo(data, 0, data.Length, SocketFlags.None, RemoteEP)
SocketOptionName.ReuseAddressを使って、既にBindされているポートからパケットを飛ばそうという考えです。
しかし同上、Windows XPでは機能するものの、Windows Vistaでは機能しません。[ アクセス許可で禁じられた方法でソケットにアクセスしようとしました。 ]と表示されます。
どうにかしてこれらを回避し、他Socketに既にBindされているポートをLocal Endpointに持つUDPパケットを作る方法はないのでしょうか?
自己解決しましたので、方法を記しておきます。もし興味のある方がいましたら、参考にしていただけると幸いです。なにぶん素人ですので、誤りもあるかもしれませんがご勘弁を。
『他アプリケーションにBindされているLocal Endpointから、UDPパケットを飛ばす』ことが目的なのですが、SocketOptionName.ReuseAddress や SocketOptionName.HeaderIncluded は、Windows Vistaでは正常に動作しないようです。
そこで、SocketType.rawを使うことで問題を解決しました。
まずは、基礎知識を。
<UDPパケットの構造>
UDPパケットは、大まかに
送受信側MACアドレス + IPヘッダ + UDPヘッダ + データ部 という構造をしています。(各ヘッダの構造については、Googleで調べれば簡単に出てきます)
UDPパケットのポートのデータは、UDPヘッダ内に格納されています。
<ソース>
---------------------------------
'ソケットを定義します。
Dim soc As New Sockets.Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Udp)
'送信先へ接続
soc.Connect(New IPEndPoint(New IPAddress(RemoteHost), RemotePort))
'データを送信します
soc.Send(data, data.Length, SocketFlags.None)
'ソケットの開放
soc.Shutdown(SocketShutdown.Both)
soc.Close()
---------------------------------
<解説>
ソースは、以上のとても簡素なものです。ですが、定義してあるソケットが通常の SocketType.Dgram, ProtocolType.Udp ではなく、 SocketType.raw, ProtocolType.Udp ですから、Socketが作成してくれるパケットのヘッダは、
[送受信側MACアドレス + IPヘッダ] のみです。ですから、送信するdata(Byteの一次配列) に、あらかじめ自分で作ったUDPヘッダを含める必要があります。その部分のソースは割愛しますが、調べれば容易に書けることでしょう。Checksumの計算がやや煩雑ではありますが。
以上です。板汚し失礼しました。