C# Programming

ImageWin Ping

開発環境: Visual Studio 2003 

1.目次

2.目的

Socket の中でも、ICMP_ECHO を使うPing はちょっと特殊ですね。
前から、どうやっているのか気になってましたが、良いサンプルがあったので
それをベースに次のような WinPing を作ってみました。

Image

3.参考

(1) http://www.eggheadcafe.com/articles/20020209.asp
Special Thanks to Peter A. Bromberg!

4.解説


以下の Ping.cs は、もともとは Peter A. Bromberg, Ph.D. のコードです。
詳細は、 http://www.eggheadcafe.com/articles/20020209.asp を見てください。
もともとは、MSDNの Lance Olson の記事をベースに Peter が改良したようです。
Ping 自体の解説はいろいろなところで解説かれているのでここでは説明しません。

ただ、Peter の Ping.cs ではちょっとした問題があったので、次のように修正しています。

Ping.cs の変更点
1public string PingHost(string host, out float deltaTime)
で、out float deltaTime を追加して、数値で Ping のラウンドトリップタイムを取得できるようにしました。
2もともとのコードは、ReceiveFrom を使っているので、Ping のレスポンスが帰ってこない場合にフリーズしてしまいました。
そこで、BeginReceiveFrom, WaitOne, EndReceiveFrom による非同期処理で、1秒でタイムアウトするようにしました。

注意:
WaitOne() で、タイムアウトした後、EndReceiveFrom() を呼ぶと、同様にそこでフリーズしてしまいます。
WaitOne() でタイムアウトした場合は、EndReceiveFrom() を呼ばないようにしていますが、その場合、リソースが開放されるのかどうか不明です。
MSのヘルプがプアーでそこまで書いてない! なんとかしてよ!

Form1.cs の Windows.Forms のメインでは、ボタンが押されたら、ホスト名に対して Ping をかけて、結果をListBoxに書いているだけです。

手でコードを書く必要があるのは、ボタンが押されたときのイベントハンドラ内の、4行だけです。
deltaTime は、使っていません。このあと、もう1つ別のものを作るつもりなので、それようです。

private void button1_Click(object sender, System.EventArgs e) {
    float deltaTime;
    Uchukamen.Util.Ping ping = new Uchukamen.Util.Ping();
    this.listBox1.Items.Add(ping.PingHost(this.textBox1.Text, out deltaTime));
    this.listBox1.SelectedIndex = this.listBox1.Items.Count-1;

}

注意:
ListBoxに次々と追加していくと、最新のデータがスクロールアウトされてしまいます。
スクロール位置を調整できないなんてどうなってるんじゃ!
しょうがないので、ListBox で、SelectedIndex = Items.Count -1 としてスクロール時に追加した行が表示されるようにしています。

5.WinPing のソースコード


Version 情報

2002/10/07 version
Ping.csPing を行うクラス
Form1.csWindows.Forms のメインクラス

Ping.cs

// Following code comes from Peter A. Bromberg, Ph.D.
// Please look at following URL in more detail.
// http://www.eggheadcafe.com/articles/20020209.asp
//
// Version 2.0 by Uchukamen.
// Changed PingHost to handle ref float deltaTime to get ping delta time.
// Changed Non blocking mode.
// All thanks should go to Peter and Lance Olson.

using System;

namespace Uchukamen.Util // change this to yourcompany.yourdivision.whatever
{
        using System;
        using System.Net;
        using System.Net.Sockets;
        /// <summary>
        /// The Main Ping Class
        /// </summary>
        public class Ping
        {
                //Declare some Constant Variables
                const int SOCKET_ERROR = -1; 
                const int ICMP_ECHO = 8;

