리눅스데브코스 [9주차 - 2(2)]<네트워크 프로그래밍(3) 실습>

심우열·2023년 5월 30일
0

1. 웹 브라우저 구현

1. 구현 방안

1. 구현 조건

  1. HTTP Request 메시지를 생성하여 서버로 전송
  2. 서버로부터 돌아오는 HTTP Response 메시지를 처리

2. 구현 상세

1. Command Line Argument

  1. 호스트 이름. 포트 번호, 파일
  2. 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. 파일은 현재 디렉토리에 저장

  1. 파일명은 입력한 파일 경로의 파일 명(flower.jpg)
  2. 이 파일의 URL에서 실제 파일 이름을 찾아낸 다음 그 파일 이름으로 저장

4. 출력

  1. 다운로드 성공
    -> HTTP response 메시지의 Content-Length 헤더 필드에서 파일 크기 출력
  2. 서버 연결이 안되는 경우
    -> 에러 메시지 출력
  3. HTTP Response 메시지의 status 코드가 200이 아닌 경우
    -> Response 메시지의 status 코드 라인 출력

5. 확인

  1. 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;
		}
	}  

	// now, open the file write
	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. 구현 조건

  1. 간단한 웹서버를 구현
  2. 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");

	// Create a Server Socket
	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);
	}

	/* a new connection is available on the connetion socket */
	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);
	
		// get the request from the client
		// parse the data from the client
		// give the file to the client
		strtok(buf, " \t\n\r"); // GET
		char *filename = strtok(NULL, " \t\n\r");
		char fname[1024];
		sprintf(fname, ".%s", filename);
	
		// just remove rest of the data
		headercnt = 0;	
		while (fgets(buf, BUFSIZ - 1, fp) != NULL) { 
			// read them out
			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;              /* place to store info */

		/* get file info */
		if ( stat(fname, &infobuf) == -1 )  {
			/* file not found */
			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);
//				write(new_sock, "\n", 1);
				printf("finish %d %d\n", s, (int)infobuf.st_size);
			}
		}
		if (close(new_sock)) perror("close error2");
	}
} /* main - websrv.c */

2. 작동 모습

profile
Dev Ops, "Git, Linux, Docker, Kubernetes, ansible, " .

0개의 댓글