Document

ネットワーク・プログラミング
UDPソケット
1.1 UDPプロトコル

UDP機能



アドレッシング(ポート番号によりプロセス(アプリ)を指定する)。
破損したUDPデータグラムを廃棄する。
UDPのサービスはベストエフォートである。つまり、パケット紛失
や到着順の逆転が起きても紛失パケットの再送やパケットの並
び替えを行わない。
AP
AP
ソケット
ソケット
UDP
TCP
TCP
IP
コンピュータ
通信
チャネル
IP
ルータ
通信
チャネル
UDP
IP
コンピュータ
1.2 UDPソケットの特徴



UDPソケットは作成後、すぐに送受信に利用できる。
つまり、コネクションを設定しない。
複数の宛先へ送信可能(送信時、アドレスを指定する)。
複数の送信元から受信可能(受信時、送信元アドレスを取得する)。
TCPの場合
UDPの場合
サーバの
ソケット
ユーザ
listen()
sendto()
accept()
accept()
カーネル
recvfrom()
ユーザ
カーネル
クライアント2
専用ソケット
TCP
クライアント1
サーバのソケット
クライアント2
UDP
クライアント1
クライアント2
1.3 UDPシステムコール
•
宛先アドレスを指定
複数アドレスへ送信可能
int sendto(int socket, const void *msg, unsigned int msgLength, int flags, struct sockaddr
*destAddr, unsigned int addrLen)
•
複数アドレスから受信可能
int recvfrom(int socket, void *msg, unsigned int msgLength, int flags, struct sockaddr *srcAddr,
unsigned int *addrLen)
入出力パラメータ
UDPEchoClient
受信メッセージの送信元アドレス
UDPEchoServer
ソケット作成
socket()
bind()
socket()
sendto()
Echo
recvfrom()
Echo
受信メッセージ
recvfrom()
sendto()
ユーザ
カーネル
close()
送信元アドレス
ソケットにポート
番号を割当る
close()
ソケット削除
recvfrom(..,*srcAddr,…)
システム
コール
書込む
メッセージ受信
srcAddr
msg
1.4 UDPのポート番号

クライアント側(TCPと同じ)


カーネルによって、自動的に割当てられる。
サーバ側(TCPと同じ)

bind()システムコールを使って設定する。
AP
AP
ソケット
ソケット
ポート番号は自動
的に割当られる
1
2
65535
bind()によって、指定した
ポート番号に割当られる
1
2
7
UDP
UDP
IP
IP
クライアント
65535
サーバ
1.5 UDPソケットによる送受信

メッセージ境界


