ご存知のようにメールを出すサーバーがSMTPサーバーで、受けるほうは POP3サーバーが一般的です。まずはSMTPサーバーに接続して 簡単なメールを送るプログラムを作ってみます。
SMTPに接続するにはWinsockを少しだけ使います。
というように結構面倒くさいです。1.Wsock32.libをプロジェクトに加える 2.ソース・ファイルにWIN32_LEAN_AND_MEANをdefineする winsock2.hをインクルードする 3.WSAStartup関数でWinsockを初期化 4.gethostbyname関数でホスト情報を取得する 5.socket関数でソケットを作ります 6.getservbyname関数でサービス情報を取得する 7.getservbynameが失敗したときはhtons関数を使ってポートを指定する 8.connect関数でソケットに接続 9.recv関数で接続したソケットからのデータを取得します 10.send関数で「HELO サーバー名」で挨拶する(?) 11.recv関数でサーバーの応答を受け取る 12.対話を続ける 13.send関数で「QUIT\r\n」を送信して対話を終了 14.recv関数でサーバーの応答を受け取る 15.closesocket関数でソケットをクローズします 16.WSACleanup関数でクリーンアップ
さてWIN32_LEAN_AND_MEANとは何かというと、VC++特有の定義で、ヘッダファイルの 容量を小さくします。
また、これをdefineしないと、エラー(二重定義)が山のように出てきます。 これをソースファイルでdefineするかわりに、「プロジェクト」「設定」 「C/C++」「プリプロセッサ」で「プリプロセッサの定義」に 加えても同じことです。
また、「12.の対話を続ける」というのはSMTPのコマンドを 知らなくてはいけません。 どうもこれは(VC++の)ヘルプには書いてないようです。 で、どうするかというと直接SMTPに聞きます。 「HELP\r\n」を送信するとコマンドの一覧が返ってきます。
うすうす気づいていると思いますがSMTPに送信するときは、 最後に「\r\n」を付けます。これを忘れるといつまでたっても サーバーの応答が来ません。
この関数はW32_32.DLLを初期化します。int WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData );
wVersionRequestedにはWinsockの最低限必要なバージョンを指定します。 2.0が必要ならMAKEWORDマクロでMAKEWORD(2,0)とします。
lpWSADataはWSADATA構造体へのポインタで、これに情報が格納されて戻ってきます。 今回は、この構造体を参照しません。
ホスト情報を取得します。struct hostent FAR * gethostbyname ( const char FAR * name );
nameにはホスト名を指定します。
戻り値はHOSTENT構造体へのポインタとなります。
h_nameはホストのオフィシャル名です。struct hostent { char FAR * h_name; char FAR * FAR * h_aliases; short h_addrtype; short h_length; char FAR * FAR * h_addr_list; };
h_aliasesはホストのあだ名です。
h_addrtypeは返されるアドレスのタイプです。
h_lengthはアドレスの長さです。
h_addr_listはホストのアドレスのリストです。
ソケットを作ります。SOCKET socket ( int af, int type, int protocol );
afにはアドレスファミリィを指定します。ここではPF_INETを指定します。
typeにはソケットタイプを指定します。ここではSOCK_STREAMを指定します。
protocolにはソケットで使われるプロトコルを指定します。ここでは0を指定します。
戻り値はソケット型データとなります。(WindowsのHWNDみたいなもの)
サービス情報を取得します。struct servent FAR * getservbyname ( const char FAR * name, const char FAR * proto );
nameにはサービス名を指定します。今回はメールなので"mail"を指定します。
protoにはプロトコル名を指定します。NULLを指定すると最初のサービスエントリを 返します。
s_nameはサービスのオフィシャル名です。struct servent { char FAR * s_name; char FAR * FAR * s_aliases; short s_port; char FAR * s_proto; };
s_aliasesはサービスのあだ名です。
s_portはサービスが接触できるポート番号です。
s_protoは、サービスに接触するとき使われるプロトコル名です。
TCP/IPネットワークにおけるバイトオーダーを返します。u_short htons ( u_short hostshort );
ソケットへの接続を確立します。int connect ( SOCKET s, const struct sockaddr FAR* name, int namelen );
sはsocket関数の戻り値を指定します。
nameには接続しようとしているソケットの名前のSOCKADDR構造体へのポインタを指定します。
namelenには名前の長さを指定します。
この構造体はWinsock通信に参加しているマシンのIPアドレスを格納するのに 使いますが、実際にアドレスの各部分を設定するにはSOCKADDR_IN構造体を 使います。2つの構造体は単に型キャストすれば相互に使えます。struct sockaddr { u_short sa_family; char sa_data[14]; };
sin_familyは、アドレスファミリィ(AF_INET)を指定します。struct sockaddr_in{ short sin_family; unsigned short sin_port; struct in_addr sin_addr; char sin_zero[8]; };
sin_portにはIPポートを指定します。
sin_addrにはIPアドレスを指定します。
sin_zero[8]はSOCKADDRと同じサイズにするためのパディングです。
in_addr構造体は次のように定義されています。
さて、ここまでできればあとは、サーバーとのやり取りとなります。struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un;
sはsocket関数の戻り値です。int recv ( SOCKET s, char FAR* buf, int len, int flags );
bufにはサーバーから返されるデータのバッファです。
lenはバッファサイズを指定します。
flagsにはこの関数の呼び出しが行われる方法を指定します。 ここでは特に指定しません。
接続しているソケットにデータを送ります。int send ( SOCKET s, const char FAR * buf, int len, int flags );
sはSOCKETです(socket関数の戻り値)。
bufは転送するバッファです。
lenはバッファサイズです。
flagsはこの関数の呼び出しが起こったときの行動を指定します。 ここでは、特に指定しません。
ソケットをクローズします。int closesocket ( SOCKET s );
sはソケットを指定します。
W32_32.DLLの使用を終了します。int WSACleanup (void);
成功すれば0を返します。
さて、今回はわかったようなわからん説明でした。実際の プログラミングをみればなんとなくわかってきます。
Update 20/Sep/1999 By Y.Kumei