                /// <summary>
                /// This public method takes the "hostname" of the server
                /// and then it pings it and shows the response time
                /// Data is returned as a string. IP addresses are resolved.
                /// Example usage:
                /// using .CBS.Util
                /// Ping p = new Ping();
                /// textBox2.Text = p.PingHost(textBox1.Text);
                /// </summary>
                public string PingHost(string host, out float deltaTime)
                {
                        deltaTime = -1.0F;

                        //Declare the IPHostEntry 
                        IPHostEntry serverHE, fromHE;
                        int nBytes = 0;
                        int dwStart = 0, dwStop = 0;
                        //Initialize a Socket of Type ICMP

                        Socket socket = 
                                new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
                        socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 1000);
                        // Get the server endpoint
                        try
                        {
                                serverHE = Dns.GetHostByName(host); 
                        }
                        catch(Exception)
                        {

                                return "Host not found"; // uh-oh!
                        } 
                        // Convert the server IP_EndPoint to an EndPoint
                        IPEndPoint ipepServer = new IPEndPoint(serverHE.AddressList[0], 0);
                        EndPoint epServer = (ipepServer); 

                        // Set the receiving endpoint to the client machine
                        fromHE = Dns.GetHostByName(Dns.GetHostName());
                        IPEndPoint ipEndPointFrom = new IPEndPoint(fromHE.AddressList[0], 0); 
                        EndPoint EndPointFrom = (ipEndPointFrom);

                        int PacketSize = 0;
                        IcmpPacket packet = new IcmpPacket(); // didn't know we had this in .Net, eh? 
                        // (script kiddies: GET LOST!)
                        // Construct the packet to send
                        packet.Type = ICMP_ECHO; //8
                        packet.SubCode = 0;
                        packet.CheckSum = UInt16.Parse("0");
                        packet.Identifier = UInt16.Parse("45"); 
                        packet.SequenceNumber = UInt16.Parse("0"); 
                        int PingData = 32; // sizeof(IcmpPacket) - 8;
                        packet.Data = new Byte[PingData];
                        //Initialize the Packet.Data
                        for (int i = 0; i < PingData; i++)
                        {
                                packet.Data[i] = (byte)'#';
                        }

                        // Variable to hold the total Packet size
                        PacketSize = PingData + 8;
                        Byte [] icmp_pkt_buffer = new Byte[ PacketSize ]; 
                        Int32 Index = 0;
                        // Call Method Serialize which counts
                        // The total number of Bytes in the Packet
                        Index = Serialize( 
                                packet, 
                                icmp_pkt_buffer, 
                                PacketSize, 
                                PingData );
                        //Error in Packet Size
                        if( Index == -1 )
                        {
                                return "Error Creating Packet";

                        }

                        // convert into a UInt16 array
                        //Get the Half size of the Packet
                        Double double_length = Convert.ToDouble(Index);
                        Double dtemp = Math.Ceiling( double_length / 2);
                        int cksum_buffer_length = Convert.ToInt32(dtemp);
                        //Create a Byte Array
                        UInt16 [] cksum_buffer = new UInt16[cksum_buffer_length];
                        //Code to initialize the Uint16 array 
                        int icmp_header_buffer_index = 0;
                        for( int i = 0; i < cksum_buffer_length; i++ ) 
                        {
                                cksum_buffer[i] = 
                                        BitConverter.ToUInt16(icmp_pkt_buffer,icmp_header_buffer_index);
                                icmp_header_buffer_index += 2;
                        }
                        //Call method to return a checksum 
                        UInt16 u_cksum = checksum(cksum_buffer, cksum_buffer_length);
                        //Save checksum to Packet
                        packet.CheckSum = u_cksum; 

                        // Now that we have the checksum, serialize the packet again
                        Byte [] sendbuf = new Byte[ PacketSize ]; 
                        //again check the packet size
                        Index = Serialize( 
                                packet, 
                                sendbuf, 
                                PacketSize, 
                                PingData );
                        //if there is an error return it
                        if( Index == -1 )
                        {
                                return "Error Creating Packet";

                        }

                        dwStart = System.Environment.TickCount; // Start timing
                        //send the Packet over the socket
                        if ((nBytes = socket.SendTo(sendbuf, PacketSize, 0, epServer)) == SOCKET_ERROR) 
                        { 
                                return "Socket Error: cannot send Packet";
                        }
                        // Initialize the buffers. The receive buffer is the size of the
                        // ICMP header plus the IP header (20 bytes)
                        Byte [] ReceiveBuffer = new Byte[256]; 
                        nBytes = 0;

