例としてソケットを使用した.NET相互運用

すでに理論を駆り立てるのに十分なので、あなたは私に練習、練習を与えます!




多くの技術があります。 非常に高速なものもあれば、非常に快適なものもあります。 光の速度で飛行できるものもあれば、光の速度で飛行できるものもあります。



どのアプローチがより良いかという論争はめったに治まらない。 次に、ハリネズミとヘビを横切る方法を示します。 迅速に実行できる.NETがあり、迅速に実行できるネイティブがあります。



教育目的のために、これらの2つの方向を横断します。 この記事には別の目標があります。 私とArwylが書いたDuSterというプログラムに基づいています。 このプログラムは、ネットワークプログラムをテストできるダミーサーバーです。 このサーバーは非常に使いやすく、構成するのに十分な柔軟性があり、プロトコル記述ファイルをサポートしており、プロトコルのテストを多少自動化できます。 私は友人であるネットワーク層の開発に従事していました-ビジネスロジックとプロトコル解析です。 それは信じられないほどなめらかで楽しいものでした。 私たちはこのプログラムを誇りに思っており、非営利的な使用のために世界に提供したいと考えています。



CLR -Common Language Runtimeは、 CLI (Common Language Infrastructure)をサポートする言語で書かれたプログラムを実行できる環境です。 これらすべてのビジネス+コンパイラおよびライブラリは、世界で最も一般的な開発環境の1つである.NET Frameworkを形成します 。 .NETで作成されたプログラムがどのように機能するかについては説明しません。このトピックではさらに2、3の記事に値するからです。 私たちの記事に必要な基本的なことだけを言います。 .NETマシンコードとネイティブマシンコード(非.NET)アプリケーションは同じものではありません。 したがって、興味深いことが出てきます。Assemblerで記述された1つのネイティブアプリケーションを使用し、Pascalで記述された別のネイティブアプリケーションを使用して、それらを相互にクロスできます。 とても簡単です。 私たちは大学でそのような仕事を与えられました。 私は好奇心following盛な性格に従って、自慢することにしました。 アセンブラーとC#をクロスさせることにしました。 すべてがシンプルになると思ったので、それを取り、C#でアセンブラコードを記述します。 私がどれほど間違っていたか。 当然、 MSILとは何かを知ったので、このアイデアは最高ではないことに気付きましたが、あきらめたくありませんでした。 長い間、私はこの状況から抜け出す方法を探していました-そして見つけました:P \ DllImportを介して呼び出します。



したがって、.NETランタイムを使用して動作する.NETプログラムがあります。 タスクは、ランタイムに外部ライブラリをプルさせることです。 さて、タスクを少し複雑にしましょう-Windows Socket 2.0に基づくソケットをプログラムで使用できるようにします。



研究所でネットワークプログラミングを行っていたとき、WS2を使用して書くことを余儀なくされましたが、熱心な研ぎ師のように、System.Net.Socketライブラリに比べてWS2は悲惨なコードのパロディであるため、このライブラリから目をそらしました。

私たちは長い間、先生と妥協点を探しました。その結果、次のことに気付きました。





始めて、コードの解析に直行しましょう。



[DllImport( "ws2_32.dll" , CharSet = CharSet.Auto, SetLastError = true )]

public static extern Int32 accept( Int32 socketHandle, ref SocketAddres socketAddress, ref Int32 addressLength);



[DllImport( "ws2_32.dll" , CharSet = CharSet.Auto, SetLastError = true )]

public static extern Int32 bind( Int32 socketHandle, ref SocketAddres socketAddress, Int32 addressLength);



[DllImport( "ws2_32.dll" , CharSet = CharSet.Auto, SetLastError = true )]

public static extern Int32 listen( Int32 socket, Int32 queue);



[DllImport( "ws2_32.dll" , SetLastError = true )]

public static extern Int32 WSAStartup(Int16 wVersionRequested, ref WSADATA lpWSAData);



[DllImport( "ws2_32.dll" , SetLastError = true )]

public static extern String inet_ntoa( Int32 inadr);



[DllImport( "ws2_32.dll" , SetLastError = true )]

public static extern Int32 inet_addr( String addr);



[DllImport( "ws2_32.dll" , SetLastError = true )]

public static extern Int32 WSACleanup();



[DllImport( "ws2_32.dll" , SetLastError = true )]

public static extern Int32 WSAGetLastError();



[DllImport( "ws2_32.dll" , SetLastError = true , CharSet = CharSet.Ansi)]

