ネットワーク・プログラミング 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 2024 ExpyDoc