• ベストアンサー

tcpを使った カウンタプログラム

サーバーをデーモンで常駐させてクライアントからの接続要求を確立した段階で、data.txt のデータベース(もどき)へ直アクセスしそこに書かれている数値をインクリメントするプログラムを作成しました。 どうか、以下のプログラムを参照してください。 http://userlocalhost.web.fc2.com/ 使用している端末の環境は 2.6.11-1.1369_FC4 です。 xinetd で監視させないで、スタンドアローンでサーバープログラムを動作させ、クライアントから接続要求を送ると、サーバープログラムのrecord() 関数に入って、 データの読み込み ↓ データをインクリメント ↓ データを更新 という流れで期待どおりの動作をしてくれるのですが、デーモンで常駐させると、record()関数に入らないのか、データがインクリメントされて更新されません。 もう一つ問題点として、サーバープログラム又はクライアントプログラムのいずれかをプログラムが置いてあるディレクトリよりも上の階層で実行した場合、プログラムが置いてあるディレクトリよりも遠い場所から実行したばあい、そのプログラムはセグメンテーションエラーになってしまいます。 推論として。これと最初の問題点の相関が強く、別々のファイルから同時に同じファイルへアクセスしているのがマズイ(原因)のかもしれないと思い、クライアントプログラムのmain() 内のcheck() をコメントアウトしたのですが、同様にセグメンテーションエラーが出てしまい、問題点と解決策がわからず、困っております。 どうか、問題解決の答えないしヒントを教えてください。

質問者が選んだベストアンサー

  • ベストアンサー
  • BIGT
  • ベストアンサー率42% (12/28)
回答No.7

例えばxinetdを使用した簡単なechoサーバは、こんな風に書く事が出来ます。 ※半角スペースだとインデントが崩れるので全角スペースでインデントしています。 #include <stdio.h> int main(void) {   int c;   while ((c = getchar()) != EOF)     putchar(c);   return 0; } 見て分かるとおり、ソケット関係の処理は一切含んでいません(xinetdが行うため)。 問題のプログラムですが、ようやく見れるようになったので確認させていただきました。server.cのmain()を下記のように書き換えれば、とりあえずはうまくいくのではないかと思います(未チェックです)。 int main(int argc, char **argv) {   FILE *fp;   /* ファイルへ結果を出力する */   if ((fp = fopen("/home/gakusei/programming/usb/test/sample03/sample.txt", "a")) == NULL) {     exit(1);   }   record(fp);   fclose(fp);   return 0; }

user_localhost
質問者

お礼

ありがとうございます。 デーモンで期待どおりの動作が出来ました。 xinetd について深い理解だとおもいますので、しばらくそれについて調べてみます。 又、tatsu99 さんも長きに渡り回答してくださり本当にありがとうございました。

その他の回答 (6)

  • BIGT
  • ベストアンサー率42% (12/28)
回答No.6

「コネクション管理をプログラムではなくxinetdで行う」の意味ですが、ソケットの生成等はxinetdが処理するので、自作サーバ部分はbind()などを行う必要はないという意味です。 即ち、指定ポートはxinetdがlisten()していますので、自作サーバでこのポートにbind()しようとするとエラーになります。 従って、単独で動作するサーバをそのままxinetdの監視下に置く事は出来ません。

user_localhost
質問者

お礼

回答をくださり、ありがとうございます。 おかげで、何故システムエラーメッセージで Address already in use が出てくるのか納得致しました。 自分はこれがxinetdの設定の問題だと思いましたが、xinetdの機能の問題であることを理解致しました。 xinetdの機能については表面(システムの流れ)的なことしか理解していませんでした。 問題は、プログラムをどう書き換えるか。答えはxinetdの機能を深く知ることに他ならないのかもしれませんが。この問題を解決するためのヒントをいただけませんでしょうか。

  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.5

>デーモンで接続を受けた場合には、 bind のエラーが出力されました。 bindの戻り値 及びそのときのerrnoを提示してください。 errnoにはbindエラーの理由が設定されているはずです。 又、どのようにして「デーモンで接続を受けた場合」を実現したのでしょうか。そのときのスクリプトも提示してください。 本件とは、関係ありませんが、 string = (char *)calloc(BUF_LEN, sizeof(char)); で確保したメモリを解放していません。もし、デーモンで正常に動作するようになると、次に、これが原因で、メモリの枯渇が発生します。必ず、解放するか、 char string[BUF_LEN]; memset(string,0x00,sizeof(string)); のようにして、メモリをアロケートしないことをすすめます。

user_localhost
質問者

お礼

