2008年7月24日木曜日

簡単サーバ

弊社 IP*Works! 製品には幾つかのサンプルが同梱されています。それらの大半は通常どのサイトでもほぼご利用可能なプロトコルを前提として作成されています。

しかし、中には既に古いプロトコルとなってしまい、現在ではサービスがなかなか提供されていないものもあります (Echo サーバや DateTime サーバ等)。

しかし、これらのサンプルは非常にシンプルであり、コンポーネントの動作を理解するにはお手軽で便利なものとなっています。(サーバがあれば、ですが。)

そこで、弊社の UDPPort および IPDaemonを使って非常にシンプルな UDP Echo サーバおよび DateTime サーバを C# 作成してみました。(サービス仕様の詳細に興味のある方は、RFC862 - Echo Protocol および RFC867 - Daytime Protocol をご参照ください。)

●UDPEcho サーバ

クリックすると拡大表示この UDP Echo サーバの UI は非常にシンプルです。ListView が1つと Button が1つだけです。VisualStudio でフォームを作成し、これらをドロップします。名前はそれぞれ既定値の ListView1 および button1 です。続いて UDPPort をフォームにドロップします。こちらも名前は規定値の udpport1 です。この状態で、以下のようなソースを作成します。(VS2005 の場合。) ユーザが定義するのは、ボタンクリックイベントハンドラ button1_Click と、UDPPort の DataIn イベントハンドラ udpport1_OnDataIn のみです。
namespace UDPEchoServer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
if (button1.Text.Equals("&Start"))
{
udpport1.Active = false;
udpport1.InvokeThrough = this;
udpport1.LocalPort = 7; // UDP Echo
udpport1.Active = true;
udpport1.AcceptData = true;

button1.Text = "&Stop";
}
else
{
udpport1.Active = false;
button1.Text = "&Start";
}
}

private void udpport1_OnDataIn(object sender, nsoftware.IPWorks.UdpportDataInEventArgs e)
{
ListViewItem newdata = new ListViewItem(new String[] {DateTime.Now.ToString(), e.SourceAddress, e.Datagram});
listView1.Items.Add(newdata);

udpport1.RemoteHost = e.SourceAddress;
udpport1.RemotePort = e.SourcePort;
udpport1.Send(e.DatagramB);
}
}
}
非常にシンプルですね。button1_Click では、サーバ停止状態 (button1.Text が "&Start") であれば、使用するポートを設定し、udpport コンポーネントをアクティブにし、データを受け入れ可能にします。サーバ動作中であれば、サーバを非アクティブにします。動作中にデータを受信すると、udpport1_OnDataIn イベントハンドラが実行されます。ここでは受信したデータグラムの情報をリストビューに表示し、受信したデータをそのまま送信元に返送しています。

次は少し複雑な DayTime サーバを見てみましょう。

●DayTime サーバ

クリックすると拡大表示こちらの DayTime サーバは、UDP/IP と TCP/IP を両方サポートする関係上 UI も少し項目が増えています。情報表示用のリストビュー、プロトコル選択用チェックボックス (UDP TIME と TCP TIME)、返す日時フォーマット選択用のラジオボタン (US 形式か UK 形式か)、そして起動/停止用のボタンです。これらをフォームのドロップした後、UDPPort コンポーネントおよび IPDaemon コンポーネントをフォームにドロップします。今回もユーザが作成しなければならないのは、起動/停止ボタンのクリックイベントハンドラ、UDPPort の OnDataIn イベントハンドラ、および IPDaemon の OnDataIn イベントハンドラのみです。
namespace DayTimeServer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void bStart_Click(object sender, EventArgs e)
{
Control[] controls = { chkbUDP, chkbTCP, rbUK, rbUS };

if (bStart.Text.Equals("&Start"))
{
foreach (Control c in controls)
{
c.Enabled = false;
}

if (chkbUDP.Checked)
{
udpport1.Active = false;
udpport1.InvokeThrough = this;
udpport1.LocalPort = 13; // UDP Daytime port
udpport1.Active = true;
udpport1.AcceptData = true;
}
if (chkbTCP.Checked)
{
ipdaemon1.InvokeThrough = this;
ipdaemon1.LocalPort = 13; // TCP Daytime port
ipdaemon1.Listening = true;
}
bStart.Text = "&Stop";
}
else
{
if (chkbUDP.Checked)
{
udpport1.AcceptData = false;
}
if (chkbTCP.Checked)
{
ipdaemon1.Listening = false;
}
foreach (Control c in controls)
{
c.Enabled = true;
}
bStart.Text = "&Start";
}
}

