1. 웹 브라우저 구현
1. 구현 방안
1. 구현 조건
- HTTP Request 메시지를 생성하여 서버로 전송
- 서버로부터 돌아오는 HTTP Response 메시지를 처리
2. 구현 상세
1. Command Line Argument
- 호스트 이름. 포트 번호, 파일
- ex)
./wcli upload.wikimedia.org 80 /wikipedia/ko/a/a6/Pokémon_Pikachu_art.png
2. HTTP Request 메시지
- 주어진 파라미터를 기반으로 HTTP Request 메시지 작성
- 메시지 라인과 3개의 헤더 라인만 포함
GET /member/flower.jpg HTTP/1.0
Host: naver.com
User-agent: webcli/1.0
Connection: close
3. 파일은 현재 디렉토리에 저장
- 파일명은 입력한 파일 경로의 파일 명(flower.jpg)
- 이 파일의 URL에서 실제 파일 이름을 찾아낸 다음 그 파일 이름으로 저장
4. 출력
- 다운로드 성공
-> HTTP response 메시지의 Content-Length 헤더 필드에서 파일 크기 출력
- 서버 연결이 안되는 경우
-> 에러 메시지 출력
- HTTP Response 메시지의 status 코드가 200이 아닌 경우
-> Response 메시지의 status 코드 라인 출력
5. 확인
- ls 명령으로 그 파일이 다운로드 되었는지 확인
-> content-length 필드의 크기와 저장된 파일의 크기가 같은지 비교
2. 구현
1. webcli.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int ConnectToServer(char *hostname, char * pnum, char *filename);
int ParseResponse(int sd, char *fname);
int main(int argc, char *argv[]) {
int socktoserver = -1;
if (argc != 4) {
printf("Wrong command\n");
printf("hw1 host port file\n");
return 0;
}
printf("Student ID : 01048028464\n");
printf("Name : w10sim\n");
char *hostname = argv[1];
char *pnum = argv[2];
char *filename = argv[3];
char fname[BUFSIZ];
socktoserver = ConnectToServer(hostname, pnum, filename);
if(socktoserver < 0) {
exit(0);
}
char *realname = strrchr(filename, '/');
if (realname == NULL)
strcpy(fname, filename);
else
strcpy(fname, realname + 1);
ParseResponse(socktoserver, fname);
}
int ConnectToServer(char *hostname, char * pnum, char *filename) {
int socktoserver;
struct sockaddr_in server;
if ((socktoserver = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket");
exit(1);
}
struct hostent *hostp;
if ((hostp = gethostbyname(hostname)) == 0) {
fprintf(stderr,"%s: unknown host\n", hostname);
return -1;
}
memset((void *) &server, 0, sizeof server);
server.sin_family = AF_INET;
memcpy((void *) &server.sin_addr, hostp->h_addr, hostp->h_length);
server.sin_port = htons((u_short)atoi(pnum));
if (connect(socktoserver, (struct sockaddr *)&server, sizeof server) < 0) {
return -1;
}
char msg[BUFSIZ];
sprintf(msg, "GET %s HTTP/1.0\r\nHost: %s\r\nUser-agent: webcli/1.0\r\nConnection: close\r\n\r\n", filename, hostname);
if(write(socktoserver, msg, strlen(msg)) < 0) {
perror("write");
exit(1);
}
return socktoserver;
}
int ParseResponse(int sd, char *fname) {
char buf[BUFSIZ];
FILE *fpSock = fdopen(sd, "r");
unsigned int numread = 0;
unsigned int numtoread = 0;
while(1) {
if (fgets(buf, BUFSIZ - 1, fpSock) != NULL) {
if(strcmp(buf, "\r\n") == 0) {
break;
} else if(strncmp(buf, "HTTP/", 5) == 0) {
char *field = strtok(buf, " ");
char *value = strtok(NULL, " ");
char *rest = strtok(NULL, "");
int val = atoi(value);
if(val != 200) {
printf("%s %s\n", value, rest);
fclose(fpSock);
return -1;
}
} else {
char *field = strtok(buf, ":");
char *value = strtok(NULL, " \t\n\r");
if(strcmp(field, "Content-Length") == 0) {
numtoread = atoi(value);
printf("Total Size %d bytes\n", numtoread);
}
printf("%s %s\n", field, value);
}
}
else {
printf("input error");
return 0;
}
}
FILE *fp = fopen(fname, "w+");
if(fp == NULL) {
printf("File Open Fail %s", fname);
}
unsigned int step = 1;
while(1) {
int nread = fread(buf, sizeof(char), BUFSIZ - 1, fpSock);
if( nread <= 0)
break;
numread += (unsigned int)nread;
fwrite(buf, sizeof(char), nread, fp);
unsigned int fill = (unsigned int)numread * 10 / numtoread;
if(fill >= step) {
printf("Current Downloading %d/%d (bytes) %.0f%%\n",
numread, numtoread, (float)numread / numtoread * 100);
step = fill + 1;
}
}
fclose(fpSock);
fclose(fp);
printf("Download Complete: %s, %d/%d\n",
fname, numread, numtoread);
return 1;
}
2. 작동 모습
1. HTTP Request 301
- ./wcli ko.wikipedia.org 80 /wiki/파일:Pokémon_Pikachu_art.png

2. HTTP Request 301?
HTTP Request 301 이란?
3. 301 문제 해결
- 아직 301이 무엇인지 정확히 이해가지 않는다.
- 좀 더 공부를 해보아야 할듯 하다
4. 기존 웹에서 받아오기
./wcli netapp.cs.kookmin.ac.kr 80 /member/flower.jpg


- wget 명령어를 통해 직접 다운받은 파일과 같은 크기를 갖는 것을 확인
2. 웹 서버 구현
1. 구현 방안
1. 구현 조건
- 간단한 웹서버를 구현
- HTTP Request 와 HTTP response 메시지를 잘 이해하고 처리
-> .html 파일 처리
-> .jpg 파일 처리
2. 구현 상세
2. 구현
1. websrv.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int portnum = 0;
int main(int argc, char *argv[]) {
struct sockaddr_in server, remote;
int request_sock, new_sock;
int bytesread;
socklen_t addrlen;
char buf[BUFSIZ];
int headercnt = 0;
if (argc != 2) {
(void) fprintf(stderr,"usage: %s portnum \n",argv[0]);
exit(1);
}
portnum = atoi(argv[1]);
if ((request_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket create error");
exit(1);
}
printf("Student ID : 2000000\n");
printf("Name : Sanghwan Lee\n");
memset((void *) &server, 0, sizeof server);
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons((u_short)portnum);
if (bind(request_sock, (struct sockaddr *)&server, sizeof server) < 0) {
perror("bind error");
exit(1);
}
if (listen(request_sock, SOMAXCONN) < 0) {
perror("listen error");
exit(1);
}
while(1) {
addrlen = sizeof(remote);
fflush(stdout);
new_sock = accept(request_sock,
(struct sockaddr *)&remote, &addrlen);
if (new_sock < 0) {
perror("accept error");
exit(1);
}
printf("Connection : Host IP %s, Port %d, socket %d\n",
inet_ntoa(remote.sin_addr), ntohs(remote.sin_port), new_sock);
FILE *fp = fdopen(new_sock, "r");
buf[0] = 0;
if (!fgets(buf, BUFSIZ - 1, fp)) {
printf("No Data: close connection");
fclose(fp);
if (close(new_sock)) perror("close error");
continue;
}
printf("%s", buf);
strtok(buf, " \t\n\r");
char *filename = strtok(NULL, " \t\n\r");
char fname[1024];
sprintf(fname, ".%s", filename);
headercnt = 0;
while (fgets(buf, BUFSIZ - 1, fp) != NULL) {
char *field = strtok(buf, ":");
char *value = strtok(NULL, " \t\n\r");
if(strcmp(field, "User-Agent") == 0) {
printf("User-Agent: %s\n", value);
}
headercnt++;
if((strcmp(buf, "\n") == 0) || (strcmp(buf, "\r\n") == 0))
break;
}
printf("%d headers\n", headercnt - 1);
char msg[1024];
struct stat infobuf;
if ( stat(fname, &infobuf) == -1 ) {
printf("Server Error : No such file %s!\n", fname);
sprintf(msg, "HTTP/1.0 404 NOT FOUND\r\nConnection: close\r\n"
"Content-Length: 0\r\nContent-Type: text/html\r\n\r\n");
infobuf.st_size = -1;
if (write(new_sock, msg, strlen(msg))!=strlen(msg))
perror("echo error");
} else {
char *p = strrchr(fname, '.');
if (strcmp(p, ".html") == 0) {
sprintf(msg, "HTTP/1.0 200 OK\r\nConnection: close\r\n"
"Content-Length: %d\r\nContent-Type: text/html\r\n\r\n",
(int)infobuf.st_size );
} else {
sprintf(msg, "HTTP/1.0 200 OK\r\nConnection: close\r\n"
"Content-Length: %d\r\nContent-Type: image/jpeg\r\n\r\n",
(int)infobuf.st_size );
}
if (write(new_sock, msg, strlen(msg))!=strlen(msg))
perror("echo error 2");
if(infobuf.st_size != -1) {
int s = 0;
int num = 0;
FILE *fp1 = fopen(fname, "r");
memset(buf, 0, sizeof(buf));
while((num = fread( buf, sizeof( char ), BUFSIZ - 1, fp1 )) > 0) {
s += num;
if (write(new_sock, buf, num)!= num)
perror("send file error");
}
fclose(fp1);
printf("finish %d %d\n", s, (int)infobuf.st_size);
}
}
if (close(new_sock)) perror("close error2");
}
}
2. 작동 모습