                        // loop for checking the time of the server response
                        IAsyncResult ar = socket.BeginReceiveFrom(ReceiveBuffer, 0, 
                        	256, SocketFlags.None, ref EndPointFrom, null, null);
                        // Wait for 1000mSec 
                        bool result = ar.AsyncWaitHandle.WaitOne(1000, false);

                        deltaTime = dwStop = System.Environment.TickCount - dwStart; // stop timing

                        string reply = null;
                        if (result)
                        {
                                // Successfully Received
                                nBytes = socket.EndReceiveFrom(ar, ref EndPointFrom);
                                reply = "Reply from "+ epServer.ToString()+" in " + 
                                dwStop + "ms. Received: "+ nBytes + " Bytes.";
                        } 
                        else
                        {
                                reply = "Timed out in " + dwStop + "ms.";
                        }

                        // Console.WriteLine(reply); // for debug
                        
                        // close the socket
                        socket.Close(); 
                        return reply;
                }

                /// <summary>
                /// This method get the Packet and calculates the total size 
                /// of the Pack by converting it to byte array
                /// </summary>
                public static Int32 Serialize(IcmpPacket packet, Byte[] Buffer,
                        Int32 PacketSize, Int32 PingData )
                {
                        Int32 cbReturn = 0;
                        // serialize the struct into the array
                        int Index=0;

                        Byte [] b_type = new Byte[1];
                        b_type[0] = (packet.Type);

                        Byte [] b_code = new Byte[1];
                        b_code[0] = (packet.SubCode);

                        Byte [] b_cksum = BitConverter.GetBytes(packet.CheckSum);
                        Byte [] b_id = BitConverter.GetBytes(packet.Identifier);
                        Byte [] b_seq = BitConverter.GetBytes(packet.SequenceNumber);


                        Array.Copy( b_type, 0, Buffer, Index, b_type.Length );
                        Index += b_type.Length;


                        Array.Copy( b_code, 0, Buffer, Index, b_code.Length );
                        Index += b_code.Length;


                        Array.Copy( b_cksum, 0, Buffer, Index, b_cksum.Length );
                        Index += b_cksum.Length;


                        Array.Copy( b_id, 0, Buffer, Index, b_id.Length );
                        Index += b_id.Length;

                        Array.Copy( b_seq, 0, Buffer, Index, b_seq.Length );
                        Index += b_seq.Length;

                        // copy the data 
                        Array.Copy( packet.Data, 0, Buffer, Index, PingData );
                        Index += PingData;
                        if( Index != PacketSize/* sizeof(IcmpPacket) */) 
                        {
                                cbReturn = -1;
                                return cbReturn;
                        }

                        cbReturn = Index;
                        return cbReturn;
                }
                /// <summary>
                /// This Method has the algorithm to make a checksum 
                /// </summary>
                public static UInt16 checksum( UInt16[] buffer, int size )
                {
                        Int32 cksum = 0;
                        int counter;
                        counter = 0;

                        while ( size > 0 ) 
                        {
                                UInt16 val = buffer[counter];

                                cksum += Convert.ToInt32( buffer[counter] );
                                counter += 1;
                                size -= 1;
                        }

                        cksum = (cksum >> 16) + (cksum & 0xffff);
                        cksum += (cksum >> 16);
                        return (UInt16)(~cksum);
                }
        } // class ping
        /// <summary>
        /// Class that holds the Packet information
        /// </summary>
        public class IcmpPacket 
        { 
                public Byte Type; // type of message
                public Byte SubCode; // type of sub code
                public UInt16 CheckSum; // ones complement checksum of struct
                public UInt16 Identifier; // identifier
                public UInt16 SequenceNumber; // sequence number 
                public Byte [] Data;

        } // class IcmpPacket
}


Form1.cs

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;