メッセージ境界は、sendto()とrecvfrom()で1対1に対応する。
recvfrom()実行時、メッセージが受信バッファ長よりも長い場合
受信バッファ長だけ受信し、残りは廃棄される。
recvfrom()
sendto() sendto()
メッセージ 1
メッセージ 2
recvfrom()
recvfrom()
メッセージ 1
ユーザ
カーネル
受信バッファ
メッセージ
メッセージ 2
コピー
複数メッセージをまとめたり、
一つのメッセージを分割しない
廃棄
1.6 UDPエコークライアント
その1
UDPEchoClient.c その1
#define ECHOMAX 255 /* エコー文字列の最大長 */
int main(int argc, char *argv[])
{
int sock;
/* ソケット識別子 */
struct sockaddr_in echoServAddr; /* エコーサーバアドレス */
必ずしも、サーバとは限らない
struct sockaddr_in fromAddr; /* エコー送信元アドレス */
unsigned short echoServPort; /* エコーサーバのポート番号 */
unsigned int fromSize; /* recvfrom()のアドレスの入出力サイズ*/
char *servIP;
/* サーバのIPアドレス */
char *echoString;
/* エコーサーバへ送信する文字列 */
char echoBuffer[ECHOMAX+1];/* エコー文字列の受信用バッファ */
int echoStringLen;
/* エコー文字列の長さ */
int respStringLen;
/* 受信した応答の長さ */
servIP = argv[1];
/* 1つ目の引数: サーバのIPアドレス */
echoString = argv[2];
/* 2つ目の引数: エコー文字列 */
/* 入力文字列の長さをチェック */
if ((echoStringLen = strlen(echoString)) > ECHOMAX) DieWithError("Echo word too long");
if (argc == 4) echoServPort = atoi(argv[3]); /* 指定ポート番号があれば使用 */
else
echoServPort = 7; /* 7 はエコーサービスのwell-knownポート番号*/
/* UDPデータグラムソケットの作成 */
if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) DieWithError("socket() failed");
/* サーバのアドレス構造体の作成 */
memset(&echoServAddr, 0, sizeof(echoServAddr)); /* 0を埋める */
echoServAddr.sin_family = AF_INET; /* インタネットアドレスファミリ */
echoServAddr.sin_addr.s_addr = inet_addr(servIP); /* IPアドレス */
htons: バイト送信順の変換
echoServAddr.sin_port = htons(echoServPort);
/* ポート番号 */
1.6 UDPエコークライアント
その2
ソケット作成後直に送信可能
UDPEchoClient.c その2
/* 文字列をサーバへ送信 */
if (sendto(sock, echoString, echoStringLen, 0, (struct sockaddr *)
&echoServAddr, sizeof(echoServAddr)) != echoStringLen)
DieWithError("sendto() sent a different number of bytes than expected");
/* 応答を受信 */
fromSize = sizeof(fromAddr);
if ((respStringLen = recvfrom(sock, echoBuffer, ECHOMAX, 0,
(struct sockaddr *) &fromAddr, &fromSize)) != echoStringLen)
DieWithError("recvfrom() failed");
受信は1回だけ
(メッセージ境界を
保存するから)
if (echoServAddr.sin_addr.s_addr != fromAddr.sin_addr.s_addr)
{
fprintf(stderr,"Error: received a packet from unknown source.\n");
exit(1);
}
/* 受信データをNULL文字で終端させる */
echoBuffer[respStringLen] = '\0';
printf文は、\0の前まで印刷する
/* 引数のエコー文字列を表示 */
printf(“Received: %s\n”, echoBuffer);
close(sock);
exit(0);
}
メッセージを受信するまで
プログラムは停止。
メッセージが紛失するとプ
ログラムが終了しない!!
想定外のコンピュータから
のメッセージがあり得る!!
1.7 UDPエコーサーバ その1
UDPEchoServer.c その1
#define ECHOMAX 255
/*エコー文字列の最大長 */
メッセージの最初の255
文字だけ返信
int main(int argc, char *argv[])
{
int sock;
/*ソケット識別子 */
struct sockaddr_in echoServAddr; /* ローカルアドレス */
struct sockaddr_in echoClntAddr; /* クライアントアドレス */
unsigned int cliAddrLen;
/* クライアントアドレス長 */
char echoBuffer[ECHOMAX];
/* エコー文字列用バッファ */
unsigned short echoServPort; /* サーバのポート番号 */
int recvMsgSize;
/* 受信メッセージ長 */
echoServPort = atoi(argv[1]); /* 1つ目の引数: ポート番号 */
/* データグラムの送受信に使うソケットを作成 */
if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) DieWithError("socket() failed");
/* ローカルのアドレス構造体を作成 */
memset(&echoServAddr, 0, sizeof(echoServAddr)); /* 0を埋める */
echoServAddr.sin_family = AF_INET; /* インタネットアドレスファミリ */
bindでソケットにローカルアドレスを設定後、
echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* ワイルドカード */
直に受信出来る(listen()不要)
echoServAddr.sin_port = htons(echoServPort); /* ローカルポート */
/* ローカルアドレスへバインド */
if (bind(sock, (struct sockaddr *) &echoServAddr,sizeof(echoServAddr)) < 0) DieWithError("bind() failed");
1.7 UDPエコーサーバ その2
UDPEchoServer.c その2
for (;;) /* 繰返し実行 */
{
/* 入出力パラメータのサイズをセット */
cliAddrLen = sizeof(echoClntAddr);
クライアント毎にaccept()を実行し
て、ソケットを作る必要がない
/* クライアントからのメッセージ待ち */
if ((recvMsgSize = recvfrom(sock, echoBuffer, ECHOMAX, 0,(struct sockaddr *) &echoClntAddr, &cliAddrLen)) < 0)
DieWithError("recvfrom() failed");
printf("Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr));
受信は1回だけ(メッセージ
境界は保存されるから)
/* 受信したデータグラムをクライアントに返信 */
if (sendto(sock, echoBuffer, recvMsgSize, 0,(struct sockaddr *) &echoClntAddr, sizeof(echoClntAddr)) !=
recvMsgSize)
DieWithError("sendto() sent a different number of bytes than expected");
}
1ソケットから複数の
/* ここには到達しない */
IPアドレスへ送信
}
宿題11

UPDEchoClient.c, UDPEchoServer.c,
UDPEchoClient
DieWithError.cを使って、UDPEchoClientと
Echo
UDPEchoServerを作成し、プログラムの実
行結果を報告せよ(可能であれば、クライアン
Echo
トとサーバは異なるコンピュータ上で実行せ
よ)。
Hello

表紙に氏名と学籍番号を書く

プログラムの実行結果を書く
Hello

実施した内容を説明する文章を書く

レポートの締切は次の週の水曜日18:00
UDPEchoServer
[oida@rpc261 socket]$ gcc -o UDPEchoServer UDPEchoServer.c DieWithError.c
[oida@rpc261 socket]$ gcc -o UDPEchoClient UDPEchoClient.c DieWithError.c
[oida@rpc261 socket]$ ./UDPEchoServer 5000 &
[1] 11972
[oida@rpc261 socket]$ ./UDPEchoClient 150.43.220.22 Echo 5000
Handling client 150.43.220.22
Received: Echo
[oida@rpc261 socket]$ ./UDPEchoClient 150.43.220.22 Hello 5000
Handling client 150.43.220.22
Received: Hello
同じポート番号を複数の
[oida@rpc261 socket]$
人が使うことは出来ない
サーバのIPアドレス
/sbin/ifconfig
まとめ

UDPソケット




UDPプロトコル
sendto(), recvfrom()システムコール
UDPエコークライアント/サーバ
UDPソケットによる送受信