private void udpport1_OnDataIn(object sender, nsoftware.IPWorks.UdpportDataInEventArgs e)
{
DateTime now = DateTime.Now;
ListViewItem newdata = new ListViewItem(new string[] {
now.ToString(), e.SourceAddress, "UDP"
});
listView1.Items.Add(newdata);

String fmt = rbUK.Checked ? "dd MMM yy hh:mm:ss' UTC'" : "dddd', 'MMMM dd', 'yyyy hh':'mm':'ss'-UTC'";
String utcnow = now.ToUniversalTime().ToString(fmt);

udpport1.RemoteHost = e.SourceAddress;
udpport1.RemotePort = e.SourcePort;
udpport1.Send(System.Text.Encoding.UTF8.GetBytes(utcnow+"\r\n"));
}

private void ipdaemon1_OnDataIn(object sender, nsoftware.IPWorks.IpdaemonDataInEventArgs e)
{
DateTime now = DateTime.Now;
ListViewItem newdata = new ListViewItem(new string[] {
now.ToString(), ipdaemon1.Connections[e.ConnectionId].RemoteHost, "TCP"
});
listView1.Items.Add(newdata);

String fmt = rbUK.Checked ? "dd MMM yy hh:mm:ss' UTC'" : "dddd', 'MMMM dd', 'yyyy hh':'mm':'ss'-UTC'";
String utcnow = now.ToUniversalTime().ToString(fmt);

ipdaemon1.Send(e.ConnectionId, System.Text.Encoding.UTF8.GetBytes(utcnow+"\r\n"));

ipdaemon1.Connections[e.ConnectionId].AcceptData = false;
ipdaemon1.Connections[e.ConnectionId].Connected = false;
}
}
}
bStart_Click はサーバの起動と停止を制御します。起動時にはチェックボックスやラジオボタンを無効化して動作中に変更できなくし、またプロトコル選択チェックボックスの状態に応じて UDPPort コンポーネントおよび IPDaemon コンポーネントのインスタンスをアクティブ化します。停止時にはプロトコルの選択状態に応じて対応するコンポーネントを非アクティブ化し、チェックボックスおよびラジオボタンを有効にします。

udpport1_OnDataIn では UDP で受信したデータから得られた送信元に対し、現在時刻を指定されたフォーマットで UDP で返信します。同様に ipdaemon1_OnDataIn では TCP で受信したデータから得られた送信元に対し現在時刻を指定されたフォーマットで TCP で返信します。なお、この DateTime サーバでは、日時は UTC で返すようにしています。

このどちらのサーバも、弊社のデモに対するテスト用サーバとしてご利用いただけます。

2008年7月9日水曜日

IP*Works! 製品の複数バージョンの共存について

殆どの IP*Works! 製品には複数のバージョンが存在しています。
通常は最新バージョンのみをご利用になることが殆どだと思いますが、共有サーバ上に複数の開発部隊がそれぞれの所有しているバージョンを同時にインストールしなければならないようなケースがあります。

この場合、製品によっては異なるバージョンの共存インストールができないものがありますのでご注意ください。基本的には以下のようになります。
  • .NET/Java 系製品は、バージョン間の共存が可能です。プロジェクトごとに参照するバージョンを指定できます。
  • ActiveX 製品は、バージョン間の共存が可能です。
  • Delphi / C++ Builder 製品はバージョン間の共存インストール自体は可能ですが、ツールパレットには1つのバージョンのアイコンしか表示できず IDE そのものが単一のバージョンしか管理できません。利用可能なバージョンはメニューの [コンポーネント] - [インストールパッケージ] で選択します。

その他の製品に関しましては、製品名およびバージョン番号をご明記の上、弊社までお問い合わせください。

なお、バージョン間共存インストールができても、同一プロジェクト内で複数のバージョンのコンポーネントを参照すること (たとえば同一のプロジェクト内で同一製品の V6 と V8 のコンポーネントを混在参照する等) はできませんのでご注意ください。

2008年7月8日火曜日

IPDaemon/IPPort の SendFile(...) と Timeout

IPDaemon や IPPort では、V8 から SendFile(...) メソッドをサポートしています。
これを使うことで、ファイルに格納されたデータをまとめて送信することができ大変便利です。

ただし、IPDaemon や IPPort の設定を既定値のまま SendFile(...) を使用すると IPWorksException (code=702; message="This operation cannot be performed in a non-blocking manner.") が発生してしまいます。

これは、IPDaemon および IPPort では既定値では Timeout は 0 になっており、非同期動作するように構成されているためです。SendFile を使用する際には Timeout 値に適当な正の整数値を指定してからお使いください。

また、IPDaemon では Timeout 値の設定には2つの方法があります。
  • DefaultTimeout プロパティを使用して、全てのコネクションの既定値として設定する方法。こちらの場合は、コネクションの生成前に値を設定しておく必要があります。
  • コネクションごとに Timeout を設定する方法。こちらの場合は、コネクション生成後でも必要に応じて当該コネクションの Timeout フィールドの値を設定・変更することが可能です。

状況に応じてこの2つの方法を使い分けることが可能です。