Project MMO: 2005年12月アーカイブ

 マルチスレッドについて少々調査していたらどうもWindowsとUNIXで移植性の高いコードを書くのは難しそうであることが分かった。別にスレッドで処理する部分は共通だからsocketの時のように根気よく#ifdefしていれば大丈夫だと思うがあまり可読性を犠牲にしたくないこともある。まぁチャットサーバーだからマルチスレッドにしたところで速度的なメリットはないし返って同期処理に苦労するだけかな。一応selectならOS間での移植性も高いしこれを元に各OS用のマルチスレッド版作ればいいと判断。年末発行のメルマガはselect版echoサーバーで決まり。Perl版完全移植なのでlistenをselectして1socketで多数のクライアントとの接続を行います。(完全移植という事は次はPostgreSQLとの接続か…)

 VS.NETだとちょっと高価だし、WindowsでC言語の勉強がしたいなという方はこちらがオススメ。
下記URLより無償でダウンロードできます。
Microsoft .NET Framework ダウンロード情報

MFCとか使わずDirectXプログラミングやコマンドツール中心なら導入&運用コストを考えても最強だと思う。←この分野の開発はIDEに頼るレベルじゃやっていけないし自動化も自力でスクリプト書くのに苦労はしないスキルはお持ちだと思う。

インストールに関してはダウンロードしたsetup.exeを実行して指示に従い、後は下記のようにPathの追加を行えば完了。

標準でインストールすればC:\Program Files\Microsoft.NET\SDK\v2.0\Binの下にsdkvars.batがあるのでこれを実行するとclコマンドが何処からでも使用可能になる。毎回実行コマンド入力も大変なのでmakeバッチを作ってこの記述を入れておくのもいいですね。

…とちょいとこの前のソケットサーバーコンパイルしたらwinsock.hが無いと言う。コンパイラがフリーといってもライブラリに制限あるみたいですね。.NETなのにWinsockが無いなんて驚きです。多分C#とかのFrameworkを使うほうで組まないとダメなんでしょうね。メルマガではWin環境をVS.NETとしてやってるから間違っては無いが、Winでフリー環境はいろいろと大変だなという事でした。

※BCCにはWinsockあります。(でも今は使ってないのでノンサポート)

マルチスレッドもいろいろ

| | コメント(0)

 マルチスレッドと言ってもいろいろありますね(もしかすると名称が違うだけで同じ物があるかもしれない)、2002~2003年頃はVC++とeVC++でワーカースレッドを使ってガリガリとプログラム組んでいましたが、排他は問題ないとして同期に失敗して何度もプログラムをフリーズさせてしまった事があります。今度はFreeBSDで制作するのですが、kqueue()とkevent()というものがあるらしい。これはpthreadとはまた別物のようです。
 fork()については元々windowsでサポートしない様なので作成は控えました。
そういうわけで最初はselect()でシングルプロセス・シングルスレッドだけど多数接続の同期配信という形のサーバーを作成します。これはすぐにチャットサーバーに改造可能。その後にマルチスレッドはどのライブラリを使用するか検討。kqueue()を使うとBSD以外で使えなくなるのでpthreadかな?Windowsのコンソールレベルではどういうライブラリなんだろ?pthreadで底なし沼のようなデバッグにハマりそうな悪寒。

 只今、メルマガ向けのSocketプログラミングをしているところですが(UNIX版は終了)、同じソースで条件分けする際にヘッダーファイルで定義しないとダメのかなと思っていたら、コンパイラ自身である程度定義が入っているとネット上の情報で発見。実際はコンパイル時に必ず読み込まれるヘッダー辺りにも書いていると思いますが、その内容の今回使用する部分を抜粋。

GCC(UNIX系)の場合
#define __GNUC__

Windowsの場合
#define _WIN32

つまり、ヘッダーファイルの名前が違うとか、ライブラリ関数の差異を埋めるためには、

#ifdef __GNUC__
// ここにUNIX版の処理を記述
#endif
#ifdef
// ここにWindows版の処理を記述
#endif

このようにすればOK

参考:Hey! Java Programming! --移植性--http://www.mars.dti.ne.jp/~torao/program/general/portability.html

 現在、開発に使用しているポート番号を5000にしているが、外部からの接続に成功しない。サーバー自身のFWやクライアントのFWで制限していないし、PHSでもダメなのでもしかしたらISP側で制限されているのかも?

 現在調査中で、どうしてもダメなら使えるポートを探すしかなさそう…。

