2009 年 2 月 のアーカイブ

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

2009 年 2 月 27 日 金曜日

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サーバを作る

2009 年 2 月 25 日 水曜日

ネットワークを通して、データをやりとりするためには、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

参考書籍

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

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

2009 年 2 月 24 日 火曜日

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の使い方 基本編

2009 年 2 月 23 日 月曜日

基本コマンド

使う前に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