C#でTCPコネクションの一覧を取得する方法

初出:2004/04/24
改定:2004/05/01

C#を使ってTCPコネクション(セッション)の一覧を取得する方法です。
コマンドプロンプトで「netstat -a」したときに表示されるのと同じものです。

<技術資料>
TCP状態の一覧取得
GetTcpTable (MSDN Library)

<参考ソースコード>
Get TCP Information (VB.NET)

<C#ソースコード>
※VB.NETな人は、上記の参考ソースコードを見てください。
/* tcptable.cs */
/* version 0.02 */

using System;
using System.Text;
using System.Collections;
using System.Runtime.InteropServices;

namespace TcpView{
  class TcpTableView{
  
    //GetTcpTable APIのインポート
    [DllImport("iphlpapi.dll", CharSet=CharSet.Auto)]
    public extern static int GetTcpTable(IntPtr pTcpTable,
                                         ref int pdwSize, bool bOrder
                                        );
    
    //GetTcpTable の戻り値
    public const short NO_ERROR = 0;
    public const short ERROR_NOT_SUPPORTED = 50;
    public const short ERROR_INVALID_PARAMETER = 87;
    public const short ERROR_INSUFFICIENT_BUFFER = 112;
    
    /*MIB_TCPTABLE 構造体
    [ StructLayout(LayoutKind.Sequential) ]
    public struct MIB_TCPTABLE{
      public int dwNumEntries;
      public MIB_TCPROW[] table;
    }
    */
    
    //MIB_TCPROW 構造体
    [ StructLayout(LayoutKind.Sequential) ]
    public struct MIB_TCPROW{
      public int dwState;
      public int dwLocalAddr;
      public int dwLocalPort;
      public int dwRemoteAddr;
      public int dwRemotePort;
    }
    
    //MIB_TCPROW.dwState 用定義
    public const short MIB_TCP_STATE_CLOSED = 1;
    public const short MIB_TCP_STATE_LISTEN = 2;
    public const short MIB_TCP_STATE_SYN_SENT = 3;
    public const short MIB_TCP_STATE_SYN_RCVD = 4;
    public const short MIB_TCP_STATE_ESTAB = 5;
    public const short MIB_TCP_STATE_FIN_WAIT1 = 6;
    public const short MIB_TCP_STATE_FIN_WAIT2 = 7;
    public const short MIB_TCP_STATE_CLOSE_WAIT = 8;
    public const short MIB_TCP_STATE_CLOSING = 9;
    public const short MIB_TCP_STATE_LAST_ACK = 10;
    public const short MIB_TCP_STATE_TIME_WAIT = 11;
    public const short MIB_TCP_STATE_DELETE_TCB = 12;
    
    public static string[] StateStrings = {"", "CLOSED","LISTEN","SYN_SENT","SYN_RCVD","ESTABLISHED","FIN_WAIT1","FIN_WAIT2",
                                           "CLOSE_WAIT","CLOSING","LAST_ACK","TIME_WAIT","DELETE_TCB"};
    
    
    //============================================================
    
    
    public static void Main(){
      ArrayList TcpTable = GetTcpTableView();
      
      //結果を表示
      for (int i = 0; i <= TcpTable.Count - 1; i++){
        Console.Write(GetIpAddress(((TcpRow)TcpTable[i]).LocalAddr));
        Console.Write(" ");
        Console.Write(GetPortNumber(((TcpRow)TcpTable[i]).LocalPort));
        Console.Write(" ");
        Console.Write(GetIpAddress(((TcpRow)TcpTable[i]).RemoteAddr));
        Console.Write(" ");
        Console.Write(GetPortNumber(((TcpRow)TcpTable[i]).RemotePort));
        Console.Write(" ");
        Console.Write(StateStrings[((TcpRow)TcpTable[i]).State]);
        Console.WriteLine("");
      }
    }

    public static ArrayList GetTcpTableView(){
      int pdwSize = 0;
      IntPtr pTcpTable = IntPtr.Zero;  //TcpTable用ポインタ
      GetTcpTable(pTcpTable, ref pdwSize, false);  //pdwSizeを取得
      pTcpTable = Marshal.AllocHGlobal(pdwSize);   //メモリを割り当て
      
      try{
        //TCPの一覧を取得
        if (GetTcpTable(pTcpTable, ref pdwSize, true) == NO_ERROR){
          int RowCount = Marshal.ReadInt32(pTcpTable);  //MIB_TCPTABLE.dwNumEntriesを読み取り
          
          ArrayList TcpTable = new ArrayList();  //結果用リスト
          
          for (int i = 0; i<= RowCount - 1; i++){
            //1列ずつ取得
            IntPtr pTcpRow = new IntPtr(pTcpTable.ToInt32() + 4 + (i * Marshal.SizeOf(typeof(MIB_TCPROW))));
            MIB_TCPROW _TcpRow = new MIB_TCPROW();
            _TcpRow = (MIB_TCPROW)Marshal.PtrToStructure(pTcpRow, typeof(MIB_TCPROW));
            
            //取得結果
            TcpRow row = new TcpRow();
            row.LocalAddr = _TcpRow.dwLocalAddr;
            row.LocalPort = _TcpRow.dwLocalPort;
            row.RemoteAddr = _TcpRow.dwRemoteAddr;
            row.RemotePort = _TcpRow.dwRemotePort;
            row.State = _TcpRow.dwState;
            //RemoteAddrが0の場合は、Remoteポートも0にする
            if (row.RemoteAddr == 0) row.RemotePort = 0;
            
            TcpTable.Add(row);
          }  //for
          
          return TcpTable;
          
        }  //if
        else
          return null;
      
      }  //try
      finally{
        Marshal.FreeHGlobal(pTcpTable);  //メモリ開放
      }
      
    } //gettcptableview
    
    
    private static string GetIpAddress(int Address){
      byte[] IpParts = BitConverter.GetBytes(Address);
      
      StringBuilder builder = new StringBuilder();
      builder.Append(IpParts[0].ToString());
      builder.Append(".");
      builder.Append(IpParts[1].ToString());
      builder.Append(".");
      builder.Append(IpParts[2].ToString());
      builder.Append(".");
      builder.Append(IpParts[3].ToString());
      
      return builder.ToString();
    }
  
    private static int GetPortNumber(int Port){
      return Port / 256 + (Port % 256) * 256;
    }
  
  } //class
  
  
  public struct TcpRow{
    public int LocalAddr;
    public int LocalPort;
    public int RemoteAddr;
    public int RemotePort;
    public int State;
  }
  
  
  
} //namespace

<ソースのダウンロード>
tcptable v0.02

<解説>
TCPコネクションの一覧を取得するには、「GetTcpTable」Windows APIを使用します。
これはアンマネージドなので、iphlpapi.dllからインポートして使用します。さらにアンマネージドな構造体「MIB_TCPROW」を定義します。

WindowsAPIではポインタを使うことが多いのですが、GetTcpTableでは第1引数のpTcpTable、第2引数のpdwSizeがポインタです。
本来pTcpTableはMIB_TCPTABLE構造体を定義すべきですが、この構造体は.NET Frameworkでの扱いが難しいため、汎用的なポインタ「IntPtr」として定義しています。
pdwSizeをポインタとして扱うためには、参照渡しにします。refパラメータを付ければOKです。

アンマネージドな構造体を定義するには、構造体の上に「StructLayout」を記述します。

表紙へ戻る


Copyright © 2004 H'Imagine.
All rights reserved.