C# で UDP ってみた
必要だったので。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Windows.Forms;
System.Net 名前空間と System.Net.Sockets 名前空間と、
System.Threading 名前空間を追加してある。
ということでコンストラクタは華麗にスルーして次。
/// <summary> /// ウィジェットの Text プロパティを設定するためのデリゲートです。 /// </summary> /// <param name="s"></param> delegate void text_property_setter(string s); /// <summary> /// ウィジェットをメソッド内であれこれするためのデリゲートです。 /// </summary> delegate void action();
Control#Invoke メソッドで使うためのデリゲート。
どっちのデリゲートも System 名前空間の Action デリゲートとか使えばいいんだろうけど
めんどいので新しく作った。
ということで、次のメソッドがそれぞれデリゲートの型をどーてらこーてらしてる。
/// <summary> /// tbResult.Text に result を付け足します。 /// </summary> /// <remarks> /// 呼び出し側が別スレッドで動いている場合、ウィジェット(コントロール)にアクセスすることはできないので、 /// Invoke メソッドを使う。 /// そのためのメソッド。 /// </remarks> /// <param name="result"></param> private void result_append_to_textbox(string result) { StringBuilder text_builder = new StringBuilder( tbResult.Text ); text_builder.AppendLine( result ); tbResult.Text = text_builder.ToString(); } /// <summary> /// 「送信」ボタンを有効にします。 /// </summary> private void enable_send_button() { if ( !btnSend.Enabled ) btnSend.Enabled = true; }
ということで、次は受信後のコールバック。
/// <summary> /// 非同期受信に成功した場合に呼び出されます。 /// </summary> /// <param name="ar"></param> private void async_receive_callback(IAsyncResult ar) { UDPRecord record = (UDPRecord)ar.AsyncState; if ( record.client.Client != null && record.client.Client.Connected ) { byte[] received_bytes = record.client.EndReceive( ar, ref record.endpoint ); record.client.Close(); tbResult.Invoke( new text_property_setter( result_append_to_textbox ), new object[] { Encoding.UTF8.GetString( received_bytes ) } ); } btnSend.Invoke( new action( enable_send_button ) ); }
この関数は、受信に成功した場合に呼びだされるよ。
で、送信側が送信しなかった場合は呼びだされない。
ようやくデータの送信に。
/// <summary> /// 「送信」ボタンが押された場合に呼び出されます。 /// </summary> /// <param name="sender"></param> /// <param name="ergs"></param> private void btnSend_Click(object sender, EventArgs ergs) { int src_port_numer = Convert.ToInt32( tbSrcPortNumber.Text ); int dst_port_number = Convert.ToInt32( tbDstPortNumber.Text ); this.udp_sender_ = new UdpClient( src_port_numer ); this.udp_sender_.DontFragment = true; this.udp_sender_.EnableBroadcast = true; try { // 送信先に接続します。 this.udp_sender_.Connect( tbDstIpAddress.Text, dst_port_number ); string send_text = string.Format( "<request><get version=\"{0}\">/</get><host>{1}</host></request>", 1.1, tbDstIpAddress.Text ); // 送信データを UTF-8 でエンコードしてバイト配列にします。 byte[] send_bytes = Encoding.UTF8.GetBytes( send_text ); int sended_bytes_amount = this.udp_sender_.Send( send_bytes, send_bytes.Length ); Thread.Sleep( 1 ); if ( sended_bytes_amount > 0 ) { IPEndPoint remote_endpoint = new IPEndPoint( IPAddress.Any, 0 ); //tbResult.Text = Encoding.UTF8.GetString( udp_sender.Receive( ref remote_endpoint ) ); IAsyncResult ar = this.udp_sender_.BeginReceive( async_receive_callback, new UDPRecord( this.udp_sender_, remote_endpoint ) ); btnSend.Enabled = false; udpSenderTimer.Start(); } else { this.udp_sender_.Close(); } } catch ( SystemException se ) { MessageBox.Show( se.ToString(), se.Source, MessageBoxButtons.OK, MessageBoxIcon.Warning ); this.udp_sender_.Close(); } }
接続して、文字列からバイト列にエンコードしてー、送信の後、1 ミリ秒寝た後受信してる。
どんどん書くことが無くなってくけど気にしない方向で。
次はえーっと、送信したあとに受信出来るまで送信ボタンが無効になるんだけど、ずーっと受信出来ないとアプリを再起動させないといけないので、
5 秒くらいで送信ボタンを有効にして接続を閉じる処理。
/// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="ergs"></param> private void udpSenderTimer_Tick(object sender, EventArgs ergs) { if ( this.tick_ > 50 ) { udpSenderTimer.Stop(); this.udp_sender_.Close(); MessageBox.Show( "応答がありませんでした。接続を閉じます。", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information ); return; } ++this.tick_; }
ということで、全文はここに載ってるよ〜