宜しくお願いいたします。
UDPプロトコルで、連続したデータを送信する場合(ストリーミング)のアルゴリズムに
ついて伺いたいのですが、このような場合コールバック関数を実装するのが一般的なので
しょうか?
具体的には、下記のような関数をコールバック関数化するか否か、ということなのですが。
/*! @brief データを送信する。
* @param[in] 送信データ。
* @return 成功の場合は送信したデータ長(バイト)。
* 失敗の場合はSOCKET_ERROR。
*/
int CWinSock::DataTransmission(char * data)
{
ret = sendto(
sock, data,
(int)strlen(data) + 1,
0,
(SOCKADDR *)&sa,
sizeof(sa)
);
if( ret == SOCKET_ERROR ) return SOCKET_ERROR;
else return ret;
}
UDPと言うとデータグラム通信になるのでストリームという表現と合わないかなぁと。
UDPは投げっぱなしでしかも到着順番も保証されていなかったはずですし、
投げっぱなしなので基本的にすぐに呼び出しも終わったと思うのですが、
そのような状況でなぜコールバックと言う話が出てくるのかも分かりません。
実際にやりたい事のイメージをちゃんと説明した方が良いと思います。
PATIOさんお世話になります。
動画をネットワークへ送出する場合、UDPプロトコルを使うのがセオリーではないかと思
うのですが、そのデータが圧縮データの場合、データが毀損していたり、欠落していた場
合正しく、再生できないものと考えます。
ゆえに、送信が正しく行われたことを知らせるロジックが必要なので、コールバック関数
を用いるのが、良策なのではと考えたしだいなのですが、いかがなものでしょうか?
>データが毀損していたり、欠落していた場合正しく、再生できないものと考えます。
そういう場合は、その部分の再生を止めて(飛ばして)対処するのでは?
一瞬ノイズが乗るけど「途切れなく」再生されるのを望むのか、
再生が一時停止しようがコマ落ちや音飛び無しで再生されるのを望むのか、
どちらでしょう?
前者ならUDPで投げっぱなしにすればよい(受け取る側はデータの欠落や入れ替わりがある
ことを想定して処理する)し、
後者ならばTCPで送ればTCP/IPプロトコルスタックが順番を保証してくれます。
マルチキャストで送りたい場合はUDPしか選択肢無いかと思われますが。
>送信が正しく行われたことを知らせるロジックが必要
TCP使ってください。
ストリームの保証やエラーパケットの再送処理など、いろいろありますのでUDPと比較す
ると「重い」です。
UDPはエラー処理も再送処理も、順番の保証もありませんので、その分「軽い」ので、多
少のノイズが乗ろうが止まらずに再生させる必要のある場合に使用されます。
ストリーミングってファイル保存しなくて
受信しながら再生するものも指すよね。
生放送みたいなことや多数へ配信する方法の説明から
動画はUDPにしないといけないと勘違いしてんだろう。
ここでいう生放送といっても実際は録画だったりするけど。
>動画をネットワークへ送出する場合、UDPプロトコルを使うのがセオリーで
>はないかと思うのですが、
UDPを使う場合は、
1.データの送受信の間隔が一定である。
その間隔なら、データの取りこぼしのないことが保障出来ること。
2.データのパケットフォーマットにヘッダ・ーフッダ・連番が含まれていて、データの
受取る順番がバラバラになっても必ず正規のデーターに繋げること
が出来ること。
の2項目が最低限必要かなと思います。
>そのデータが圧縮データの場合、データが毀損していたり、欠落していた場
>合正しく、再生できないものと考えます。
フラグとして送信前にパケットに加えて、受信時に跳ねないとだめですね。
>送信が正しく行われたことを知らせるロジックが必要
UDPは無理ではないでしょうか。
送る前から分かればパケットに含めることが出来るでしょうけど、
まず無理でしょう。
瀬戸っぷさんの意見どおりにTCPで検討しなおした方がいいと思います。
そもそも順番が保証されていない時点でストリーミングには
不向きになりますよね。>UDP
順番が保証されていない以上、受信した順番に再生すれば良いと言う
仕様にはなりませんし、何処まで待てば順番に再生できる状態になるのかも
全く分かりませんから揃うまでバッファリングするの?という話になります。
実際の話、殆どの場合はうまく行くけど、うまく行かない時はさっぱり
うまく行かないなんて事にもなりかねません。
あと、送信側と受信側でUDPを使って送ったよ、受け取ったから次ちょうだい、なんて
やり取りをするくらいだったらTCPで実装した方がスマートだと思いますよ。
多少のデータ欠けは容認するけど、再生途中でデータ詰まりを起こして再生が引っ掛る
ことは避けたい、という用途の場合はTCPの方が不向きになりますね。
UDPを使ったストリーミングデータ転送には、有名所でRTPというプロトコルがあります。
すべてRFCで公開されていますので、未調査ならば一度調べてみる価値はあると思います。
ちなみにRTPでのデータ転送はこんな感じになります。
1. 送り手は固定長のUDPデータグラムを一定間隔でひたすら送信しつづける。
それが正しく到達したかどうかは気にしない。(4によってレートを調整することは
あります)
2. 受け手は、自身したデータグラムをある程度のバッファに溜め込んで、
到着間隔のゆらぎや順序入れ替えに対応しつつ再生を行う。
3. 再生すべき時刻までに到着しなかった部分については適当に補完するなり潔く
すっとばすなりする。
4. 個々のデータグラムに対しての受信確認は行わないが、RTCPという、RTPと対になる
プロトコルを使って、各種統計情報のフィードバックやある程度のフロー制御が可能。
訂正。
> 2. 受け手は、自身したデータグラムを
→ 2. 受け手は、受信したデータグラムを
UDPでストリーミングってのは割と普通なことだと思いますが…
ただ、欠落時の処理はノウハウの世界。欠落が処理できないなら素直にTCPが吉です。
それでもやはりUDPということならば、既に上がっているRTPとか無難かと。
(但しフルスペックで対応するのは難しいかも…>RTP)
# MPEGとかならMPEG2-TSなんてのも割りと有名なストリーミング用フォーマット。
ふむふむ、結局は送りっぱなしという前提の下、受け取り側で何とかせよ
という方針でやっているわけですよね。
それなら納得がいきます。
結局送信側に送り方の注文をある程度付ける為に
受信側から送信側へ情報を出すためのプロトコルが既定されているわけですよね。
ただ、そういう話であれば、ある受信に対する応答と言う形ではないと思いますので
コールバックの必要はないように感じます。
逆にサーバー側でも受け取る為の口を開けておけば良いだけの話ですよね。
UDPでやるなら、かっちりきっちりやり取りをすると言うよりも
受け取れた内容でそれなりに再生するという方針でやるしかないかなと。
そういう前提でUDPを使うと言うのであれば、ありなんでしょうね。
wclrp ( 'o') 2009/06/17(水) 07:52:35
さんのご指摘が多分的を射てそう。
それはそれとして、通常(TCPでなく)UDPでストリーミング再生する時ってのは、
・(古い絵を再送してもらうより新しい絵で更新するような)
ほんとにリアルタイムな動画配信
・マルチキャスト動画配信
とかだと思います。そしてUDPで再送なしの欠落補填をする方法論もあります。
> ・(古い絵を再送してもらうより新しい絵で更新するような)
> ほんとにリアルタイムな動画配信
> ・マルチキャスト動画配信
あとボイスチャットのような、双方向のリアルタイムなやり取り
なんかもそうですね。
それはそうと、蜩さんの質問を見直してみたら、ストリーミング
配信での転送成否判定についての質問ではないのではないかな?
という印象を受けました。
ストリーミング配信を実装するにあたって、配信用の外枠 +
コールバック関数という形で、UDP送信部分を独立させるか、
すべて一枚岩の閉じた処理して記述した方がよいか、という
質問ではないかと。
つまり、
// 「コールバック関数」の一例として、蜩さんの質問
// にあった、 CWinSockData がある。
// 他に、ファイル書き出しとかTCPでの転送だとかは
// コールバック関数を差し替えるだけで実現可。
データ配信の外枠関数(全データ, コールバック関数)
{
while(配信すべきデータがまだある)
{
if(コールバック関数(データ一単位分) == エラー)
{
エラー処理; // たぶん中断
}
}
}
とすべきか、UDPによる配信処理はそのように一般化できない要素が
あるため、(註: 本当にそうなのかは私は知りません)
UDPによるデータ配信関数(全データ)
{
while(配信すべきデータがまだある)
{
UDP配信に特化した送信処理, エラー処理諸々
}
}
といった、一枚岩のUDP専用の関数を作るべきか、ということを質問
しているのではないかな、と。
# RTPには、IP電話の転送品質モニタといった形で関ったことがあるけど、
# 実際にRTP使ってデータ送信してたわけじゃないので正解が予想つかん。
なるほど。
ですが、もしそうだとすると、もっとシステム全体の情報がないと
判断できないのでは、という気がします。
int CWinSock::DataTransmission(char * data)を作るか否かは、
・その部分を動的に変更する予定がある
・その部分を第三者に変更させる用意がある
・その部分を例えば別通信I/F環境に移植予定がある
などを想定しているかどうかの方がよほど重要な要因かと。
(あえてOO的な言い方をすれば、Storategyパターンの適用を考えるか否か?)
一般論でいえば、もし本当に簡単に汎化できるならしても損はない気がしますが、
コールバックは分からない人(=半素人)には理解しづらくなると思うので、
あまり美味しくないような気もします、KISSとの兼ね合いにはお気をつけて。