반응형
HTTP 통신
HTTP Request
형식
- HTTP 통신 전용 포트번호
80
- 요청 라인이 가장 중요
- 한 줄이 끝나면,
cr
lf
로 줄을 바꿔주어야 함. System.lineSeparator()
으로 줄바꿈- Header 부분이 끝나면 줄바꿈을 한번 더하여 header가 끝났음을 나타냄
- HTTP method 중 get, head는 개체 몸체가 없음
Client
// ip주소와 포트는 고정
static final String IPADDRESS = "127.0.0.1";
static final int PORTNUM = 80;
static final String requestFormat = "%s / HTTP/1.1" + System.lineSeparator() +
"Host :" + IPADDRESS + System.lineSeparator() +
System.lineSeparator();
//서버로 HTTP Request문 전송
//String.format을 사용하여 requestFormat안에 요청을 넣어줌
//Scanner를 통해 요청을 받음
printWriter.println(String.format(requestFormat, request));
HTTP Response
형식
상태 라인이 응답에서 가장 중요
각 줄이 끝나면 줄바꿈을 해주어야 함
Header 부분이 끝나면 줄바꿈을 한번 더하여 header가 끝났음을 나타냄
Head method의 경우 개체 몸체가 없음.
static final String resposeFormat = "HTTP/1.1 200 OK" + System.lineSeparator() + "Date: "+ new Date().toString() +System.lineSeparator() + System.lineSeparator(); printWriter.println(responseFormat);
- HTTP 프로토콜 형식에 맞게 메시지를 보내니 WireShark에서 HTTP 통신으로 인식한다
HTTP Request method
Method | 역할 |
---|---|
CONNECT | 요청한 리소스에 대해 양방향 연결을 시작하는 메소드 |
DELETE | 지정한 리소스를 삭제 |
GET | 특정한 리소스를 가져오도록 요청 |
HEAD | GET 메서드로 요청했을 때 돌아올 헤더를 요청 |
OPTIONS | 목표 리소스와의 통신 옵션을 설명하기 위해 사용 |
PATCH | 리소스의 부분적인 수정을 할 때에 사용 |
POST | 서버로 데이터를 전송 |
PUT | 요청 페이로드를 사용해 새로운 리소스를 생성하거나, 대상 리소스를 나타내는 데이터를 대체 |
- 참고 : https://developer.mozilla.org/ko/docs/Web/HTTP/Methods
- 현재 코드에서는 GET, HEAD, POST, PUT만 사용
HTTP Response code, message
code | message | 역할 |
---|---|---|
200 | OK | 요청이 성공했음을 나타냄 |
201 | Created | 요청이 성공적으로 처리되었으며, 자원이 생성되었음을 나타냄 |
204 | No Content | 요청이 성공했으나 클라이언트가 현재 페이지에서 벗어나지 않아도 된다는 것을 나타냄 |
400 | Bad Request | 서버가 클라이언트 오류를 감지해 요청을 처리할 수 없거나, 하지 않는 것을 나타냄 |
404 | Not Found | 서버가 요청받은 리소스를 찾을 수 없다는 것을 의미 |
505 | HTTP Version Not Supported | 서버에서 해당 Http 버전을 지원하지 않을 때 |
- 참고 : https://developer.mozilla.org/ko/docs/Web/HTTP/Status
- 위의 6개의 상태코드 사용
- Created, No Content 응답의 경우 개체 몸체 X
- Custom 해서 response code, message 구현
구현 내용
HTTP Request
- Method GET, HEAD, POST, PUT만 사용
- 연결 완료 후 Scanner를 통해 사용자가 원하는 method를 받음
- 위의 4개 이외의 값이 들어올 경우 응답으로
400 Bad Request
- 위의 4개 이외의 값이 들어올 경우 응답으로
- POST, PUT의 경우 추가로 data를 입력받음
- POST : 데이터 추가
- PUT : 데이터 대체
HTTP Response Case
Method | Code | 특징 |
---|---|---|
" " | 505 | HTTP 1.1이 아니면 발생 |
" " | 400 | GET, HEAD, POST, PUT 이외의 Method 입력시 발생 |
HEAD | 404 | 서버에 데이터 X, Header부분만 전송 |
HEAD | 200 | 서버에 데이터 O, Header부분만 전송 |
POST | 201 | 서버에 데이터 X, 데이터 생성 개체몸체 없음 |
POST | 200 | 서버에 데이터 O, 데이터 생성 데이터를 개체몸체로 보내줌. 기존의 데이터 + 추가데이터 |
PUT | 201 | 서버에 데이터 X, 데이터 생성 개체몸체 없음 |
PUT | 204 | 서버에 데이터 O, 데이터 생성 개체몸체 없음. 새 데이터로 교체 |
GET | 404 | 서버에 데이터 X, 개체몸체로 추가 안내메시지 전송 |
GET | 200 | 서버에 데이터 O, 개체몸체로 데이터 전송 |
- PUT 메소드의 경우 멱등성(동일한 요청을 여러번 연속으로 보내도 한번 보내는 것과 같은 효과를 지님)을 지님
- 따라서 데이터가 완전 대체되게 구현
- POST 메소드의 경우 멱등성이 없기 때문에 기존의 데이터에 데이터가 추가되는 형태로 구현
- 400 Bad Request의 경우 클라이언트로부터 잘못된 HTTP Method를 전달 받았을 때 발생
- 404 Not Found는 서버에 전역변수 데이터가 없을 시 일어나게 구현
HTTP Response Header
- Date
- Content-length (개체 몸체가 없을 때 추가되지 않음)
- Content-type (개체 몸체가 없을 때 추가되지 않음)
Client
client.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class client {
static final String IPADDRESS = "127.0.0.1";
static final int PORTNUM = 80;
static final String requestFormat = "%s / HTTP/1.1" +System.lineSeparator()+
"Host :"+IPADDRESS+System.lineSeparator() +
System.lineSeparator();
public static void main(String[] args) throws IOException {
try(Socket socket = new Socket(IPADDRESS, PORTNUM)) {
System.out.println("서버연결완료");
PrintWriter printWriter = new PrintWriter(socket.getOutputStream());
Scanner scanner = new Scanner(System.in);
//사용자 요청받기
System.out.println("요청을 입력하시오. GET/HEAD/POST/PUT");
String request = scanner.next().toUpperCase();
//요청 구별해 서버에 전송
switch (request) {
case "POST":
case "PUT":
// Post, put 요청시 사용자가 원하는 데이터 받기
System.out.println("data를 입력하시오");
String dataRequest = scanner.next();
printWriter.println(String.format(requestFormat, request) + dataRequest);
break;
default:
printWriter.println(String.format(requestFormat, request));
}
printWriter.flush(); //flush를 해야 데이터가 전송됨.
//응답
InputStreamReader inputStreamReader = new InputStreamReader(socket.getInputStream());
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
//헤더부분 읽기
while (true) {
String str = bufferedReader.readLine();
if (str.isEmpty()) {
break;
}
System.out.println(str);
}
//Data 부분이 있다면 읽기 (\r\n\r\n 때문에 data부분이 한번에 안읽힘.
String str= bufferedReader.readLine();
if(str != null){
System.out.println(str);
}
}
}
}
bufferReader.readLine()
이 줄바꿈이 두번 연속 있을 시 개체 몸체를 인식 못해서 추가로 출력하게 코드를 작성System.lineSeparator()
를 사용해 운영체제 상관없이 줄바꿈
Server
server.java
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class server {
public static void main(String[] args) throws IOException {
//서버 오픈
try(ServerSocket serverSocket = new ServerSocket(80)){
while (true){
//클라이언트 연결
try(Socket socket = serverSocket.accept()){
System.out.println("client connected");
//Client에서 메시지 받아옴
InputStreamReader inputStreamReader = new InputStreamReader(socket.getInputStream());
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
// 전송 메시지 담아 처리할 배열
ArrayList<String> dataList = new ArrayList<>();
//Header 부분 읽기,배열에 담기
System.out.println("받은 메시지----------------");
try{
while (true) {
String str = bufferedReader.readLine();
dataList.add(str);
if (str.isEmpty()) {
break;
}
System.out.println(str);
}
//Data 부분 읽기, 배열에 담기 (\r\n\r\n 때문에 data부분이 한번에 안읽힘).
if(dataList.get(0).contains("POST") || dataList.get(0).contains("PUT")){
String str = bufferedReader.readLine();
System.out.println(str);
dataList.add(str);
}
}catch (Exception e){
//소켓 연결은 되었지만, 클라이언트가 메시지 전송을 하지 않고 접속이 비정상적으로 끝났을때.
System.out.println("비정상적인 접속종료");
continue;
}
//요청 분석 후 메시지 생성
RequestHttp header = new RequestHttp(dataList);
PrintWriter printWriter = new PrintWriter(socket.getOutputStream());
printWriter.println(header.getMessage());
System.out.println("보낸 메시지:-----------");
System.out.println(header.getMessage());
//전송
printWriter.flush();
}
}
}catch (IOException e){
e.printStackTrace();
}
}
}
- Client로 부터 전송된 HTTP Request를 한줄씩 terminal에 띄워주며
ArrayList
에 저장 - ArrayList를
RequestHttp
class에 넘겨 각종 데이터를 처리 - 예외처리를 통해 비정상적으로 client의 접속이 끊겼을 때 서버가 종료되지 않게 처리.
RequestHttp.java
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
public class RequestHttp {
private ArrayList<String> dataList;
final private static String[] METHOD_LIST= {"GET", "POST", "PUT", "HEAD"};
private String method;
private String url;
private String HTTPversion;
private String date;
private String contentLength;
private String contentType;
private String content;
private String stateCode;
private String stateMessage;
//전역 변수로 저장해 서버가 실행되는 동안 유지됨
private static String data;
String message;
public RequestHttp(ArrayList<String> dataList){
this.dataList = dataList;
//요청 라인 나누기, 필요한 데이터 추출
String[] requestLine = dataList.get(0).split(" ");
this.method = requestLine[0];
this.url = requestLine[1];
this.HTTPversion = requestLine[2].trim(); //끝 개행문자 제거
this.date = new Date().toString();
doRequest();
}
// 요청라인 바탕으로 response메시지에 담길 요소 생성
private void doRequest(){
//버전 불일치
if (!HTTPversion.equals("HTTP/1.1")){
stateCode = "505";
stateMessage = "HTTP Version Not Supported";
setContent("HTTP Version Not Supported");
return;
}
//잘못된 method가 왔을때.=> 400 BAD Request.
if (!Arrays.asList(METHOD_LIST).contains(method)){
stateCode = "400";
stateMessage = "BAD REQUEST";
setContent("bad request. check your request");
return;
}
//head : 응답본문 없음
if(method.equals("HEAD")){
if(data == null){
//Data 없을때
stateCode = "404";
stateMessage = "Not Found";
return;
}else{
//data 있을 때.
stateCode = "200";
stateMessage = "OK";
setContent(data);
return;
}
}
if (method.equals("POST")) {
if (data == null) {
//데이터 추가. 응답 본문 x
stateCode = "201";
stateMessage = "Created";
data = dataList.get(dataList.size() - 1);
return;
} else {
//데이터 수정(기존+새 데이터), 응답 본문 o
stateCode = "200";
stateMessage = "OK";
data += dataList.get(dataList.size() - 1);
setContent("post done : "+data);
}
}
if (method.equals("PUT")){
//put은 멱등성을 가짐 =>한번을 보내도 여러번을 연속으로 보내도 같은 효과를 지님
if(data == null){
//데이터 생성. 응답 본문 x
stateCode = "201";
stateMessage = "Created";
data = dataList.get(dataList.size()-1);
return;
}else{
//데이터 수정(새 데이터)
//응답 본문 x
stateCode = "204";
stateMessage = "No content";
data = dataList.get(dataList.size()-1);
return;
}
}
if (method.equals("GET")){
//전역변수 data 없을 때 안내 메시지 생성
if(data == null){
stateCode = "404";
stateMessage = "Not Found";
setContent("Not found please post or put first");
return;
}else{
stateCode = "200";
stateMessage = "OK";
setContent(data);
return;
}
}
}
private void setMessage(){
message = HTTPversion+" "+ stateCode+ " "+ stateMessage +System.lineSeparator()+
"Date: "+date+ System.lineSeparator();
// 개체 몸체 없을때는 불필요한 헤더 전송안함.
if (content != null){
message +="Content-length: "+contentLength + System.lineSeparator()+
"Content-type: "+contentType+System.lineSeparator();
}
message += System.lineSeparator();
//head 메소드, 응답코드 201, 204 일 경우 entity body 없음.
if (!method.equals("HEAD") && !stateCode.equals("201")&& !stateCode.equals("204")){
message +=content;
}
}
//개체몸체 및 헤더 설정
public void setContent(String content){
this.content = content;
this.contentLength = String.valueOf(content.getBytes(StandardCharsets.UTF_8).length);
this.contentType = "Text";
}
public String getMessage(){
setMessage();
return message;
}
}
- Client에서 전송된 메시지를 기반으로 동작
- 요청라인과 개체몸체 사용
- 헤더라인들은
dataList
안에 있음
- 헤더라인들은
Http/1.1
이외의 버전 사용불가- 505 HTTP Version Not Supported 발생
실행결과
data 없을때 GET, HEAD
- 404 not found 응답
PUT
- 사용자가 입력한 데이터가 전역변수에 저장
- 저장 후 GET 입력시 데이터 받아옴
- 데이터가 있을 때 PUT 입력시 데이터 대체
POST
- 사용자가 입력한 데이터가 전역변수에 저장
- 데이터 있을 때 Post시 데이터 뒤에 추가
400 Bad Request
- 잘못된 method 입력 시 400 bad Request
비정상적 client 종료
- 예외처리를 통해 client가 비정상적으로 종료되어도 서버에 문제 없음.
반응형
'컴퓨터네트워크' 카테고리의 다른 글
컴퓨터 네트워크 5-1 링크 계층 (0) | 2023.01.20 |
---|---|
컴퓨터 네트워크 4-2 네트워크 계층 (0) | 2023.01.17 |
[JAVA] server, client 프로그램 - 소켓통신 (0) | 2023.01.08 |
컴퓨터 네트워크 3-1 트랜스포트 계층 (1) | 2023.01.01 |
컴퓨터 네트워크 2-2 - 애플리케이션 계층 (0) | 2022.12.30 |