Archive for 2月, 2009

[C言語]Socket間通信 HTTPクライアントを作る

金曜日, 2月 27th, 2009

C言語でのHTTPクライアントの実装例です。
以下の手順でリモートのサーバと通信し、データを取得します。

  1. gethostbyname()によりドメイン名からIPアドレスを引く
  2. socket() によりソケットを開く
  3. connect() によりサーバに接続する
  4. write() でサーバにリクエストを送る
  5. read() でレスポンスを受信する
  6. close() によりソケットを閉じる
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* bzero */

#include <sys/types.h> /* netinet/in.h */
#include <sys/socket.h> /* AF_INET */
#include <netinet/in.h> /* sockaddr_in */
#include <netdb.h> /* gethostbyname */
#include <sys/uio.h>
#include <sys/param.h>
#include <unistd.h>

#define  DESTSERV "localhost"
#define  DESTPORT 80
#define  MESSAGE  "GET / HTTP/1.0\r\nHOST: localhost\r\n\r\n"
#define  BUF_LEN  1024

int main() {

    struct hostent *hostent;
    struct sockaddr_in server;

    int fd;

    char buf[BUF_LEN]; /* receive buffer */

    hostent = gethostbyname(DESTSERV); /* lookup IP */
    if (hostent == NULL ) {
        fprintf(stderr, "Cannot resolve %s.\n", DESTSERV);
        return 0;
    }

    bzero(&server, sizeof(server)); /* zero clear struct */

    server.sin_family = AF_INET;
    /* server.sin_addr = hostent->h_addr */
    bcopy(hostent->h_addr, &server.sin_addr, hostent->h_length);
    server.sin_port = htons(DESTPORT);

    if ( ( fd = socket(AF_INET, SOCK_STREAM, 0) ) < 0) {
        fprintf(stderr, "Cannot make socket.\n");
        return 0;
    }
    if ( connect(fd, (struct sockaddr *)&server, sizeof(server)) == -1) {
        fprintf(stderr, "Cannot connect.\n");
        return 0;
    }

    write(fd, MESSAGE, strlen(MESSAGE));

    /* Receive data */
    while (read(fd, buf, BUF_LEN) > 0) {
        printf("%s", buf);
    }

    close(fd);
    return 0;
}

サーバ側の実装例については、「C言語でSocket間通信 echoサーバを作る」に記載しています。

参考書籍

猫でもわかるネットワークプログラミング 第2版 (猫でもわかるプログラミングシリーズ)
猫でもわかるネットワークプログラミング
UNIXネットワークプログラミング入門
UNIXネットワークプログラミング入門

[C言語]Socket間通信 echoサーバを作る

水曜日, 2月 25th, 2009

ネットワークを通して、データをやりとりするためには、Socketに対して、読み書きをします。
C言語による、Socket間通信は、下記のような手順になります。

  1. socket() によりソケットを開く
  2. bind() により、コネクションを受けつけるIPアドレス・ポート番号と ソケットとを対応づける
  3. listen() によりクライアントからの接続待ち受け状態にする
  4. accept() によりクライアントからの接続を受け付ける
  5. read(), write(), send(), recv() などを用いて通信を行う
  6. close() によりソケットを閉じる

実際に文字列を単純にクライアントに返すだけのプログラム(echoサーバ)を書いて見ます。
ちなみに、本コードでは、1回やりとりが終わると、サーバプロセスも終了します。

#include <stdio.h>
#include <stdlib.h>     /* exit() */
#include <string.h>     /* bzero() */
#include <sys/types.h>
#include <sys/socket.h> /* socket(), bind(), listen(), accept(), recv() */
#include <netinet/in.h> /* htons() */
#include <unistd.h>

#define PORT    8823    /* Listenするポート */
#define MAXDATA 1024    /* 受信バッファサイズ */