public static extern Int32 gethostbyname( String name);



[DllImport( "ws2_32.dll" , SetLastError = true )]

public static extern Int32 socket( Int32 af, Int32 type, Int32 protocol);



[DllImport( "ws2_32.dll" , SetLastError = true )]

public static extern Int32 closesocket( Int32 socket);




* This source code was highlighted with Source Code Highlighter .








これはC#コードであり、単純で明白なことを行います。 標準のWindowsライブラリws2_32.dllに目を向けると、彼は.NETの上記のメソッドへのポインターをインポートします。 つまり、次のようになります。プログラムでネイティブメソッドを使用できるようにします。



さらに詳しくは、DllImportのすべての機能は、 この記事の@ alex-blankによって奉献されています。



主なメソッドはすでにプログラム内にあります-それらを整理するために残っています。



WS2ライブラリでいつも悩まされたのは、エラーを返し、情報を読み取る方法でした。 読み取りバイトの値を返し、エラーが発生した場合に-1を返すメソッドには非常に慎重です。 さらに、エラーが何であるかを理解するために-1の結果を取得した後にGetLastErrorを取得するのは好きではありません。 .NETに存在する例外スタックを促進するメカニズムは、私の美的要件をはるかに満たします。



XaocCPSからのコメント



わからない場合は、このエラー処理方法がWinAPIの特異性であることに注意してください。 その中で、すべての(またはほとんどの)関数は、HRESULT、0、-1などを含むエラーコードを返します...さて、GetLastErrorは実際に何が起こったかを理解するために作成された単なるシステム関数です。



実際、例外を介したエラー処理への切り替えは、.netプラットフォームの重要な機能の1つです。 リリース時点で、この瞬間は広く宣伝され、多様性の問題の解決策とエラーコードの混乱として位置付けられました。



*もしこれを知っていたら、その情報が知らなかった人に役立つことを願っています





したがって、次のステップはこれでした。ソケットを操作するメカニズムを.NETアプリケーションのレベルに引き上げることです。 これには何が必要ですか?



はじめに-ネイティブアプリケーションにあるすべての定数を列挙して収集します。これにより、必要のない場所にたむろしなくなります。



enum ADDRESS_FAMILIES : short

