やっと念願のウェブサーバーができた!と言っても、自分の実力でできたものではなくて、ベースはTCP/IP Sockets in CのTCPEchoServerのコードに加えて、KENJI’S HOMEPAGEのページをかなり参考にさせて頂いて、最低限これで動くだろうというように、パズルのようにつぎはぎして作ったものですが・・・、1行1行しっかりやったので満足感はそれなりです。
問題点はめちゃくちゃあり、Forkしてない、エラーチェックがあまいといった感じであまあまです。
とにかくリクエストされたページを返すという超簡単なものです。
でも、うれし~ (≧∇≦)
実行結果 (この画像じゃよくわからんだろうけど)
端末側(Forkしてないのね・・・)
[root@vm01 ~/webServer]# ./webServer 80 Handling client 192.168.24.53 GET /index.html HTTP/1.1 GET /index.html HTTP/1.1 [root@vm01 ~/webServer]#
やたらとGETが出てますが、これはDEBUGのリターンが表示されてます。
そーす
#include <stdio.h> //printf, fprintf, perror
#include <sys/socket.h> //socket, bind, connect, recv, send
#include <arpa/inet.h> //sockaddr_in, inet_ntoa
#include <stdlib.h> //atoi, exit
#include <string.h> //memset
#include <unistd.h> //close
#include <netinet/in.h> //IPPROTO_TCP, INADDR_ANY
#include <sys/stat.h>
typedef struct {
char method[16];
char path[128];
char version[16];
} HEAD_DATA;
#define DEBUG
#define MAXPENDING 5
void DieWithError(char *errorMessage) //エラー処理関数
{
perror(errorMessage);
exit(1);
}
void CutCrLf( char *str )
{
char *p;
if ((p = strchr(str, '\r')) != NULL) *p = '\0';
else if((p = strchr(str, '\n')) != NULL) *p = '\0';
}
int CutChar( char *data, char *str, char c )
{
unsigned int i;
for(i=0; data[i] != c; i++)
str[i] = data[i];
str[i] = '\0';
return i;
}
void GetHtml( FILE *sock_fp, HEAD_DATA *data )
{
FILE *fp;
char *buf;
struct stat st;
if(stat(data->path + 1, &st) < 0){
fprintf(stderr, "404error\n");
exit(1);
}
if((buf = (char *)calloc(st.st_size, sizeof(char))) == NULL){
fprintf(stderr, "calloc\n");
exit(1);
}
fprintf(sock_fp, "%s 200 OK\r\n", data->version);
fprintf(sock_fp, "Server:sample\r\n");
fprintf(sock_fp, "Content-Type:text/html\r\n");
fprintf(sock_fp, "Content-Length:%d\r\n", (int)st.st_size);
fprintf(sock_fp, "\r\n");
if((fp = fopen(data->path + 1, "r")) == NULL){
fprintf(stderr, "fopen\n");
exit(1);
}
fread(buf, 1, st.st_size, fp);
fwrite(buf, 1, st.st_size, sock_fp);
free(buf);
fclose(fp);
exit(0);
}
void HandleTCPClient(int clntSocket)
{
FILE *sock_fp;
char *data[64], buf[2048];
HEAD_DATA headData;
if((sock_fp = fdopen(clntSocket, "r+")) == NULL){
fprintf(stderr, "fdpoen\n");
exit(1);
}
unsigned int len;
unsigned int i, k;
for(i = 0; i < 64; i++){
fgets(buf, sizeof(buf), sock_fp), (void)CutCrLf(buf);
if((len = strlen(buf)) == 0) break;
if((data[i] = (char *)calloc(len + 1, sizeof(char))) == NULL){
fprintf(stderr, "calloc\n");
exit(1);
}
strcpy(data[i], buf);
}
k = i;
#ifdef DEBUG
printf("%s\n", data[0]);
#endif
len = CutChar( data[0] , headData.method, ' ' ), len++;
len += CutChar( data[0] + len, headData.path, ' ' ), len++;
len += CutChar( data[0] + len, headData.version, '\0' ), len++;
#ifdef DEBUG
printf("%s %s %s\n", headData.method, headData.path, headData.version);
#endif
if(strstr("headData.version" , "1.0") != NULL || strstr("headData.version" , "1.1") != NULL){
fprintf(stderr, "request error\n");
exit(1);
}
GetHtml(sock_fp, &headData);
for(i=0; i < k; i++){
printf("%s", data[i]);
free(data[i]);
}
close(clntSocket); //クライアントソケットをクローズ
}
int main(int argc, char *argv[])
{
int servSock; //サーバのソケットディスクリプタ
int clntSock; //クライアントのソケットディスクリプタ
struct sockaddr_in echoServAddr; //ローカルアドレス
struct sockaddr_in echoClntAddr; //クライアントアドレス
unsigned short echoServPort; //サーバポート
unsigned int clntLen; //クライアントのアドレス構造体の長さ
if(argc != 2) //引数の数をチェック
{
fprintf(stderr, "Usage: %s <Server Port>\n", argv[0]);
exit(1);
}
echoServPort = atoi(argv[1]); //ローカルポート
//着信用のソケットを作成
if((servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
DieWithError("socket() failed");
//ローカルのアドレスの構造体を作成
memset(&echoServAddr, 0, sizeof(echoServAddr)); //構造体を0で埋める
echoServAddr.sin_family = AF_INET; //アドレスふぁいみり
echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); //ワイルドカードを使用
echoServAddr.sin_port = htons(echoServPort); //ローカルポート
//ローカルアドレスへバインド
if(bind(servSock, (struct sockaddr *)&echoServAddr, sizeof(echoServAddr)) < 0)
DieWithError("bind() fialed");
//接続を要求中というマークをソケットにつける
if(listen(servSock, MAXPENDING) < 0)
DieWithError("listen() failed");
for(;;) //繰り返し実行
{
//入出力パラメータのサイズをせっと
clntLen = sizeof(echoClntAddr);
//クライアントからの接続要求を待機
if((clntSock = accept(servSock, (struct sockaddr *)&echoClntAddr,&clntLen)) < 0)
DieWithError("accept() failed");
//clntsockはクライアントへ接続済み
printf("Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr));
HandleTCPClient(clntSock);
}
//この部分には到達しない
}
CutCrLf関数、CutChar関数、GetHtml関数はKENJIさんのオリジナルで多少たけけんが改変したところ(簡略に・・)があります。HandleTCPClient関数はTCP/IP Socket in CとKENJIさんのオリジナルとたけけんのMIXで、あとはだいたいTCP/IP Socket in Cがベースです。
今回参考にさせて頂いたKENJIさんのウェブサイトですが、プログラマのテクニックらしきものがところどころで見られて、非常に面白かったです。
KENJIさんは技術的な観点から、ウィルスやエクスプロイドのコードも追及するというスタンスで、サイトをいろいろ読み漁りながらいろいろと影響を受けたところがありんす。
たけけんは(一応)インターネット利用者のITリテラシーを向上しようという目的をもった2次元キャラなんで、多少矛盾した内容になりますが、今後はそういったことも取り入れていきたいなぁと思います。
最近そういうことにしようとおもいました。
TCP/IP Socket in Cの方は5章に入ってUDPEchoに入りました。まだまだこれからです。