int main(void)
{
    struct sockaddr_in saddr; /* サーバ用アドレス格納構造体 */
    struct sockaddr_in caddr; /* クライアント用アドレス格納構造体 */

    int listen_fd;
    int conn_fd;

    int len = sizeof(struct sockaddr_in);

    int rsize;
    char buf[MAXDATA]; /* 受信バッファ */

    /* ソケットの生成 */
    if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    /* 
      * saddrの中身を0にしておかないと、bind()でエラーが起こることがある
     */
    bzero((char *)&saddr, sizeof(saddr));

    /* ソケットにアドレスとポートを結びつける */
    saddr.sin_family        = PF_INET;
    saddr.sin_addr.s_addr   = INADDR_ANY;
    saddr.sin_port          = htons(PORT);
    if (bind(listen_fd, (struct sockaddr *)&saddr, len) < 0) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    /* ポートをListenする */
    if (listen(listen_fd, SOMAXCONN) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    printf("Start Listening Port : %d...\n", PORT);

 /* 接続要求を受け付ける */
    if ((conn_fd = accept(listen_fd, (struct sockaddr *)&caddr, &len)) < 0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }

    /* Listeningソケットを閉じる */
    close(listen_fd);

    /* 送信されたデータの読み出し */
    do {
        rsize = recv(conn_fd, buf, MAXDATA, 0);

        if (rsize == 0) { /* クラアイントが接続を切ったとき */
                break;
        } else if (rsize == -1) {
                perror("recv");
                exit(EXIT_FAILURE);
        } else {
                write(conn_fd, buf, rsize);
        }
    } while (1);

    if ( close(conn_fd) < 0) {
        perror("close");
        exit(EXIT_FAILURE);
    }

    printf("Connection closed.\n");
    return 0;
}

実行方法

# gcc echoserver.c -o echoserver
# ./echoserver

別のターミナルで

# telnet localhost 8823
# (適当に文字を打ってみる)
# Ctrl-] //入力終了
telnet> quit //接続切断

参考URL

参考書籍

Linux教科書 LPICレベル1 第4版 (CD-ROM付)
Linux教科書 LPICレベル1
¥ 3,990
UNIXネットワークプログラミング入門
UNIXネットワーク
プログラミング入門
TCP/IPソケットプログラミング C言語編
TCP/IPソケット
プログラミング C言語編
¥ 175より

gdbの使い方 Apacheをデバッグに役立つTips

火曜日, 2月 24th, 2009

Apacheをシングルプロセスで立ち上げる

Apache ハンドラーを開発しているときなど、httpd を 直接 gdbにかけたい場合、シングルプロセスで起動するとデバッグしやすい。

具体的には下記のように、「-X」オプションをつけて、apacheを起動します。

$ sudo gdb httpd
gdb > run -X -f /etc/httpd/conf/httpd.conf

ブレークポイントをソース中に設定する

gdbで、”’break 30”’とかやってもいいが、ソース中でbreak pointを設定することもできる。
特定の条件下の場合のみbreakさせたい場合などに便利。

signal.hをincludeしてSIGTRAPを投げるとbreakする。

サンプルコード

 #include <stdio.h>
 #include <signal.h>
 
 int main() {
 
        printf("a\n");
        raise(SIGTRAP);
        printf("b\n");
 
        return 0;
 }

参考書籍

Apache モジュールプログラミングガイド GDBハンドブック

参考ページ

gdbの使い方 基本編

月曜日, 2月 23rd, 2009

基本コマンド

使う前にgccのオプションに「-g」をつけてコンパイルしてから使う。

コマンド 説明
break N N行目でブレーク。
break function_name 関数function_nameを呼んでいる箇所でブレーク。
print var 変数varの内容を表示。
list line_number line_number行目のソースを表示
list function_name 関数function_nameのソースを表示
step 1ステップずつ実行

動いているプロセスをデバッグ

実行ファイルの次にプロセスIDを指定する。

$ gdb /usr/local/bin/apache/apache <PID>

参考文献

GDBハンドブック

参考URL