TCPを使う(クライアント、inet_addr、gethostbyname)

前述したTCPクライアント例では、connectする宛先は「127.0.0.1」固定でした。 ここでは、任意の宛先にconnectできるようにTCPクライアントのサンプルを改造したいと思います。

gethostbyname

前述した例では、inet_addr()を利用して「127.0.0.1」という文字列を32ビットのバイナリ値に変換していました。 inet_addr()は、IPアドレスを表現した文字列をバイナリ値に変換するための関数です。

しかし、「127.0.0.1」というようなIPアドレス表現は一般的ではなく、普通は「localhost」のような名前を利用して通信先を指定すると思います。 このような名前からIPアドレスへの変換を行ってくれる関数がgethostbyname()です。 ここでは、gethostbyname()を使ったクライアント例を示したいと思います。

以下に改造したTCPクライアントを示します。 このTCPクライアントのサンプルは、前述した単純なTCPサーバと接続します。 このTCPクライアントの動作確認を行うためには、あらかじめ前述したTCPサーバを起動しておく必要があります。


#include <stdio.h>
#include <winsock2.h>

int
main(int argc, char *argv[])
{
 WSADATA wsaData;
 struct sockaddr_in server;
 SOCKET sock;
 char buf[32];
 char *deststr;

 if (argc != 2) {
	 printf("Usage : %s dest\n", argv[0]);
	 return 1;
 }
 deststr = argv[1];

 WSAStartup(MAKEWORD(2,0), &wsaData);

 sock = socket(AF_INET, SOCK_STREAM, 0);

 server.sin_family = AF_INET;
 server.sin_port = htons(12345);

 server.sin_addr.S_un.S_addr = inet_addr(deststr);
 if (server.sin_addr.S_un.S_addr == 0xffffffff) {
	 struct hostent *host;

	 host = gethostbyname(deststr);
	 if (host == NULL) {
		 return 1;
	 }
	 server.sin_addr.S_un.S_addr =
           *(unsigned int *)host->h_addr_list[0];
 }

 connect(sock, (struct sockaddr *)&server, sizeof(server));

 memset(buf, 0, sizeof(buf));
 int n = recv(sock, buf, sizeof(buf), 0);

 printf("%d, %s\n", n, buf);

 closesocket(sock);

 WSACleanup();

 return 0;
}

上記例のポイントは、まず最初にinet_addr()を試してみて失敗しているとgethostbyname()を利用している事です。 このサンプルでは、inet_addr()とgethostbyname()両方が失敗した場合には、単純にreturn 1をしてしまっていますが、本来はWSAGetLastError()などを利用してエラー処理をして下さい。

h_addr_list[0]の前に(unsigned int *)のキャストがありますが、これを忘れると意図しない結果になるので注意しましょう。 h_addr_list[0]は、「char *」なので、*h_addr_list[0]は8ビットの値になってしまいます。 本来は、32ビットの値が欲しいので、(unsigned int *)が重要になります。

IPv6基礎検定

YouTubeチャンネルやってます!