ネットワーク・プログラミング
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ソケットによる送受信
© Copyright 2026 ExpyDoc