{

/// <summary>

/// Unspecified [value = 0].

/// </summary>

AF_UNSPEC = 0,

/// <summary>

/// Local to host (pipes, portals) [value = 1].

/// </summary>

AF_UNIX = 1,

...




* This source code was highlighted with Source Code Highlighter .








次に、WS2からエクスポートしたメソッドは、.NET環境に存在しない変数で機能します。 したがって、目的を達成するためには、 マーシャリングテクノロジーを少し工夫する必要がありました。



[StructLayout(LayoutKind.Sequential)]

public struct SocketAddres

{

public Int16 sin_family;

public UInt16 sin_port;

public Int32 sin_addr;



[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]

public String sin_zero;

}




* This source code was highlighted with Source Code Highlighter .








この構造により、リモートホストのアドレスで操作できます。



その結果、WS2ライブラリを.NETに完全にインポートできます。 これはクールですが、不十分であることがわかりました。 このライブラリの使用は非常に不便だからです。 そのため、WS2メソッドを手元に置いて、NSocketクラスの開発を開始しました。 最初のステップは、最も単純な例外クラスを作成することでした。 ネットワークでの作業には問題がたくさんあり、開発者はこれらの問題を報告する必要があるためです。 .NETでは、エラーを報告する最良の方法は、例外をスローすることです。



/// <summary>

///

/// </summary>

public class NSocketException : System.Net.Sockets.SocketException



* This source code was highlighted with Source Code Highlighter .








このクラスは、エラーを報告できるシンプルなラッパーです。 まあ、私たちはすでに間違いを持っ​​ています、今私たちはそれらを投げる何かをする必要があります。 これらの目的のために、さらに2つのクラスが作成されます。 NSocketおよびNNet。 これらのクラスは、ネットワークで基本的な作業を行います。 NNetクラスがネットワークの操作に合わせて調整されている場合、NSocketはソケットのオブジェクト指向表現です(WS2で私が最も見逃しているものです)。



/// <summary>

/// .

/// </summary>

/// <param name="bindedSocket"> , </param>

/// <returns> </returns>

public static NSocket Accept(NSocket bindedSocket)

{

WS2_NET.SocketAddres n = new WS2_NET.SocketAddres();

Int32 toref = Marshal.SizeOf(n);

NSocket s = new NSocket(WS2_NET.accept(( Int32 )bindedSocket, ref n, ref toref));

s.Connected = true ;

return s;

}




* This source code was highlighted with Source Code Highlighter .








これは、着信接続を受け入れることができるNnetクラスのメソッドの1つです。



/// <summary>

/// .

/// IPv4

/// </summary>

/// <param name="type"> </param>

/// <param name="proto"> </param>

public NSocket(NSocketType type, NProtocol proto)

{

if ( this .Disposed)

throw new InvalidOperationException( "Component is disposed" );

socket = WS2_NET.socket(2, ( Int32 )type, ( Int32 )proto); // 2 = AIF_INET

if ( this .HasError)

throw new NSocketException(WS2_NET.WSAGetLastError());

this .Closed = false ;

this .Protocol = proto;

this .SocketType = type;

}




* This source code was highlighted with Source Code Highlighter .








そしてこれは、ソケットの新しいインスタンスを初期化するNSocketクラスのコンストラクタです。



出力には何がありますか?

はい、正直なところ、私は.NETがあまり得意ではなかったので、クラスの実装にいくつかの欠陥がありましたが、一般的に、1週間でそれほど苦労せずにソケットをゼロから作成しました。 ここでは、ネイティブWS2から.NETオブジェクトへの進化の道筋をたどっていることに注意してください。 確かに、.NETにはソケットを操作するための優れたクラスだけでなく、一般的なプロトコルのサーバーとクライアントを実装するクラスもあるため、このタスクはやや要求されていません。 .NET 3.0の柱の1つであるWCFのようなものについては話していません。WCFを使用すると、ソケットの知識がなくてもネットワーク経由でプログラムを接続できます。 しかし! ネットワークプログラマーを問題から救うこれらの巨人のために、WS2はすべて忘れられています。 それらを研究することは、純粋に理解するために良いでしょう。



実際のプログラムでは、数十倍のコードを提供します。 このすべてを整理できるように、私たちは非常に一生懸命努力し、各メソッドにコメントを書きました。



その結果、ソケットでの作業が曲になりました:

try

{

NetModule.NNet.StartWS();

if (CurrExemp.SocketProtocol == NetModule.NProtocol.Tcp)

CurrExemp.Socket = new NetModule.NSocket(NetModule.NSocketType. Stream , CurrExemp.SocketProtocol);

else

CurrExemp.Socket = new NetModule.NSocket(NetModule.NSocketType.Datagram, CurrExemp.SocketProtocol);

CurrExemp.Socket.Bind(CurrExemp.Port);

if (CurrExemp.SocketProtocol == NetModule.NProtocol.Tcp)

CurrExemp.Socket.Listen();

}

catch

{

if (CurrExemp.ClientSocket != null && CurrExemp.ClientSocket.Connected)

CurrExemp.ClientSocket.Close();

if (CurrExemp.Socket != null && CurrExemp.Socket.Binded)

CurrExemp.Socket.Close();

if (NetModule.NNet.Started)

NetModule.NNet.StopWS();

MessageBox.Show( "Can't create socket on specified port!" );

return ;

}




* This source code was highlighted with Source Code Highlighter .








このコードは、TCPまたはUDPプロトコル用の新しいサーバーを簡単に起動できることを示しています。 WS2に必要な多くのチェックを実行する必要はありません。



この記事のこの情報を使用して、たとえばOpenGLライブラリを使用する独自のアプリケーションの作成を開始できます。 いいえ、それは価値がありませんが、そのようなライブラリは既に存在します。



さて、非常に複雑で高速なアセンブラーでの計算を使用して、アプリケーションを高速化することができます。 私はそれをするのがどれほど簡単かさえ言うことができます。 MASM32ここでは、WindowsでAcmeを操作するための楽しいパッケージがあります。 .NETアプリケーションにプラグインできる標準ライブラリとしてアセンブラコードをエクスポートできます。



C / C ++のカーネルとC#のフェイスを使用して、COMポートまたはUSBインターフェイスと対話するためのプログラムを作成することもできます。 純粋なCよりもC#でインターフェイスをプログラミングする方がはるかに便利であることに多くの人が同意すると思います。



まあ、そして最も重要なこと:ソケット、XMLビジネスロジック、およびネットワークアプリケーションをテストするためのサーバー自体を操作するためのすべてのソースコードをここに配置しました



さて、これはヘビとハリネズミを横切る方法についての私の話です。




All Articles