[BlueLeaf1336]> PROGRAM> WinSock勉強会>
| history | TOP |
2002/12/04:作成
| ソケット | TOP |
いよいよソケットを作る。で、ソケットとは何か?何だろう。 「こける」では、通信するための電話とある。媒介するもの。 音に対する空気にあたるものととらえてよいのだろうか。 光に対するエーテル(嘘)という感じだろうか。 とりあえず、問題が出てくるまでこの認識でいこう。
まず、ソケットを作る関数はこれ。
{$EXTERNALSYM socket}
function socket(af, Struct, protocol: Integer): TSocket; stdcall;
まず、アドレスファミリーとは何か。WinSock.pasを開けてみよう。
...どっさり出てきた。
{ Address families. }
{$EXTERNALSYM AF_UNSPEC}
AF_UNSPEC = 0; { unspecified }
{$EXTERNALSYM AF_UNIX}
AF_UNIX = 1; { local to host (pipes, portals) }
{$EXTERNALSYM AF_INET}
AF_INET = 2; { internetwork: UDP, TCP, etc. }
{$EXTERNALSYM AF_IMPLINK}
AF_IMPLINK = 3; { arpanet imp addresses }
{$EXTERNALSYM AF_PUP}
AF_PUP = 4; { pup protocols: e.g. BSP }
{$EXTERNALSYM AF_CHAOS}
AF_CHAOS = 5; { mit CHAOS protocols }
{$EXTERNALSYM AF_IPX}
AF_IPX = 6; { IPX and SPX }
{$EXTERNALSYM AF_NS}
AF_NS = 6; { XEROX NS protocols }
{$EXTERNALSYM AF_ISO}
AF_ISO = 7; { ISO protocols }
{$EXTERNALSYM AF_OSI}
AF_OSI = AF_ISO; { OSI is ISO }
{$EXTERNALSYM AF_ECMA}
AF_ECMA = 8; { european computer manufacturers }
{$EXTERNALSYM AF_DATAKIT}
AF_DATAKIT = 9; { datakit protocols }
{$EXTERNALSYM AF_CCITT}
AF_CCITT = 10; { CCITT protocols, X.25 etc }
{$EXTERNALSYM AF_SNA}
AF_SNA = 11; { IBM SNA }
{$EXTERNALSYM AF_DECnet}
AF_DECnet = 12; { DECnet }
{$EXTERNALSYM AF_DLI}
AF_DLI = 13; { Direct data link interface }
{$EXTERNALSYM AF_LAT}
AF_LAT = 14; { LAT }
{$EXTERNALSYM AF_HYLINK}
AF_HYLINK = 15; { NSC Hyperchannel }
{$EXTERNALSYM AF_APPLETALK}
AF_APPLETALK = 16; { AppleTalk }
{$EXTERNALSYM AF_NETBIOS}
AF_NETBIOS = 17; { NetBios-style addresses }
{$EXTERNALSYM AF_VOICEVIEW}
AF_VOICEVIEW = 18; { VoiceView }
{$EXTERNALSYM AF_FIREFOX}
AF_FIREFOX = 19; { FireFox }
{$EXTERNALSYM AF_UNKNOWN1}
AF_UNKNOWN1 = 20; { Somebody is using this! }
{$EXTERNALSYM AF_BAN}
AF_BAN = 21; { Banyan }
{$EXTERNALSYM AF_MAX}
AF_MAX = 22;
AF_INETは一回使ったことがある。 gethostbyaddr(@ip, 4, AF_INET); ただし、このときは、Structという引数名に代入される値だった。 しかし今回は、Structが別にある。それはそれとして、今回も、これを使うらしい。
で、Structだが、「こける」では、TCP/IPの場合は、SOCK_STREAM を入れると書いてある。
ということは、ここには、下の5つのうちのどれかが入るようになっているわけだ。
{ Types }
{$EXTERNALSYM SOCK_STREAM}
SOCK_STREAM = 1; { stream socket }
{$EXTERNALSYM SOCK_DGRAM}
SOCK_DGRAM = 2; { datagram socket }
{$EXTERNALSYM SOCK_RAW}
SOCK_RAW = 3; { raw-protocol interface }
{$EXTERNALSYM SOCK_RDM}
SOCK_RDM = 4; { reliably-delivered message }
{$EXTERNALSYM SOCK_SEQPACKET}
SOCK_SEQPACKET = 5; { sequenced packet stream }
さらに、protocolだが、おそらく、 getprotobynameのところで使った、IPPROTO_**が入ると思われる。ただし、0(=IPPROTO_IP)を指定することで、自動割り当てされるらしい。
そして、最も重要な、TSocketという型で作られたソケットが戻ってくる。 TSocketがどんな内容かを調べてみると、こんなの。
type
{ The new type to be used in all instances which refer to sockets. }
{$EXTERNALSYM TSocket}
TSocket = u_int;
早い話、Integerである。単なる整数値。 多分Windowsプログラミングで使うところのハンドルにあたるものだと思う。
ソケット作成に失敗した場合は、
{$EXTERNALSYM INVALID_SOCKET =}
INVALID_SOCKET = TSocket(NOT(0));
が返ってくる。
逆に、ソケットを廃棄する場合は、
{$EXTERNALSYM closesocket}
function closesocket(s: TSocket): Integer; stdcall;
今日のところはこの2つ、作成・廃棄をやってみよう。
| ソケットの作成・破棄 | TOP |
こんな感じで。
implementation
var
Sock: TSocket;
複数のボタンで、1つのソケット変数を操作するために、ユニット変数として ソケットを宣言しておく。
procedure TForm1.Button12Click(Sender: TObject);
begin
Sock := socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
//if Sock = INVALID_SOCKET then
//begin
// ShowMessage('INVALID_SOCKET');
//end;
ShowMessage(SysErrorMessage(WSAGetLastError));
end;
で、Button12Clickが、ソケットの作成。 で、予想通り、WSAStartupを呼び出したあとで、socketを呼び出さなければエラーになる。
WSAStartupより先に、上のコードで走らせると、 「アプリケーションが WSAStartup を呼び出していないか、または WSAStartup が失敗しました。」こんなエラーが表示される。つまり、WSAGetLastErrorが、エラーコードをつかんでくる。
ところが、上記コード中のコメント行を4行とも有効にすると、状況が変わってくる。
の順でメッセージ画表示される。 ここで疑問に思うのは、WSAGetLastErrorを呼び出していないのに、エラーがクリアされているということである。しているのは、変数と定数を比較してメッセージを表示しているだけである。
といいながら今試してわかったことがある。それは、
という点である。こんなことでいいんだろうか。 (※2002/12/05追記:ShowMessageが何らかのAPIを呼び 出しているなら、エラー状態が上書きされている可能性がある。 で、多分そうだろう。ウィンドウをひとつ作ってしかも成功しているわけだから。)
procedure TForm1.Button13Click(Sender: TObject);
begin
closesocket(Sock);
ShowMessage(SysErrorMessage(WSAGetLastError));
end;
それから、Button13Clickが、ソケットの破棄。
こっちは余計に何も考えることなく、作ったソケット変数を渡してやればそれでよい。表示されるエラーは、次の2つだけを確認できた。
1つ目のほうは、socket関数と同じ(つまりいっつもと同じ)エラーだが、2つ目のほうはちょっと違う。こんな感じ。
「ソケット以外のものに対して操作を実行しようとしました。」
ためしにこんなコードで試してみた。
closesocket(1);
なんとコンパイルは通る。だが、実行時には、エラー(「ソケット以外のものに対して操作を実行しようとしました。」)が表示される。
今日はここまで。
| getsockname | TOP |
2002/12/05
昨日は、TSocketを作って捨てたので、今回は、TSocketを使う関数を少しずつ見ていこう。とりあえず、getsocknameで。どっかのサイトで拾った説明によると、 「ソケットから自分のマシンのアドレスとポート番号を取得する」ための関数らしい。
{$EXTERNALSYM getsockname}
function getsockname(s: TSocket; var name: TSockAddr;
var namelen: Integer): Integer; stdcall;
TSocket以外は、すべてvarなので、ここに値がセットされて戻ってくるような気がする。
とりあえず、準備として、TSockAddrについて見る必要がある。
type
{ Structure used by kernel to store most addresses. }
{$EXTERNALSYM PSOCKADDR}
PSOCKADDR = ^TSockAddr;
{$EXTERNALSYM TSockAddr}
TSockAddr = sockaddr_in;
これによると、TSockAddrは、sockaddr_inだとわかる。 じゃあ、sockaddr_inは?というわけで、これが本体らしい。
type
PSockAddrIn = ^TSockAddrIn;
{$EXTERNALSYM sockaddr_in}
sockaddr_in = record
case Integer of
0: (sin_family: u_short;
sin_port: u_short;
sin_addr: TInAddr;
sin_zero: array[0..7] of Char);
1: (sa_family: u_short;
sa_data: array[0..13] of Char)
end;
TSockAddrIn = sockaddr_in;
「こける」には、0のほうを見ればよいとある。大きさぐらい見ておくと、 sin_portがu_shortなので、1バイト。sin_addrがTInAddrで4バイト。 sin_zeroがarray[0..7] of Charで8バイト。合計13バイト。 それから、sa_dataが、0..13の14バイト。あれ?一緒になるのかと思ったが。
というわけで、とりあえず、この関数(getsockname)が、ソケットだけを 指定すれば、残りはセットして戻してくるということにして、こんな感じで使ってみる。
procedure TForm1.Button14Click(Sender: TObject);
var
name: TSockAddr;
namelen: integer;
begin
getsockname(Sock, name, namelen);
ShowMessage(SysErrorMessage(WSAGetLastError));
end;
ん?「無効な引数が提供されました。」ときた。
どれが無効かわからん。 とりあえず、namelenに、SizeOf(TSockAddr)を渡すように変更。 大きさは16らしい。だめ。 TSockAddrをそれなりに埋めて渡すように変更。だめ。 結局こんな感じになったが、まったくおんなじエラーが出つづける。 もちろん、オープン処理・ソケット作成はやってある。 どこが何なんだろう。
var
name: TSockAddr;
namelen: integer;
retvalue: integer;
begin
FillChar(name, SizeOf(TSockAddr), #0);
namelen := SizeOf(TSockAddr);
name.sin_family := AF_INET;
name.sin_port := htons(IPPORT_FTP);
name.sin_addr.S_addr := inet_addr('127.0.0.1');
retvalue := getsockname(Sock, name, namelen);
ShowMessage(SysErrorMessage(WSAGetLastError));
ShowMessage(IntToStr(retvalue));
end;
時間が無いのでここまで。
(未完)
| EOF | TOP |