2011.03.19
iPhone
UDP通信メモ
iPhone用にwifi接続のゲームパッドアプリを作ってみた。
その際にUDP通信を使ったので、それに関するメモを少々書いとく。
[TCP]
1対1の通信を確実に行うことに適する。
サーバーとクライアントと別れ、通信待機中(Listen)のサーバーにクライアントから接続(Connect)を行って始めて通信可能になる。互いの存在を確認しながら通信を行うので、相手が通信を終了したらもう片方はそれを検出できる。プロトコルとして、データ欠損時の自動再送とデータ順序の整理が含まれているため、アプリケーション側はそれらを意識することなく通信することができる。
[UDP]
1対Nの通信に適している。
コネクションの確立が不要であり、あて先とポート番号さえ決めれば決めれば送出が可能である。相手が実際に存在せずともエラーにはならない。自動再送とデータ順序の整理を含まないシンプルなプロトコルであり、アプリケーションはこれらの発生を考慮しなければならない。互いのデータのやり取りに確認処理が生じないためにオーバーヘッドが小さく、リアルタイム性の高い通信が可能である。
リアルタイム性からネット対戦なんかにはUDP通信が使われることが多い。しかし順序のズレ、データ欠損が発生する可能性があるため、それに耐えれる仕組みをいれる必要がある模様。その分コード量が増え、見かけ上複雑になることがある。アプリケーション側により再送命令などをUDPで実現することも可能だが、ヘタなことをやるとTCP以下のパフォーマンスがでない。UDPの投げっぱなし通信をうまく利用する必要がある。
[UDP送信側]
192.168.1.8の6788ポートにint型の数値1を送信するだけならこれだけ短い。
sendtoは引数にあて先を指定する。TCPのsendは事前に通信相手が確定するため、その引数はない。
送信だけならbindの必要もない。
[UDP受信側]
すべてアドレスから届く6788ポートへのデータを受信する。
受信時はそのポートを監視しないといけないためバインドが必要になる。
recvfromでは引数に送信元データの格納先を指定してやることで、どこからの通信なのか調べることができる。ただしいデータが入っていないことがある。
(貼ってあるコードは実際に動作したコードが元になっているものの、ブログ用にある程度適当に切り貼りしたものでそのまま動くかはわかりません。)
その際にUDP通信を使ったので、それに関するメモを少々書いとく。
[TCP]
1対1の通信を確実に行うことに適する。
サーバーとクライアントと別れ、通信待機中(Listen)のサーバーにクライアントから接続(Connect)を行って始めて通信可能になる。互いの存在を確認しながら通信を行うので、相手が通信を終了したらもう片方はそれを検出できる。プロトコルとして、データ欠損時の自動再送とデータ順序の整理が含まれているため、アプリケーション側はそれらを意識することなく通信することができる。
[UDP]
1対Nの通信に適している。
コネクションの確立が不要であり、あて先とポート番号さえ決めれば決めれば送出が可能である。相手が実際に存在せずともエラーにはならない。自動再送とデータ順序の整理を含まないシンプルなプロトコルであり、アプリケーションはこれらの発生を考慮しなければならない。互いのデータのやり取りに確認処理が生じないためにオーバーヘッドが小さく、リアルタイム性の高い通信が可能である。
リアルタイム性からネット対戦なんかにはUDP通信が使われることが多い。しかし順序のズレ、データ欠損が発生する可能性があるため、それに耐えれる仕組みをいれる必要がある模様。その分コード量が増え、見かけ上複雑になることがある。アプリケーション側により再送命令などをUDPで実現することも可能だが、ヘタなことをやるとTCP以下のパフォーマンスがでない。UDPの投げっぱなし通信をうまく利用する必要がある。
[UDP送信側]
//送りたいデータ
int data = 1;
//ソケットの作成
int sock = socket(AF_INET, SOCK_DGRAM, 0);
//あて先
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(6788);
addr.sin_addr.s_addr = inet_addr("192.168.1.8");
socklen_t addr_size = (socklen_t)sizeof(struct sockaddr_in);
//送信
sendto(sock, &data, sizeof(int), 0, (struct sockaddr*)&addr, addr_size);
192.168.1.8の6788ポートにint型の数値1を送信するだけならこれだけ短い。
sendtoは引数にあて先を指定する。TCPのsendは事前に通信相手が確定するため、その引数はない。
送信だけならbindの必要もない。
[UDP受信側]
//受信データ
int data = 0;
//ソケットの作成
int sock = socket(AF_INET, SOCK_DGRAM, 0);
//受信アドレス
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(6788);
addr.sin_addr.s_addr = INADDR_ANY;
socklen_t addr_size = (socklen_t)sizeof(struct sockaddr_in);
//送信元アドレス格納先
struct sockaddr_in addr2;
socklen_t addr_size2;
//ソケットのバインド
bind(sock, (struct sockaddr*)&addr, addr_size);
//受信
recvfrom(sock, &data, sizeof(int), 0, (struct sockaddr*)&addr2, &addr_size2);
すべてアドレスから届く6788ポートへのデータを受信する。
受信時はそのポートを監視しないといけないためバインドが必要になる。
recvfromでは引数に送信元データの格納先を指定してやることで、どこからの通信なのか調べることができる。ただしいデータが入っていないことがある。
(貼ってあるコードは実際に動作したコードが元になっているものの、ブログ用にある程度適当に切り貼りしたものでそのまま動くかはわかりません。)