namespace Uchukamen.Windows.Appliation
{
        /// <summary>
        /// Form1 の概要の説明です。
        /// </summary>
        public class Form1 : System.Windows.Forms.Form
        {
                private System.Windows.Forms.Button button1;
                private System.Windows.Forms.TextBox textBox1;
                private System.Windows.Forms.Label label1;
                private System.Windows.Forms.ListBox listBox1;
                /// <summary>
                /// 必要なデザイナ変数です。
                /// </summary>
                private System.ComponentModel.Container components = null;

                public Form1()
                {
                        //
                        // Windows フォーム デザイナ サポートに必要です。
                        //
                        InitializeComponent();

                        //
                        // TODO: InitializeComponent 呼び出しの後に、コンストラクタ コードを追加してください。
                        //
                }

                /// <summary>
                /// 使用されているリソースに後処理を実行します。
                /// </summary>
                protected override void Dispose( bool disposing )
                {
                        if( disposing )
                        {
                                if (components != null) 
                                {
                                        components.Dispose();
                                }
                        }
                        base.Dispose( disposing );
                }

                #region Windows Form Designer generated code
                /// <summary>
                /// デザイナ サポートに必要なメソッドです。このメソッドの内容を
                /// コード エディタで変更しないでください。
                /// </summary>
                private void InitializeComponent()
                {
                        this.button1 = new System.Windows.Forms.Button();
                        this.textBox1 = new System.Windows.Forms.TextBox();
                        this.label1 = new System.Windows.Forms.Label();
                        this.listBox1 = new System.Windows.Forms.ListBox();
                        this.SuspendLayout();
                        // 
                        // button1
                        // 
                        this.button1.Anchor = (System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right);
                        this.button1.DialogResult = System.Windows.Forms.DialogResult.OK;
                        this.button1.Location = new System.Drawing.Point(280, 8);
                        this.button1.Name = "button1";
                        this.button1.Size = new System.Drawing.Size(40, 23);
                        this.button1.TabIndex = 2;
                        this.button1.Text = "Ping";
                        this.button1.Click += new System.EventHandler(this.button1_Click);
                        // 
                        // textBox1
                        // 
                        this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
                                | System.Windows.Forms.AnchorStyles.Right);
                        this.textBox1.Location = new System.Drawing.Point(80, 8);
                        this.textBox1.Name = "textBox1";
                        this.textBox1.Size = new System.Drawing.Size(160, 19);
                        this.textBox1.TabIndex = 1;
                        this.textBox1.Text = "";
                        // 
                        // label1
                        // 
                        this.label1.Location = new System.Drawing.Point(8, 8);
                        this.label1.Name = "label1";
                        this.label1.Size = new System.Drawing.Size(64, 16);
                        this.label1.TabIndex = 0;
                        this.label1.Text = "Host Name";
                        // 
                        // listBox1
                        // 
                        this.listBox1.ItemHeight = 12;
                        this.listBox1.Location = new System.Drawing.Point(0, 40);
                        this.listBox1.Name = "listBox1";
                        this.listBox1.Size = new System.Drawing.Size(328, 160);
                        this.listBox1.TabIndex = 3;
                        // 
                        // Form1
                        // 
                        this.AutoScaleBaseSize = new System.Drawing.Size(5, 12);
                        this.ClientSize = new System.Drawing.Size(328, 198);
                        this.Controls.AddRange(new System.Windows.Forms.Control[] {
                        this.listBox1,
                        this.label1,
                        this.textBox1,
                        this.button1});
                        this.Name = "Form1";
                        this.Text = "Ping";
                        this.ResumeLayout(false);

                }
                #endregion

                /// <summary>
                /// アプリケーションのメイン エントリ ポイントです。
                /// </summary>
                [STAThread]
                static void Main() 
                {
                        Application.Run(new Form1());
                }

                private void button1_Click(object sender, System.EventArgs e)
                {
                        float deltaTime;
                        Uchukamen.Util.Ping ping = new Uchukamen.Util.Ping();
                        this.listBox1.Items.Add(ping.PingHost(this.textBox1.Text, out deltaTime));
                        this.listBox1.SelectedIndex = this.listBox1.Items.Count-1;
                }
        }
}

つづきを検討中。
 PingRadar (工事中) はこちら。