経験の共有:簡単な温度モニタリングプログラムを作成する(1)

5410 ワード

前言
注意:本プロジェクトのすべてのコードの中で、クライアントのコードだけがオープンソースであり、その他の機能の実現は大まかな解決方法しか与えられず、コードはクローズソースである.クライアントのプログラムは、Linuxシステム上でのみ実行できます.
開発機材:温度センサーを取り付けた樹莓派開発板1枚、公網IPサービスを開通したルータ1台(アリクラウドにドメイン名が登録されていることが望ましい)、電源、ネットワークケーブル.
プロジェクト目標
  • お客様は、クライアントプログラムを通じて、ベリーパイ開発ボード上の温度センサがサンプリングした温度値をリアルタイムで取得することができます.
  • ベリーパイの温度サービスプログラムは、温度のサンプリングをタイミングよく行い、採取した温度データをベリーパイにインストールされたsqlite 3データベースに報告することができる.
  • ベリーパイで実行される温度サービスプログラムは、プロセス信号をリアルタイムでキャプチャし、指定されたファイルにプロセスが終了した理由とキャプチャされた信号のタイプを記録することができます.

  • りろんげん
    Unix環境の高度なプログラミングの基本内容は、ファイルI/O、socketネットワークプログラミングの初歩、マルチプロセスプログラミング、マルチスレッドプログラミングなどを含む.sqlite 3データベース基本コマンドの使用と対応するCインタフェース関数の応用.
    開発前の必要な準備
  • は、ツリーベリーパイを有線でルータに接続し、PCがパブリックネットワークIPまたはドメイン名を通じてSecureCRTにリモートでログインできることを保証する.
  • ツリーベリーパイにsqlite 3データベース(sudo apt-get install sqlite 3)、および対応するC sqlite 3インタフェース関数ライブラリ(sudo apt-get install libsqlite 3-dev)をプリインストールします.
  • 温度サービスプログラムを選定するために必要なポート番号(ポートの衝突を防ぐためには、自分の電話番号の後ろの4桁をポート番号とすることが望ましい).
  • は、クライアントプログラムまたはサービスプログラムの論理エラーを確認するために、PCにTCP Test Toolをインストールすることが望ましい.

  • クライアントコード
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include  
    #include 
    
    #define BUF_SIZE            256
    #define IP_LEN				32
    #define LOG_PATH            "./send.log"
    
    void dns(char *domain_name, char **ip); 
    void dns(char *domain_name, char **ip)
    {
    	struct hostent		*server_name = NULL;
    
    	server_name = gethostbyname(domain_name);
    	inet_ntop(server_name->h_addrtype, server_name->h_addr, *ip, 32 );
    }
    
    int main(int argc, char **argv)
    {
    	int					bind_fd = -1;
    	int					read_fd = -1;
    	int 				server_fd = -1;
    	int					connect_fd = -1;
        int                 send_id = -1;
        int                 opt = -1;
    	char				buf[BUF_SIZE];
    	char				*server_ip = (char *)malloc(IP_LEN);
    	struct sockaddr_in 	server_addr;
    	
        opt = getopt(argc, argv, "xy");
        if( opt!='x' && opt!= 'y' )
        {
            printf("temperGet usage:
    -s: read the temperature from XXX pi
    -m: read the temperature from YYY pi

    "); return -1; } if(opt == 'x') { dns("XXX.XXX.com", &server_ip); } else { dns("YYY.YYY.com", &server_ip); } server_fd = socket( AF_INET, SOCK_STREAM, 0); if(server_fd < 0) { printf("Fail to create a client socket [%d]: %s
    ", server_fd, strerror(errno) ); return 0; } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; if(opt == 'x') { server_addr.sin_port = htons(6666); printf("Get temperature from XXX.XXX.com...
    "); } else { server_addr.sin_port = htons(8888); printf("Get temperature from YYY.YYY.com...
    "); } inet_aton(server_ip, &server_addr.sin_addr); connect_fd = connect(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr) ); if(connect_fd < 0) { printf("Fail to connect sever!IP:%s error:%s

    ", inet_ntoa(server_addr.sin_addr), strerror(errno) ); return 0; } memset(buf, 0, sizeof(buf) ); read_fd = read(server_fd, buf, sizeof(buf) ); if(read_fd < 0) { printf("Fail to get temperature [%d]: %s
    ", read_fd, strerror(errno) ); return 0; } else { printf("%s

    ",buf); } memset(buf, 0, BUF_SIZE); send_id = open(LOG_PATH, O_CREAT|O_TRUNC|O_RDWR, 0666); if(send_id < 0) { printf("Fail to create a new file to record the client message :%s
    ", strerror(errno)); return -1; } dup2(send_id, STDOUT_FILENO); system("echo `date`; who | sed -n '$p'; uname -a"); lseek(send_id, SEEK_SET, 0); if( (read(send_id, buf, BUF_SIZE)) < 0) { printf("Fali to read the file :%s :%s
    ", LOG_PATH, strerror(errno)); return -1; } if( (write(server_fd, buf, BUF_SIZE)) < 0) { printf("Fail to write the server socket fd :%s
    ", strerror(errno)); return -1; } unlink(LOG_PATH); close(send_id); close(server_fd); close(read_fd); close(connect_fd); return 0; }

    クライアントのプログラムロジックを簡単に説明します.
  • 本人はすでに賢川市遠大区に公網IPサービスを持つルータを2台設置しており、2台のルータには温度を測定できる樹莓派開発板が取り付けられており、2つの開発板のドメイン名はそれぞれXXX.XX.comとYYY.YY.YY.com(この2つのドメイン名はいずれも仮名)であり、getopt関数によりパラメータ解析、すなわちクライアントがパラメータ「x」を伝達する際、前者にアクセスして、“y”を通じて後者にアクセスすることができて、dns関数は1つのドメイン名を通じてIPアドレスに解析する1つの関数で、あるサーバーのドメイン名に伝わる時、このサーバーのIPアドレスを得ることができます;
  • socketプログラミング:socket関数、connect関数を順次使用すると、サーバ側パケット付きのファイル記述子を取得することに成功し、read関数を利用してこのファイル記述子の内容を読み取り、printf関数を利用すると、サーバー側の温度情報を標準出力に印刷することに成功し、温度を取得することに成功する.
  • セキュリティのために、クライアントはLinuxのシステムコマンド(システム関数に示す)を呼び出し、取得したクライアントホスト情報を1つのファイル(./send.log))にリダイレクトして入力し、socketでプログラミングし、このファイルで読み取った内容をサーバに送信する必要があります.これにより、サーバ側のアプリケーションのメンテナンスが容易になります.
  • すべてのファイル記述子に関する関数(write、read、socket、connectなど)は、エラーの原因を特定するために、関数呼び出しに失敗する可能性があります.この場合、エラー情報を標準出力(strerror)に印刷する必要があります.

  • 以上はクライアントのコードですが、読者が読めない関数や関連関数の使い方を知らない場合は、自分で検索エンジンを利用するか、Linuxのmanマニュアルを利用して検索してください.