回答をくださり、ありがとうございます。 手直ししたファイルをアップしました。 変更点は、 ・システムエラーメッセージを標準出力ではなく、ファイルへ出力した点。 ・printf() の内容を fputs() に変更し、標準出力への情報をファイルへ出力させた点。 ・文字列用の動的に確保したメモリ領域をmain() の最後で開放した点。 です。 bindのエラーは、xinetd のデーモンで実行した場合、システムエラーメッセージをファイル(sample.txt)へ出力すると、 Address already in use と出力されました。 これは、既に使用されているポートを使用してプログラムを実行させた場合に出力されるものだと思います。 つまり、これが出力されるということは、xinetd で監視してサーバープログラムを走らせると、既に5000板ポートを使っている状態でプログラムが実行されるので、このような問題が起こるのではないかと、推理できます。 そうなると、xinetd の設定の問題なのでしょうか。 /etc/xinetd.d/ 以下の設定ファイル(/etc/xinetd.d/tcp_server)の中身を記載させていただきます。 service tcp_server { disable = no flags = REUSE socket_type = stream wait = no user = root server = /home/gakusei/programming/usb/test/sample03/secondserver log_on_failure += USERID } 又、以下に /etc/xinetd.conf の内容も記載させていただきます。 defaults { instances = 60 log_type = SYSLOG authpriv log_on_success = HOST PID log_on_failure = HOST cps = 25 30 } includedir /etc/xinetd.d 更に、/etc/services には、以下のような内容を追記しました。 tcp_server 5000/tcp 以上のように設定し、xinetd を再起動させ、スタンドアローン時と同様に5000番ポートを使用してクライアントプログラムを実行させ、サーバープログラムへ接続要求を行いました。 手間をかけさせてしまい、申し訳ありません。 御回答のほど、よろしくお願い致します。

user_localhost
質問者

補足

/etc/xinetd.d/tcp_server のサーバープログラムに ..../sample03/secondserver と書きましたが、内容はオリジナルの質問のソースが置いてあるURLのserver.c をコンパイル/リンクしたものです。

  • BIGT
  • ベストアンサー率42% (12/28)
回答No.4

こちらの環境からソースコードが見えないので見当違いな指摘かもしれませんが、xinetdを使用するならコネクション管理はxinetd側で行うので、自作サーバプログラムはコネクション管理を行う必要はありません。クライアントからの要求をstdinで受け取って、クライアントへの返答をstdoutにするだけで大丈夫なはずです。

user_localhost
質問者

お礼

回答くださり、ありがとうございます。 特定のプログラムでデーモンで監視されたサーバープログラムへアクセスした時の動作の実験プログラムでして、どうしてもこの形で、サーバープログラムを動作させたいのです。 又、何故スタンドアローンで正常に動作しているサーバープログラムが、xinetdで監視させると bind でエラーになるのかが気になって仕方がないのです。 残念ながら「コネクション管理をプログラムではなくxinetdで行う」と仰る意味がよくわからないので、もう少しヒントをいただけると幸いです。

  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.3

#1です。本件、関係ないかもしれませんが、5000のポートは、予約されているポート番号です。他のポート番号の方が、安全かと思われます。参考URLに予約済みのポート一覧がありますので参考にしてください。

参考URL:
http://www.vwnet.jp/mura/tcpip-port.htm
  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.2

#1です。ソースをみて、気がついた点です。 問題点1 if((rfp = fopen("data.txt","r")) <0) ではなく if((rfp = fopen("data.txt","r")) == NULL)としてください(ファイル名は絶対パスに読み替えてください。以下同様) fopenの失敗はNULLが返ります。 問題点2 同一のファイルを同時にオープンするのは、問題があります。 fopen("data.txt","r")) でオープンしたファイルは、 読み込み完了後、即座にcloseしてください。 その後、fopen("data.txt","w"))でオープンしてください。 問題点3 listenのエラー判定をしていない。 エラーチェックをいれてください。 問題点4 acceptのエラー判定をしていない。 エラーチェックをいれてください。 上記の修正をしても、改善しない場合は、再度補足してください。 又、エラーが発生したときに、エラーの内容を標準出力(又は標準エラー)に出力していますが、これを常駐プロセスにすると、標準出力に出力したものが、ロストしますので、特定のファイル(自前のログファイル)に出力させるようにしたほうが、さらに障害の切り分けができると考えます。

user_localhost
質問者

補足

ありがとうございます。 ご指摘の通り、エラーメッセージをファイルへ出力することで、見えて来なかった原因が見えて来ました。 サーバープログラムをスタンドアローンで起動し、クライアントから接続を受けた場合には期待どおりの結果が出力されますが、デーモンで接続を受けた場合には、 bind のエラーが出力されました。 しかしながら、何故bind で問題が発生しているのかがわかりません。 スタンドアローンでは接続成功しているので、xinetdの問題なのか。しかし、xinetdが正常に動作しているので、サーバープログラムがクライアント要求に対して起動しているわけですが…

  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.1

まだ、ソースを詳しく見てませんが、とりあえず以下の点をされては、如何でしょうか? 1.ファイ名を絶対パスで指定する。 fopen("data.txt","r")ではなく、fopen("xxx/yyy/data.txt","r")のようにする。 2.-g オプションをつけてコンパイルすると、デバッグオプションでコンパイルしたことになります。 セグメンテーションエラーが発生すると、coreファイルが作成されるので、それをgdbで参照すると、どこでセグメンテーションエラーが発生したか判ります。 gdb(GNU デバッガ)がインストールされていることが、前提ですが。

user_localhost
質問者

お礼

回答くださり、ありがとうございます。 1の御指摘の通り、絶対パスでファイルをオープンしたら、セグメンテーション違反が発生しなくなりました。 そして、どのディレクトリからサーバー及びクライアントプログラムを走らせても、スタンドアローンでは期待どおりの動作をしてくれます。 しかし新たな問題として。xinetd デーモンで監視させると、今度はコネクションを確立してくれなくなりました。所有ユーザーはプログラム自体はローカルユーザーで、アクセス権限は644です。 又、/etc/xinetd.d 以下に設定したデーモンのサービスのユーザー名はroot に設定しました。 又、この問題とは無関係だと思いますが、データ(data.txt)の所有者はローカルユーザーで、アクセス権限は664です。

関連するQ&A