今まで避けていたんだけど、そろそろC言語でsocketを使う必要が出てきたので作ってみます。Webで調べてもなかなか最小構成のプログラムがなくて、初心者には敷居が高いような・・・?ということでHTTPクライアントを作ってみました。多分これが最小かと。
#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #define BUF_LEN 256 int main(void){ int s, size; struct sockaddr_in server; char buf[BUF_LEN+1]; server.sin_family = AF_INET; server.sin_port = htons(80); server.sin_addr.s_addr = htonl((127<<24) + 1); s = socket(AF_INET, SOCK_STREAM, 0); connect(s, (struct sockaddr *)&server, sizeof(server)); sprintf(buf, "GET / HTTP/1.0\r\n\r\n"); send(s, buf, strlen(buf), 0); while (1){ if((size = recv(s, buf, BUF_LEN, 0)) > 0){ buf[size] = '\0'; printf("%s", buf); } else break; } close(s); return 0; }
まずはsocketから
socketというのはUnixから見ればファイルディスクリプターの一種。
http://www.linux.or.jp/JM/html/LDP_man-pages/man2/socket.2.html
#include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol);
内部的にはsocreateが実行されるらしく、上の3つのパラメーターによってプロトコルが決定されるらしい。
connect
socketを作ったらconnectでサーバーに接続。サーバー情報はsockaddr_inという構造体で定義するようですが、汎用性のためconnectに指定する引数はスーパークラスのsockaddr構造体としてキャストする必要があるみたいです。
struct sockaddr { sa_family_t sa_family; /* address family, AF_xxx */ char sa_data[14]; /* 14 bytes of protocol address */ };
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */ struct sockaddr_in { sa_family_t sin_family; /* Address family */ unsigned short int sin_port; /* Port number */ struct in_addr sin_addr; /* Internet address */ /* Pad to size of `struct sockaddr'. */ unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)]; };
結構強引なことをしているような。C++のクラスの実装とかも、実体はこんな感じなのかしら。
struct in_addr {
in_addr_t s_addr;
};
typedef unsigned int __u32;
なんにしても、connectするには
- protocol family
- port number
- IP address
の3点セットが必要になります。
htoXXの関数は以下のマニュアル参照。多分"host byteorder to network byteorder short/long"の略だと思う。Pentiumとかはリトルエンディアンなので、server.sin_addr.s_addrなんかをダンプしてみると"0x100007F"になってるはず*1。
http://www.linux.or.jp/JM/html/LDP_man-pages/man3/byteorder.3.html
read/write
connectしたら、あとはsocketに対してread/writeすればよい。socketはファイルディスクリプターなので、ファイルに対するようにread/writeすればよい。