今回から、少し「ソケット」のプログラムをします。
これは、すでにSDK編で取り扱っていますが、コンソールアプリでも
扱えます。
「ソケット」とは通信路の端点に結びついているもの、とでも理解しておけば
充分です。プログラム側ではこのソケットに結合さえすれば他のコンピュータと
通信が可能となります(OSが違っても)。もともとはUNIXで使われていたのですが、
これのWindows版がWinsockです。
Winsockでは、ライブラリにWSock32.libを使うので、プロジェクトに参加させておきます。 また、ソースファイルではwinsock.hまたはwinsock2.hをインクルードしておく必要があります。 基本的機能しか使わないのであればどちらでも構いません。
まずは、ソケットの初期化をします(具体的にはWs2_32.dll の初期化です)。 これには、WSAStartup関数を使います。
int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData );wVersionRequestedには、ソケットのバージョンを指定します。 基本機能だけ利用するのであれば1.1で充分です。
このWORD値の上位バイトがマイナーバージョン、下位バイトがメジャーバージョン となります。
WORD値を作るにはMAKEWORDマクロが便利です。
WORD MAKEWORD( BYTE bLow, BYTE bHigh );bLowには、下位バイト、bHighには上位バイトを指定します。
lpWSADataには、 WSADATA構造体へのポインタを指定します。 WSAData構造体は次のように定義されています。
typedef struct WSAData {
WORD wVersion;
WORD wHighVersion;
char szDescription[WSADESCRIPTION_LEN+1];
char szSystemStatus[WSASYS_STATUS_LEN+1];
unsigned short iMaxSockets;
unsigned short iMaxUdpDg;
char FAR * lpVendorInfo;
} WSADATA, *LPWSADATA;
typedefしているので、あたかもこういうデータ型が有るかのように扱えます。
WSADATA wsa;という具合に取り扱えます。これは、Windowsのプログラミングでは日常的に使われる方法ですが 初めて見たときはちょっとまごついてしまいますね。
この構造体の中身はあまり詳しく知らなくても問題有りません。
サンプルでは、WSAStartup関数が実行された後、この構造体に何が格納されたかを 表示しています。
関数が成功すると0が返されます。そうでないときはSOCKET_ERRORが返されます。
次にローカルマシンのホスト名を取得するには、gethostname関数を使います。
int gethostname( char FAR *name, int namelen );nameには、ローカルマシンのホスト名を格納するバッファへのポインタを指定します。
namelenには、バッファの長さを指定します。
成功すると0が返されます。失敗時にはSOCKET_ERRORが返されます。
ホスト名がわかったらホスト情報を取得します。
これには、gethostbyname関数を使います。Winsock2を利用するのであればgetaddrinfo関数を 使います。ここでは、Winsock2の機能は利用しないので古典的なgethostbyname関数を利用する ことにします。
struct hostent FAR *gethostbyname( const char FAR *name );nameには、ホスト名を指定します。
関数が失敗するとNULLが返されます。成功するとhostent構造体のポインタが返されます。 hostent構造体は、次のように定義されています。
struct hostent {
char FAR * h_name;
char FAR * FAR * h_aliases;
short h_addrtype;
short h_length;
char FAR * FAR * h_addr_list;
};
h_nameは、ホストの正式名称です。h_aliasesは、エイリアス(あだ名)のリストです。 (複数有ることもあるのでポインタのポインタとなっています)
h_addrtypeは、アドレスのタイプです。
h_lengthは、アドレスの長さです。
h_addr_listは、ホストのアドレスのリストです。 (複数有ることもあるのでポインタのポインタとなっています)
なお、winsock.h,winsock2.h中では
#define h_addr h_addr_list[0]と定義されています。
つまり、IPアドレスを取得したいのであればhosten構造体のh_addr を捕まえれば良いことになります。
このメンバから4バイトだけin_addr構造体にコピーします。 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;
};
共用体が出てきました。普段あまりお目にかかりませんのでもう忘れた
方もいるかもしれません。C言語編第32章で解説してあります。 共用体メンバのうち一つだけが有効なのですね。
また、ヘッダファイルでは
typedef struct in_addr IN_ADDR;のようにtypedefしているので
IN_ADDR inaddr;のようにして使うことができます。
また、ヘッダファイル中では
#define s_addr S_un.S_addrのようにも定義しています。
もっともよく使われる形のIPアドレスです。 普通IPアドレスは"192.168.0.100"のような形をしていますね。
4つの1バイトの符号無し整数のならびと考えられます。
4バイトあれば表すことが可能ですね。これは、unsigned long型です。
IPアドレスを見慣れた形の文字列に変換してくれる関数がinet_ntoa関数です。
char FAR * inet_ntoa( struct in_addr in );inは、in_addr構造体です。
戻り値は、見慣れた形のIPアドレスの文字列です。(Ipv4)
前置きがすっかり長くなってしまいました。 では、サンプルを見てみましょう。
// getip.c
#include <stdio.h>
#include <winsock.h>
int main()
{
HOSTENT *lpHost;
IN_ADDR inaddr;
WSADATA wsadata;
char szBuf[256], szIP[256];
if (WSAStartup(MAKEWORD(1, 1), &wsadata)) {
printf("WSAStartup関数失敗です\n");
return -1;
}
printf("wVersion = %u\n", wsadata.wVersion);
printf("wHighVersion = %u\n", wsadata.wHighVersion);
printf("szDescription = %s\n", wsadata.szDescription);
printf("szSystemStatus = %s\n", wsadata.szSystemStatus);
printf("iMaxSockets = %d\n", wsadata.iMaxSockets);
printf("iMaxUdpDg = %d\n", wsadata.iMaxUdpDg);
gethostname(szBuf, (int)sizeof(szBuf));
printf("\nホスト名=%s\n", szBuf);
lpHost = gethostbyname(szBuf);
memcpy(&inaddr, lpHost->h_addr_list[0], 4);
strcpy(szIP, inet_ntoa(inaddr));
printf("IPアドレス=%s\n", szIP);
WSACleanup();
return 0;
}
実行結果は左の図のようになります。もちろん、このプログラムを
動かしているマシンの環境により結果は異なります。
Update Jun/18/2003 By Y.Kumei