INET DGRAM 型の通信


いよいよInternet domain でのネットワーク通信です。 異なるマシンで動く2つのプロセス で DGRAM型の通信を行います。この通信プロトコルは UDP/IP と呼ばれています。 この通信方法は youbind などで使われています。

INETの場合は接続の目印にはポートと呼ばれる番号を使います。


まずサーバー側のプログラムから見てみましょう。名前は inetudps.cc です。
INET DGRAM server : inetudps.cc のソースintegnet.h のソース

// INET で DGRAM な UDPソケットによる通信プログラムの雛型 (サーバー側) #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include "integnet.h" // 接続のポート #define PORT 12345 int main( void ) { // INET で DGRAM なソケットの作成 int s = socket( AF_INET, SOCK_DGRAM, 0 ); // 接続の目印の指定 sockaddr_in addr = SOCKADDR_IN_INIT( AF_INET, htons(PORT), InAddr(htonl(INADDR_ANY)) ); // 目印の公開 if( bind( s, (sockaddr*)&addr, sizeof(addr) ) < 0 ){ perror("bind"); exit(1); } // データの受信 char msg[64]; int len = recvfrom( s, msg, sizeof(msg), 0, NULL, NULL ); msg[len] = '\0'; // データの表示 printf("Server received : %s\n", msg ); // ソケットの廃止 close(s); return 0; }

ソケットを AF_INET で作成します。

目印の指定を sockaddr_in 型構造体で行っています。

sockaddr_in addr = SOCKADDR_IN_INIT( AF_INET, htons(PORT), InAddr(htonl(INADDR_ANY)) );
その初期化には私が作成したマクロ SOCKADDR_IN_INITを用います。

PORTは #defineで定義したポートの値(この例では 12345)です。 これを sockaddr 型変数に指定する際は必ず htons関数で network byte order に 変換して下さい。INADDR_ANY はそのマシンのどのネットワーク装置からの アクセスも受け付けることを意味します。接続するクライアントのIPアドレスでは ありません。この INADDR_ANY は "0.0.0.0" のIPを表す 4byte の整数として 定義されているのですが、これを sockaddr型変数に指定する際には、まず htonl関数でnetwork byte order に変換して、それを in_addr 型に変換すべく InAddr関数を使ってください。

bindで指定のポートにソケットが接続されます。クライアントとの 接続の準備が完了します。LOCALの時にはbindの失敗をチェックしていませんでしたが、 INETの場合にはチェックすべきです。 LOCALのbindがファイルを作るだけなので、消せばすぐに消えるものなのですが、 INETの ポートというものは閉じてもなかなか消えるものではなりません。 なので、サーバーを再起動するときにここでエラーになることが多いです。

recvfrom関数の最後の2つの引数からクライアントの情報を得ることができますが この例ではそれを利用していません。


次にクライアント側のプログラムを見てみましょう。名前は inetudpc.cc です。
INET DGRAM client : inetudpc.cc のソース

// INET で DGRAM な UDPソケットによる通信プログラムの雛型 (クライアント側) #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include "integnet.h" // 接続のポート #define PORT 12345 #define HOSI "127.0.0.1" int main( void ) { // INET で DGRAM なソケットの作成 int s = socket( AF_INET, SOCK_DGRAM, 0 ); // 送信するデータ char msg[64] = "This is client."; // 接続の目印の指定 sockaddr_in addr = SOCKADDR_IN_INIT( AF_INET, htons(PORT), InAddr(HOST) ); if( h_errno ){ herror("gethostbyname"); exit(1); } // データの送信 sendto( s, msg, sizeof(msg), 0, (sockaddr*)&addr, sizeof(addr) ); // ソケットの廃止 close(s); return 0; }

sockaddr_in型変数の指定で第4引数に接続相手のホスト名を指定するのですが ここではIPアドレスの文字列 "127.0.0.1" で指定しています。 つまり同じマシンにサーバープロセスがあると仮定しています。もし別のマシンの サーバープロセスに接続するならここにそのホスト名を記述してください。 もしホストのIPアドレスが見つからない場合には広域変数 h_errno が 0以外になっていますのでエラーとして終了してください。

sendto関数で宛先にデータを送信します。

Internetドメインでの connect関数や sendto関数は クライアント側の空きポートからパケットを送信して、 サーバー側の目的のポートに届けようとします。 Internetドメインでの通信での接続とはサーバーとクライアントの両方の IPアドレスとPORTのペアの接続なのです。例えば以下のような接続です。
ServerIP 123.45.67.8 PORT 80
ClientIP 234.56.78.9 PORT 1025
この4つの数字で1つの通信回線が表されます。なのでサーバーのIPとPORTが同じでも、 クライアントのIPかPORTが異なれば別の通信回線なのです。

connect関数や sendto関数は発信に使うポートを 1025番から 順順に探していきます。もし特定の番号のポートで発信したいなら その前に bind を使います。これについては後で説明します。


目次

Copyright(C) by Naoki Watanabe. Jan 1st, 2001.
渡辺尚貴 naoki@cms.phys.s.u-tokyo.ac.jp