ある程度環境が整ったので、世界最強言語と評判のAdaを使用したプログラミングを再開してみる。
とりあえず、Socketを使って通信でもしてみようと思い、GNAT.Socketsについて調べてみる。が、しかし、さすがはAda、日本語の情報が皆無である。
あきらめて英語の情報を探していると、GNAT.Socketsを使ったコードのサンプルがあったので、改造してサーバのような何かを作ってみた。ちなみに、WikibooksのAdaに関するページの1部らしい。纏まっているので便利だ。
with Ada.Text_IO; use Ada.Text_IO;
with GNAT.Sockets; use GNAT.Sockets;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Streams;
use type Ada.Streams.Stream_Element_Count;
procedure Sock is
Server : Socket_Type;
Server_Address : Sock_Addr_Type;
Client : Socket_Type;
Clinet_Adress : Sock_Addr_Type;
Channel : Stream_Access;
Send : String := (1 => ASCII.CR, 2 => ASCII.LF,
3 => ASCII.CR, 4 => ASCII.LF);
Offset : Ada.Streams.Stream_Element_Count;
Data : Ada.Streams.Stream_Element_Array (1 .. 1);
begin
Initialize;
Create_Socket (Server);
Server_Address.Addr := Inet_Addr("127.0.0.1");
Server_Address.Port := 8000;
Bind_Socket (Server, Server_Address);
Listen_Socket (Server);
Ada.Text_IO.Put_Line ("Listening...");
Accept_Socket (Server, Client, Clinet_Adress);
Close_Socket (Server); -- Stop listening
Channel := Stream (Client);
loop
Ada.Streams.Read (Channel.all, Data, Offset);
exit when Offset = 0;
for I in 1 .. Offset loop
Ada.Text_IO.Put (Character'Val (Data (I)));
end loop;
end loop;
end Sock;
このコードで、TCPのポート8000でListenする。接続後は受信したデータを標準出力に表示してくれる。
Socket自体の使用方法はCと同一なのでそれほど難しくないのだが、リファレンスがろくにないのが何とも困る。
さて、Adaを勉強中の私はIOが理解できていない。Streams.Read()は第2引数のAda.Streams.Stream_Element_Array (size)が満杯になるまでバッファリングしてくれるようなのだが、用途によってはこれは好ましくない。上記のコードでは配列のサイズを1 .. 1として、1バイトごとに処理してるのだが、これは何か違う気がする。Direct_IOあたりを使うのが正しいのだろうか。
Adaはselectが言語構造に組み込まれているらしいので、次はそこら辺を弄ってみたい。が、その前に基本的なところをもう少し押さえる必要がありそうだ。
select()なんてセコいまねはしないというわけで、受信と送信を同時に行うプログラムを作ってみた。
with Ada.Text_IO; use Ada.Text_IO;
with GNAT.Sockets; use GNAT.Sockets;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Command_Line;
with Ada.Streams;
use type Ada.Streams.Stream_Element_Count;
procedure Sock is
task Connection_Send is
entry Start (Connection : in out Stream_Access);
entry Stop;
end Connection_Send;
task body Connection_Send is
Connection_Stream : Stream_Access;
Item : String(1 .. 512);
Last : Natural;
begin
accept Start (Connection : in out Stream_Access) do
Connection_Stream := Connection;
end Start;
loop
Ada.Text_IO.Get_Line (Item => Item, Last => Last);
String'Write(Connection_Stream, Item(1 .. Last) & ASCII.LF);
end loop;
end Connection_Send;
task Connection_Recieve is
entry Start (Connection : in out Stream_Access);
entry Stop;
end Connection_Recieve;
task body Connection_Recieve is
Connection_Stream : Stream_Access;
Recieve : Ada.Streams.Stream_Element_Array (1 .. 1);
Recieve_Offset : Ada.Streams.Stream_Element_Count;
begin
accept Start (Connection : in out Stream_Access) do
Connection_Stream := Connection;
end Start;
loop
Ada.Streams.Read (Connection_Stream.all, Recieve, Recieve_Offset);
exit when Recieve_Offset = 0;
for I in 1 .. Recieve_Offset loop
Ada.Text_IO.Put (Character'Val (Recieve (I)));
end loop;
end loop;
end Connection_Recieve;
Server : Socket_Type;
Server_Address : Sock_Addr_Type;
Client : Socket_Type;
Clinet_Adress : Sock_Addr_Type;
Connection : Stream_Access;
begin
Initialize;
Server_Address.Addr := Inet_Addr("127.0.0.1");
Server_Address.Port := 9999;
if Ada.Command_Line.Argument(1) = "-s" then
Create_Socket (Server);
Bind_Socket (Server, Server_Address);
Listen_Socket (Server);
Ada.Text_IO.Put_Line ("Listening...");
Accept_Socket (Server, Client, Clinet_Adress);
Close_Socket (Server); -- Stop listening
Connection := Stream (Client);
elsif Ada.Command_Line.Argument(1) = "-c" then
Ada.Text_IO.Put_Line ("Connecting...");
Create_Socket (Server);
Connect_Socket (Server, Server_Address);
Connection := Stream (Server);
end if;
Ada.Text_IO.Put_Line ("Connection Established.");
Ada.Text_IO.Put_Line ("-----------------------");
Connection_Recieve.Start(Connection);
Connection_Send.Start(Connection);
end Sock;
sock.adbという名前で保存したら、gnatmake sock.adbでコンパイルできるはず。
実行ファイルが出来たら./sock -sでサーバとして起動し、localhostの9999番ポートでlistenする。つぎに./sock -cでクライアントが起動し、同じくlocalhostの9999番ポートに接続する。接続が済めば、それぞれのターミナルで入力した文字が相手側のターミナルに表示されるはずだ。終了処理を省いているので、停止はCtrl+Cで。
最初、AdaのselectをCのselect()と似た何かだと勘違いしていたため、やや混乱した。Adaの場合はselect()を使うよりも(そもそも、どこかにあるのだろうか)、Taskで並列処理されるのが一般的なようだ。