追記:調べたらやっぱり外部から届いてない。BSDのFWで5000番はpassでもlog取得にして確認。ルーター内のグローバルIPからは接続に問題なくログに出力されています。ISPで制限しなければいけないほど危ないポートなのかなー?

 この前から、基本ライブラリはincludeしなくてもコンパイルが通ってしまう件について、コンパイルオプションで厳格にチェックが可能かググってみた。すぐに見つかりました。UNIXでC言語の授業あったのになにやってたんだろオレ。確か98年頃だったかな…。

まずは、-Wallオプション。これで#include忘れを解消。ワーニングがなくなるまで続ける。
あとは -02とか-04ってあるけど今のところは気にしなくてもいいかな。

参考:gccのデバッグ術
http://www-or.amp.i.kyoto-u.ac.jp/algo-eng/db/debug.html

DNSに接続する問題は解決

| | コメント(0)

 結局ソースを一通り見てみたらgethostbyaddrを使っていた。orz...
ありがちなオチでした。とりあえず必要ないだけでなく遅延の原因となるだけなので消去して1クライアント用echoサーバーが完成、Perl版の移植ということであればこれからselectを組み込むのだが折角なのでマルチスレッドに挑戦してみようかな。メルマガは時間切れなので発行なし。

 まさか、ソケットプログラミングで停滞するとは思ってなかった。というのもPerlでは何の問題もないのがC言語に移植した途端コア吐き出した。原因を調べてみるとC言語版はDNSの問い合わせをしているらしい。それまでFWで閉じていたDNSを許可したら問題なく動くのだ。でもDNSが必要な場面が全くないのでこれは1行ずつ解析して不要部分を取り除かないといけない。さらにgccってヘッダ書き忘れてもコンパイラが補完してくれるので便利といえばそうなのだが逆に移植性の低いソースを書く危険性が高くなるので個人的にはおせっかい機能。厳格にコンパイルするオプションはないのか?(Vcみたいに厳格すぎるのも悩みの種ですが)

 バックエンドで自動起動とか条件付コンパイル等いろいろ検索してみて一応答えが出ました。具体的なプログラミングもわかったので簡単に紹介、実際のコードはメルマガのネタにします。因みにpidの取得に関しては自動起動スクリプト内で可能でした。

インストーラーからサービス起動までのコード群:
configure(shスクリプト)
Makefileのテンプレート
条件分け用のヘッダテンプレート(ヘッダファイル)
起動スクリプトのテンプレート(shスクリプト)

実行順:
# ./configure [オプション]
まず、configureにmakeでしようする変数を引数に渡す。configureは受け取った引数をテンプレート内の変数定義に置換しMakefileとヘッダファイル、自動起動スクリプトを作成する。

# make
ソースをコンパイル

# make install
コンパイル済みのプログラムを指定されたディレクトリへインストール

# make clean
ソースディレクトリ内のコンパイル済みファイルを削除

# make uninstall
インストール先ディレクトリ内のコンパイル済みファイルを削除、空のディレクトリも削除

# make uninstall-force (勝手に命名)
インストール先ディレクトリをまるごと強制的に削除

一番単純な形式だと思いますが簡単に作成できました。
※因みに条件付きコンパイルはソースを書き換えているのであまりよくないかも、ただしコマンドラインでdefineする方法知らないためこうなりました。もしかするとmakeのターゲットで読み込むヘッダファイルを変えれば良いだけかもと思ってきました。make debugとするとdefineだけ書いたdebug.hを読み込むとか。

あとmake testとかって何をテストしているのか良く知らないまま使っていたりします。(←ダメですね)

 クライアント開発に使っているノートPCとBSDサーバーのノートPCがメモリ量とビデオ周り以外のスペックが同一なため同じプログラムを用いてベンチマークをしてみた。空きメモリの量に殆ど影響しない計測方法で純粋にOSとコンパイラがどれだけ最適化されているかの確認。

Windows側はVc7、BSDはgccを利用。
ベンチマークの結果BSDの勝ち。Vcはかなりパフォーマンスの良いコンパイラだが、それに勝ったのは以外だった。(本当はBSDがボロ負けすると思っていた)

テストに使ったプログラムは素数を少ない方から10万個抽出するもの。Vcで294秒、gccで278秒。因みに素数の一覧表示は行わず処理に掛かった秒数のみ表示。(一覧表示するとリモートSSHで操作しているBSDに通信ラグが影響するため)

参考書籍:
C言語による最新アルゴリズム事典

プログラムのソースはこちら

makeとshとpid

| | コメント(0) | トラックバック(0)

