일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 소프트웨어분석및설계
- 애플 디벨로퍼 아카데미 후기
- iOS 개발 오류
- sqoop
- Swift 디자인패턴
- 제앱소
- 애플 디벨로퍼 아카데미
- 애플 디벨로퍼 아카데미 21주차 회고
- SWIFT
- 네이버 치지직
- 데이터베이스
- StateObject
- apple developer academy 후기
- 앱 비교 프로젝트
- react
- 운영체제
- Swift 기능
- swift문법
- useReducer
- 치지직
- Swift 문법
- 애플 아카데미 후기
- 데이터베이스 공부
- Apple Developer Academy @ POSTECH
- ObservableObject
- global soop
- OS
- 네이버 부스트캠프
- 숭실대
- ObservedObject
- Today
- Total
사과하는 제라스
13강. HTTP and Web Client 본문
목차
- HTTP 프로토콜이란?
: 웹서핑을 할 때 많이 볼 수 있음. TCP 기반으로 구동되는 text-based client-server 프로토콜임.
-> ∴ HTTP 서버가 구동될 때 일반적으로 80 port를 사용함.(그러다보니 웹서버로 접근 시 80 port로 접근 많이 함.)
- HTTP는 보안이 결여되어 있음
: HTTP 트래픽을 분석해보면 암호화되지 않은 상황에서 data가 오감.
∴ 스니퍼 같은 도청 공격자에게 정보가 쉽게 노출됨.
-> 기본적인 정보 보호 기술과 Transport Layer Security(TLS)를 배울 것임.
+ HTTP가 TLS 위에 구현된 HTTPS도 구현할 것임.
- HTTP 프로토콜의 구성
1) HTTP Request
1. GET : client가 어떤 resource를 server로부터 가져오고 싶을 때
2. HEAD : client가 어떤 resource의 추상적인 정보(ex) 파일의 크기)를 server로부터 가져오고 싶을 때
3. POST : client에서 server에 정보를 보낼 때
4. PUT : 새로운 데이터를 저장하거나 기존 데이터를 업데이트를 할 때 사용함. POST와 매우 비슷하지만 명확한 저장 위치를 명시하여 업데이트를 함.
5. DELETE : server에 있는 data를 삭제할 때 사용함. URI form으로 삭제하려는 resource를 지정해줘야 함.
2) HTTP Response
1) 200(OK) : request가 성공적으로 수행된 경우. 수행된 후의 resource 값을 알려줌.
2) 301(Moved Permanently): resource가 옮겨졌을 때. 해당 위치의 header field를 알려줌.
3) 400(Bad Request) : server가 client Request를 이해X or 지원 불가능할 때
4) 401(Unauthorized) : client가 resource를 받을 수 있는 권한이 없을 때
5) 403(Forbidden) : client의 접근이 금지되었을 때
6) 404(Page Not Found or File Not Found) : 페이지가 없을 때
7) 500(Internal Server Error) : server가 client Request 처리하다가 에러났을 때
- URL(Uniform Resource Locators)이란?
: Resource의 위치를 명시함.
- Web Client란?
: HTTP request를 서버에 보내고 HTTP response를 받아와서 출력해주는 client로
입력받은 URL을 통해 host와 연결 및 자원을 검색함.
함수 3가지
1) parse_url() : URL을 파싱하는 함수로, 입력받은 URL로부터 hostname, port 번호, document path를 받아옴.
-> 이거 설명 안할거라고 함.
2) send_request() : char형 버퍼를 정의하고 이곳에 HTTP request를 담아서 패킷을 만들어 전송함.
void send_request(SOCKET s, char *hostname, char *port, char *path) {
char buffer[2048]; //buffer에 위의 매개인자들을 HTTP 형식대로 넣어줌.
sprintf(buffer, "GET /%s HTTP/1.1\r\n", path); //GET 메소드를 쓰고 HTTP1.1버젼을 쓴다.
sprintf(buffer + strlen(buffer), "Host: %s:%s\r\n", hostname, port);
sprintf(buffer + strlen(buffer), "Connection: close\r\n");
sprintf(buffer + strlen(buffer), "User-Agent: honpwc web_get 1.0\r\n");
sprintf(buffer + strlen(buffer), "\r\n");
send(s, buffer, strlen(buffer), 0); //버퍼에 저장된 내용을 소켓s를 통해 보냄.
printf("Sent Headers:\n%s", buffer); //어떤 데이터를 보냈는지 확인.
}
3) connect_to_host() : 설정한 URL로 접근하여 hostname과 port 번호로 서버와 커넥션을 맺음.
SOCKET connect_to_host(char *hostname, char *port) {
printf("Configuring remote address...\n");
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
struct addrinfo *peer_address;
if (getaddrinfo(hostname, port, &hints, &peer_address)) { //hostname이랑 port번호로 IP주소 받아옴.
fprintf(stderr, "getaddrinfo() failed. (%d)\n", GETSOCKETERRNO());
exit(1);
}
printf("Remote address is: ");
char address_buffer[100];
char service_buffer[100];
getnameinfo(peer_address->ai_addr, peer_address->ai_addrlen,
address_buffer,
sizeof(address_buffer),
service_buffer,
sizeof(service_buffer),
NI_NUMERICHOST);
printf("%s %s\n", address_buffer, service_buffer); //추가적인 정보를 출력함.
printf("Creating socket...\n");
SOCKET server;
server = socket(peer_address->ai_family, //소켓을 생성.
peer_address->ai_socktype, peer_address->ai_protocol);
if (!ISVALIDSOCKET(server)) {
fprintf(stderr, "socket() failed. (%d)\n", GETSOCKETERRNO());
exit(1);
}
printf("Connecting...\n");
if (connect(server, peer_address->ai_addr, peer_address->ai_addrlen)) { //connect함.
fprintf(stderr, "connect() failed. (%d)\n", GETSOCKETERRNO());
exit(1);
}
freeaddrinfo(peer_address);
printf("Connected.\n\n");
return server;
}
main 함수
int main(int argc, char *argv[]) { //패킷을 보내고 서버로부터 response를 받아옴.
if (argc < 2) {
fprintf(stderr, "usage: web_get url\n"); return 1;
}
char *url = argv[1]; //입력해둔 url을 가져오고
char *hostname, *port, *path;
parse_url(url, &hostname, &port, &path); //url을 parsing해서 hostname,port,path를 입력받고
SOCKET server = connect_to_host(hostname, port); //hostname, port번호로 server 소켓 생성.
send_request(server, hostname, port, path); //server에 HTTP GET request를 전송함
const clock_t start_time = clock(); //시간 측정하는 부분.
#define RESPONSE_SIZE 32768
char response[RESPONSE_SIZE]; //response 배열을 만들고
char *p = response, *q; //response 배열의 처음을 가리키는 p, 구현상 도움을 줄 q
char *end = response + RESPONSE_SIZE; //response 배열의 끝을 가리키는 end
char *body = 0; //받아온 response는 header와 body로 이뤄져 있을 텐데 그 중 body를 가리키는 포인터.
enum {length, chunked, connection};
int encoding = 0;
int remaining = 0;
while(1) {
if ((clock() - start_time) / CLOCKS_PER_SEC > TIMEOUT) { //시간이 5초 넘어가면 서버 에러로 처리.
fprintf(stderr, "timeout after %.2f seconds\n", TIMEOUT); //#define TIMEOUT 5.0
return 1;
}
if (p == end) { //p가 끝에 도달했을 때 종료
fprintf(stderr, "out of buffer space\n");
return 1;
}
fd_set reads;
FD_ZERO(&reads);
FD_SET(server, &reads);
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 200000;
if (select(server+1, &reads, 0, 0, &timeout) < 0) { //select를 통해 패킷을 받을 준비를 함.
fprintf(stderr, "select() failed. (%d)\n", GETSOCKETERRNO());
return 1;
}
if (FD_ISSET(server, &reads)) { //server에 관심있는 읽을거리가 있으면
int bytes_received = recv(server, p, end - p, 0); //recv로 서버 소켓의 data 읽어옴. p에 읽어오는데 end-p만큼 읽어옴. end-p가 response 길이잖아!ㅎㅎ
if (bytes_received < 1) { //1보다 작다 = 서버로부터 close 요청이 온 것임.
if (encoding == connection && body) { //encoding==1.length 2.chunked 3.close(connection), body가 있으면 body를 출력함.
printf("%.*s", (int)(end - body), body);
}
printf("\nConnection closed by peer.\n");
break;
}
p += bytes_received; //읽은만큼 p를 증가시킴
*p = 0; //string 관련 함수를 쓸 수 있게끔 0을 넣어줌.
//body를 처리하는 부분.
if (!body && (body = strstr(response, "\r\n\r\n"))) { //response에서 \r\n\r\n로시작하는 문자열 검색 후 시작점을 포인터로 반환.
//이때 \r\n\r\n은 header와 body를 나누는 경계임. 즉, response = header + \r\n\r\n + body
*body = 0; //나중에 header 출력하려고 끝부분을 null로 표시해두는 것임.
body += 4; //\r\n\r\n을 건너뛰려고 +4 하는 것.
printf("Received Headers:\n%s\n", response);
q = strstr(response, "\nContent-Length: "); //response 패킷의 총 길이를 q에 가져옴.
if (q) {
encoding = length;
q = strchr(q, ' '); q에 ' '이 있는지 검사.
q += 1; //length 숫자의 맨 앞을 가리킴
remaining = strtol(q, 0, 10); //10진수로 읽어오고 body의 길이(즉,Content-Remaining값)가 remaining에 저장됨. 저장할 포인터는 관계없으므로 0을 씀.
} else {
q = strstr(response, "\nTransfer-Encoding: chunked"); //response에 "\n~~~"가 존재하는지 확인하는 함수.
if (q) { //chunked여부 확인.
encoding = chunked;
remaining = 0; //받으면서 길이를 검사해야 하므로 계산할 수 없어서 0으로 세팅.
} else { //이것도 아니면 connection(close)로 처리.
encoding = connection;
}
}
printf("\nReceived Body:\n");
}
if (body) {
if (encoding == length) {
if (p - body >= remaining) { //p-body가 body길이보다 크거나 같다 = 그만가! 멈춰~!!!
printf("%.*s", remaining, body);
break;
}
} else if (encoding == chunked) {
do {
if (remaining == 0) {
if ((q = strstr(body, "\r\n"))) {
remaining = strtol(body, 0, 16);
if (!remaining) goto finish;
body = q + 2; // \r\n고려
} else {
break;
}
}
if (remaining && p - body >= remaining) {
printf("%.*s", remaining, body);
body += remaining +2; //\r\n 고려
remaining = 0;
}
} while (!remaining);
}
} //if (body)
} //if FDSET
} //end while(1)
finish:
- HTTP POST request
: data를 client에서 server로 보냄.(web client에서 생성한 data를 web server로 전송하는 역할)
- 포맷은 Content-Type Header에 의해 정의되고 매우 다양함.
- 웹사이트에 form 형태(ex)로그인 폼)가 있을 때 POST를 많이 씀.
'대학 전공 공부 > 네트워크 프로그래밍' 카테고리의 다른 글
16. Basic OpenSSL (0) | 2022.06.08 |
---|---|
15. 네트워크 보안 (0) | 2022.06.08 |
14강. 보안 기본 개념 (0) | 2022.05.21 |
12강. DNS (0) | 2022.05.13 |
11강. Error, IPv6 and Packet Socket (0) | 2022.05.12 |