[ トップ | 一覧 | 編集 | 差分 | 単語検索 | 最終更新 | ]
最新版ソースコード http://star.endless.ne.jp/users/forcount/contents/scriptmaker/read11.rev1.c
/****************************************************************************** * * * 2chのような掲示板システムってP2Pで * * * * Version:01.00 Date:2000/08/26 修正者:デフォルトの名無しさん * ******************************************************************************/
/*============================================================================* 企画段階 *============================================================================*/
■NNTPの利用
・掲示版の良い点のリアルタイム制を保てない ・削除コントロールが簡単に偽装できてしまう ・多少スペックが必要
■キャッシュサーバ
・個人のスペックではきつい ・細かいキャッシングで上手く威力を発揮するか不透明
■結論
個人単位で参加出来るようなキャッシングの仕組みをつくるのが良い。
/*============================================================================* P2Pの概要 *============================================================================*/
------------------------------------------------------- | www.2ch.net (P2P Master) | ------------------------------------------------------- ↑ | ↑※3マスタ要求取得 HTTP | |HTTP(一覧(board, thread, |※4キャッシュサーバ登録 (GET/POST) | ↓ cache_ip_port)を入手) ↓ ------------------------------------------------------- | P2Pcache | ------------------------------------------------------- ※1 ↑ | ↑ HTTP | |HTTP |※2独自の (GET/POST) | ↓ ↓キャッシュやり取り ---------------- ---------------- | ブラウザ | | P2Pcache | ---------------- ----------------
※1:通常のHTTP要求時はHTTP Proxyもしくは 2ch専用のHTTP Daemonとして、P2Pcacheは動作する。 2chの場合にはCGIなどからHTTPにて一覧を入手し、 キャッシュ内容をブラウザに伝える。 キャッシュ単位はスレ単位、レス単位である。 また、この時に新規スレの追加などの情報も取得する(If-Modified-since?)。 鯖側はピアグループを決定し、一覧を送る。 2chへの書きこみはデータの統一/即時反映のため、 P2Pcacheは単に素通りさせ鯖に直接行う。 ※2:P2Pcacheは、自分がキャッシュを持たない部分については、 2chから送られてきた一覧を用い、他のキャッシュに検索にいく。 ※3:一定時間内に発見できなかった場合には、マスタ(2ch)を入手し、 自分のところにキャッシュし、ブラウザに伝える。 但し、2ch内で常時P2P Masterを立ち上げるのは良くない?ので HTTPでやりとり? ※4:P2Pcacheは初めて自分が使用された際にマスタに登録する。 有効時間を過ぎると、マスタは自動的に登録を抹消する。
/*============================================================================* 2ch側全般 *============================================================================*/
/*----------------------------------------------------------------------------* 2chへのアクセスイメージ *----------------------------------------------------------------------------*/
●index2.html にアクセスする時 ttp://www.2ch.net/board/p2p.cgi?do=floterlist にアクセス。 スレ番号の一覧が帰ってくる。 その一覧を元に各スレにアクセスする。
●特定スレにアクセスする時 ttp://www.2ch.net/board/p2p.cgi?do=reslist&thread=XXXXXX&from=XX&to=XX にアクセス。 レスのハッシュとサイズのペアの一覧が帰ってくる。 一覧を元に検索クエリーを投げ、レスのデータを集める。 その時にはもちろんサイズとハッシュを照合する。
484 名前:375 投稿日:2001/08/26(日) 03:00 あと &ls=100 っていうリクエストについてはどうしましょう。 強制的に &st=XX&to=XX に置き換えさせないと示すレスの範囲が 変わってしまいますよね.
485 名前:266 投稿日:2001/08/26(日) 03:02 >>484 それは p2p.cgi に ls オプションの処理をつければいいと思います。 p2p.cgi は今の read.cgi とほぼ同様の機能を持っているけれど 出力されるデータはスレ番号やレス要約の一覧ばかりである、 というような格好が適当じゃないでしょうか?
/*----------------------------------------------------------------------------* 2ch側機能関連 *----------------------------------------------------------------------------*/
●ピア管理 ピア管理は 2ch.net 上で行う。 read.cgi に細工をほどこし、2ch.net にアクセスしてきたユーザーで USER_AGENT が P2Pcache のそれになっているユーザーのIPを アクセスされた板の住人としてピア一覧に登録する。 この登録は時限設定するものとし、 例えば最後のアクセスから一時間放置されたら除外する。 ピア一覧には CGI 経由でアクセスすることができる。
●レスとスレの一覧管理 read.cgi に細工をすることでスレへの書き込みと同時に 書き込み内容のハッシュのリストを生成これを 2ch.net 上に保存する。 また、フローティングスレッドやスレ一覧などのデータも保存する。 P2Pcache はまずこれらのリストを 2ch.net 上から取得し、 このリストに含まれるレスをピアや 2ch.net 上から収集する。
●データ改竄対策 2ch.net 上にあるレス情報にサイズやハッシュなどを含め ピアから収集したレスとの照合を行うことである程度防げる。 照合に失敗した場合には 2ch.net から直接レスを取得する。 仮にハッシュがたまたま誤って一致してしまった場合でも 架空のレスが他のレスに入れ替わる程度で済み実害はほとんどない。 ※2chのマスタ、他のP2PcacheからもらったデータのMD5などと比較する。
MD5 C言語 http://www.rfc-editor.org/cgi-bin/rfcdoctype.pl?loc=RFC&letsgo=1321&type=ftp
MD5 Perl http://search.cpan.org/search?mode=module&query=MD5
561 名前:デフォルトの名無しさん 投稿日:01/08/26 17:03 >>553 perlのMD5チェックしてみました。 大抵は入っていると思いますが、入ってなければ、 http://www.cpan.org/modules/by-module/Digest/Digest-Perl-MD5-1.5.tar.gz 解凍して、MD5.pm突っこんで、@INC設定されてなければ 指定すればOKそうです。
ex) ./Digest-Perl-MD5-1.5/lib/Digest/Perl/MD5.pmにおいた時。 --- #!/usr/bin/perl -w -I./Digest-Perl-MD5-1.5/lib use Digest::Perl::MD5 'md5_hex'; print 'Digest is ', md5_hex 'foobarbaz', "\n"; ---
/*----------------------------------------------------------------------------* 2ch側実装関連 *----------------------------------------------------------------------------*/
●2ch.net 側の実装物について ピア管理を行う CGI の実装が必要。 次のような動作をするものでよい?
基本的な動作は 2ch.net 上の各種のリストを読み出すこと。 動作パラメータは次の通り。
do:読み出し対象リストの選択。値は以下のいずれか。 threadlist = スレ番号の一覧 reslist = レス情報の一覧 abornlist = あぼーんされたレスの番号の一覧。 board, thread:板やスレッドの指定。 from, to, ls, nofirst:read.cgi に同じ。読み出し範囲の指定。 bycontent:do==reslist && bycontent==true ならばレスの文面を返す。
レス情報は、レスIDとレスサイズ、ハッシュのペアからなる。 レスIDは時系列にそって順序比較が可能で各レスについて固有であれば何でもよい。 reslist bycontent はキャッシュヒットしなかった場合の差分更新に用いる。
●文字コードとレスID 571 名前:デフォルトの名無しさん 投稿日:01/08/26 18:22 >>568 :問題は string の文字コードをどうするかということと 既存の2chのdatがSJISなのでSJIS統一が移行が楽なんでわ。
:resID_t の中身をどうするかといったところです。 search->found->retrieve->responseが一つのセッションで 行われるなら、searchにて、boardとthreadが指定され範囲が スレ内の一意性に帰着するから、datの頭からの行数をresID_t にして、番号ずれが起きないように、削除時には\nは消さないで 削除マークを付けるのは如何? 行数==resID_tなら採番はしなくて済む。 また、書込は2ch.net一ヶ所で受けるから矛盾はでないはず。 ただし、searchからが一連のセッションでないなら、 retrieve, responseにも、board, threadを指定しないと、 resID_tだけで一意に特定するのは困難だと思う。
ちなみに、他すれでindexつけたいね、という話がありましたから board, threadは、stringでなく、一覧で数値管理の方が、 はやいしトラフィックも少しだけ減るかも。
575 名前:266 投稿日:01/08/26 18:39 ID:V6I8TU7U >>571 素直にレス番号を使うということですね。それでやってみます。 search から response までは一つのセッションにまとめられます。 board, thread の番号も long あたりで表現できるなら楽でいいです。 ところで今のスレ番号って time か何かの値ですよね? そうなら板名と long 値のペアで当面は表現できます。
/*============================================================================* P2Pcache側全般 *============================================================================*/
/*----------------------------------------------------------------------------* P2Pcacheの実装関連 *----------------------------------------------------------------------------*/
●P2Pcache間の通信仕様について P2Pプロトコルに基づいて動作するサーバント兼 その動作過程で獲得したデータを整形してブラウザに送る HTTPd としての実装が必要。 以下ではプロトコルについて述べる。 ※「2ch側実装関連」−「文字コードとレスID」なども参照のこと。
プロトコルはメッセンジャーの非同期な交換としてモデル化する。 すなわち、ピアは他のピアへとメッセンジャーを送信するが、 どのメッセンジャーも独立しており、返信を必須条件としない。 ある目的に沿って二者間でメッセンジャーが継続的に往復する時 この往復の全体をセッションと呼ぶものとする。
メッセンジャーは次のフォーマットをとる。
struct Messenger{ struct Header ; struct Message ; } ;
struct Header{ time_t timeStamp ; GUID inquirerID ; GUID answererID ; } ;
Message の中身はメッセージの種類ごとに異なる(後述)。 Header の各フィールドは次の意味を持つ。
timeStamp:メッセンジャーが生成された日時。 inquirerID:メッセンジャーの生成者を識別する一意なID。 answererID:メッセンジャーへの応答者を識別する一意なID。
inquirerIDとanswererIDはセッションごとに生成される。
ピアAが search メッセージを持つメッセンジャーを生成する。 この時にメッセンジャーの inquirerID を初期化する。 ↓ ピアAは生成したメッセンジャーを直近のピアにばらまく。 ↓ メッセンジャーを受け取ったピアはそのメッセージをチェックし、 自分の抱えるキャッシュ内を検索してレスの有無を調べる。 レスが見つからなければ受信したメッセンジャーを複製して 更に自分の直近のピアにばらまく(無限拡散防止などはgnutellaとほぼ同じ)。 この時、「メッセンジャーをどのピアからどのピアへ中継したか」を記憶する。 ↓ あるピアでついにキャッシュにヒットしたら、 メッセンジャーのメッセージを found メッセージに置換し、 answererID を新たに生成した一意なIDで初期化する。 できたメッセンジャーは自分に search メッセージを送ってきたピアに送る。 ↓ found メッセージ付きのメッセンジャーを受け取ったピアは inquirerID を調べ、 自身が過去に生成したメッセンジャーのものかどうかを確かめる。 found メッセンジャーが自分の生成したものでなかったなら 過去の中継記録中にそのIDが含まれていないかどうかを調べる。 中継記録中に含まれていた場合は中継元に更に中継する。 ↓ あるピアでついに found メッセージ付きのメッセンジャーを受信できたなら メッセージ部分を retrieve に書き換えて・・・以下略。
timeStamp はメッセンジャーの破棄に用いる。 すなわち、一定時間よりも前に生成されたメッセンジャーはその場で廃棄する。
続いて各メッセージについて。 メッセージは search, found, retrieve, response の四つとする。 この四つはこの順序で状態遷移するものでもある。
1)search メッセージ レスの検索を行う。フィールドは次の通り。
struct SearchMessage{ long size ; // メッセージサイズを示すバイト数。 string type = "search" ; short hopCountLeft ; string board ; string thread ; resID_t resFrom ; resID_t resTo ; } ;
board 板の thread スレの resFrom から resTo までの範囲のレスを探す という意味になる。search メッセージに対しては found メッセージで応答する。 見つからなければ返信はせずに search メッセージを増幅してばらまく。
2)found メッセージ 検索結果の通知を行う。フィールドは次の通り。
struct FoundMessage{ long size ; string type = "found" ; short resCount ; struct ResInfo{ resID_t resID ; short resSize ; char resMD5[16] ; } resInfos[ resCount] ; } ;
search メッセージに該当するレスの一覧の情報を返す。 この時、必ずしも完全にヒットしていなくてもよい。 例えば、resFrom=1, resTo=100 の search に対して 1 〜 40 のレスしか持っていなくても返信してよい。 found メッセージを受信したピアは次の retrieve メッセージで応答する。 ただし、found メッセージ中のハッシュが 2ch.net から取得したものと 一致しなかった場合にはこれを無視してよい。
3)retrieve メッセージ レス内容の提供を要請する。フィールドは次の通り。
struct RetrieveMessage{ long size ; string type = "retrieve" ; short resCount ; resID_t resIDs[ resCount] ; } ;
提供を要請する ID の列を resIDs に収める。 retrieve メッセージに対する返信として ピアは次の response メッセージで応答する。
4)response メッセージ レス内容を提供する。フィールドは次の通り。
struct ResponseMessage{ long size ; string type = "response" ; short resCount ; struct Response{ resID_t resID ; string content ; } reponses[ resCount] ; } ;
response メッセージを受信したピアは content から改めてハッシュを計算して照合を行う。 この段階でハッシュが一致しなければ 2ch.net 上から直接レスを集める。
なお、セッションごとに状態遷移をきちんと管理すること。 ありえない状態に属するメッセージを受け取った場合には エラーとみなしてこれを廃棄する。
●ピア間通信はTCP 558 名前:266 投稿日:01/08/26 16:54 >>557 ありがとうございます:) 早速なんですがピア間の通信は TCPよりもUDPの方がよいような気がするんですが UDPだと何か問題は出てきますか? 俺の理解の範囲内だと response メッセージで 大量のメッセージを一度に送る場合以外は問題ないように思えるんですが 穴があったらご指摘ください。
559 名前:1 投稿日:01/08/26 16:57 >>558 F/Wを超えないことがある。< UDP
560 名前:266 投稿日:01/08/26 17:02 >>559 UDPとTCPでは前者の方がブロックされていることが多い、ということですね。 これはADSLとCATVユーザーの環境で一般的な問題にもなりますね。 プロバイダ板あたりで聞けば分かるかな?
562 名前:1 投稿日:01/08/26 17:09 CATVはNATを通しているケースもあるので、UDP通らないケースが多いと思います。 ADSLはグローバルが多いのかな?(うちはそう。)
以上
YukiWiki 1.6.2 Copyright (C) 2000,2001 by Hiroshi Yuki.
Modified by aki.