UNIXで開発するのに後何が必要か検討したところ、まずWin/UNIX両対応のサーバープログラムを作るということでMakefileを条件分けしないといけない。昔メルマガで条件付コンパイルについて書いたことがあったけどそのときはデバッグモードコンパイルの為に書いていてソースにdefineしていた。今度はmakeコマンドでそれを実現しないといけない。さらにUNIXの場合は起動時にpidファイルを書き出せるようにしないと後々終了時が面倒だ。さらにサービスとして起動することはバックエンドで動作するのが基本なのでシェルスクリプトでstartとstopのオプションを作る。

 この辺の情報って日本語じゃあまりないかな…

 あるとすれば既にBSDの中にあるはずなのでfindで検索。
# pwd
/usr/include
# find ./ -type f | grep sock
./netgraph/bluetooth/include/ng_btsocket.h
./netgraph/bluetooth/include/ng_btsocket_hci_raw.h
./netgraph/bluetooth/include/ng_btsocket_l2cap.h
./netgraph/bluetooth/include/ng_btsocket_rfcomm.h
./netgraph/ng_ksocket.h
./netgraph/ng_socket.h
./netgraph/ng_socketvar.h
./netipsec/keysock.h
./netkey/keysock.h
./netncp/ncp_sock.h
./sys/socket.h
./sys/socketvar.h
./sys/sockio.h

ブルートゥース用とかある…ある意味凄い。因みにC++用ライブラリは/usr/include/c++/3.4以下なので
上記には当てはまっていない。

3.4以下も探したがそれらしき名前するないです。ということはC用で構造体として組むわけですね。
それならばWindows-C/UNIX-C/UNIX-C++で使いまわしのできるコーディングで組んでみます。
Windows-C++だと殆どのケースはVC++なのでVC用クラスライブラリの方がいいと思う。

途中経過

| | コメント(0) | トラックバック(0)

C++用ライブラリ

PostgreSQL: libpq++
http://www.postgresql.jp/document/pg653doc/ej/programmer/libpqplusplus.htm

C++とソケット(UNIX)についてはまだ検索中。C言語用ならあるんだけど、C++用クラスライブラリは無いのかな?ピュアC++で組めない予感。

 今週からC++移植を始めるわけですが、ライブラリ関連の情報がなかなかヒットしない…。
現在の時点で必要だとわかっているのは、ソケット・XMLパーサ・DBコネクタの3つで、XMLパーサはすぐに見つかったがライセンス上制限があるし、簡単なXMLでやり取りするだけなので自作予定。次にDBコネクタに関しては以前見つけたような気がするが全く入手していない状況。ソケットについては元々あるはずなので入手する必要はないが手持ちの資料は全てwinsock用でC++に至ってはVC++用しか持っていなかったのでプログラミング資料探しの段階。

 実際にコーディングする時間は少ないので週末まではこんな状況かも。

 早速、C++に久しぶり挑戦なのだが今までC++といえばMFCで単機能ツール作ったり、仕事ではPocketPC2002~2003で組み込みをやっていただけなのでピュアなC++を知らなかったりする。つまりC言語で言うstdio.hのiostream.hでガリガリ組んだ事ないのです。
 今回はBSDサーバーなのでMFCとかのライブラリ無しで組むので少々敷居が高いかも知れませんが、パフォーマンスは出ると思います。

 さてさて早速C++のコンパイルが可能かどうか確認をしてC++の再勉強をします。

 先程のPerlヤバい件で、直ぐにC++への移植を決意と同時に連動予定のメルマガを再開しました。具体的なソースプログラム等はここに書くとだらだら長くなるので長くなりそうな部分はBlogでは続きの中に隠して、メルマガで公開という形がいいかも、あとテキスト整形の都合からもメルマガの方がやり易いメリットあるし。Blogの中ではメルマガのカテゴリは敢えてつくりません。連動になるのでプロジェクトごとです。本日から1~2年間はずっとProject MMOだと思います。

登録ページはこちら
http://c-production.com/service/mag-cpro/

紹介コメントが5年前のままとか、広告提携解消したリンク貼ったままとか読者3000部切って殿堂じゃなくなっているとか突っ込みどころ満載。メインサイトのメンテの方放置しすぎだな…。 orz.....

 クライアントの開発も進めて、いざテストをしたところ文字化けが発生。そもそもクライアント、サーバースクリプト、データーベース全てにおいてUNICODEで統一したのになんで文字化け?しかも全部化けるなら納得がいくが名前欄のみ化ける。

 原因を切り分けていくうちに最初に疑ったのはDBから取得した文字が化けていたので、psqlで確認するとコンソール上では問題なし、変だな~と化け文字の入った変数をダンプするとutf-8ではなくutf-8nになっていた。調べると両者の違いは先頭にBOMコードが付いているかどうかだけ。

 そこでバイナリエディタでBOMコードを調べつけてみた。
UTF-8N→UTF8の変換
頭に3バイトのBOM(Byte Order Mark)コードをつけるバイナリでEF-BB-BF
$bom = "\xEF\xBB\xBF";

 でも、化け文字の先頭にゴミが増えただけで化けたまま…。
意味がわからずいろいろ弄っているとこんな事に気づいた。

print $fh "name =$name message=$message";

実際はFLASHクライアントなのでXMLタグが付いているが説明に不要なので省略。
ここで$nameはDBからアカウントIDで検索したキャラクターの名前が入り、$messageはクライアントから直接送られた会話が入る。ここで文字化けを起こしていたのは$name。もちろん$nameに日本語を使ってなければ何ら問題はでない。この時点で$messageの方は日本語でも文字化けしないのでDBを疑っていた。

 化けているのが$nameなのでメッセージを日本語にしてテストするのも面倒・意味無しと思いメッセージを英数字にしたら、なんと$nameが文字化けせずに表示された。さらにメッセージ部分をクライアントからの入力ではなくソース内に固定の文字(日本語)を入れても化けない。

 ここまで来ると、何故他の変数が日本語かどうかによって化けるのか意味不明。最後に$nameが化ける条件でダンプ(つまりダンプするのにも$nameだけでなくほかの文字列変数(日本語)を連結)するとバイナリレベルで相違が発生していた。

[例]あいう(utf-8n) UNICODEは3バイト文字です。
DBから取り出し変数に格納した状態:E3 81 82 E3 81 84 E3 81 86
他の文字列変数と連結すると変化 :C3 A3 C2 81 C2 82 C3 A3 C2 81 C2 84 C3 A3 C2 81 C2 86

何故か6バイト文字です。
よく見るとこんなルールで変換されている。
E3 -> C3 A3
81 -> C2 81
82 -> C2 82
84 -> C2 84
86 -> C2 86

どう見てもPerlのバグです。
本当にありがとうございました

Perl5.8.7だからUNICODE大丈夫と思ったのに、Perl4からの付き合いだがホント日本語に弱い言語だなぁ…と、このままではいけないので対策を考える。

1. UNICODE禁止(FLASHはSJIS、PerlとDBはEUC)
2. この段階でC++言語へ移植を考える

1だとPerl内での変換ミスが恐いところだが慣れている変換方法なので実現は可能、但しDBは一度ドロップしないとだめ、FLASHも改造の必要あり。

2の場合は元々の目標内なので逆に近道かも知れない、但しC言語のXMLパーサとか調査が必要なので暫くプログラム組めないかも。

答えは2、Perlはお手軽実装だけの目的だったので今後はパーサを用いない小規模ブロードキャスト型のサーバースクリプトとして保管。(そのレベルでは汎用サーバーとして使用できるので使い道あり)

で、明日からはC++で組みますか。

 とりあえず、サーバー側の組み込みは完了。但しテストとデバッグはまだです。何故かクライアントの方が非常に開発しにくい状況。主な原因はASのXMLパーサーが非効率な仕様のため。

 ところで、つい最近オンラインゲームに関する書籍が販売されていることを知って早速購入。
オンラインゲームプログラミングはDirectXでクライアント開発している為参考書に、MMORPGゲームサーバープログラミングはタイムラグや分散処理のノウハウを積むために購入。どちらも著者は韓国の技術者でMMORPGにおいては既に成熟期になっているから本に書けるのだなと関心。というか最近の技術和書は入門書レベル未満(要するにネット検索で済んでしまうレベル)ばかりだし、海外の良書の翻訳本はなかなか出版されないし(正直専門書は売れる売れないの予測で出版を決めて欲しくない)、この本にいたってはMMORPGという事で一番ホットな題材でありながら国内の開発レベルが成熟してないから翻訳出版されたんだと思う。

 それともう一つ驚いたのはソースコードの入ったCDが付いていたこと。こういうのがあるとついCDのソースから弄りたくなりますが、なるべく使わない方向で勉強します。

 プログラミングの勉強は数学の勉強に通じるところがあり、初めてみるアルゴリズムや関数は数学でいうと公式。公式を使えば簡単に答えが出るが、その公式がどうやって出来ているか基礎理論がわかってないとその公式が適用できないケースが発生してときに問題が解けなくなる。プログラムでも同じで参考ソースのコピペだけで組み立てると非常に無駄な処理が増えたり、適用できないケースが発生するとバグるので急がずに着実に理解しないといけないですね。

オンラインゲームプログラミング
MMORPGゲームサーバープログラミング

このアーカイブについて

このページには、2005年12月以降に書かれたブログ記事のうちProject MMOカテゴリに属しているものが含まれています。

前のアーカイブはProject MMO: 2005年11月です。

次のアーカイブはProject MMO: 2006年1月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

ウェブページ

Powered by Movable Type 4.1