1. 먼저 http의 메시지 구조를 살펴보자.
1. 요청 메시지
HTTP 요청 메시지는 요청 라인, 요청 헤더, 빈 라인, 요청 데이터의 네 부분으로 구성됩니다. 다음 그림은 요청 메시지의 일반적인 형식을 보여줍니다.
(1) 요청 라인
요청 라인은 공백으로 구분된 요청 방법 필드, URL 필드, HTTP 프로토콜 버전 필드의 세 가지 필드로 구성됩니다. 예를 들어 GET /index.html HTTP/1.1입니다.
HTTP 프로토콜의 요청 메소드에는 GET, POST, HEAD, PUT, DELETE, OPTIONS, TRACE 및 CONNECT가 포함됩니다. 여기서는 가장 일반적으로 사용되는 GET 방식과 POST 방식을 소개합니다.
GET: GET 메소드는 클라이언트가 서버에서 문서를 읽으려고 할 때 사용됩니다. GET 메소드를 사용하려면 서버가 응답 메시지의 데이터 부분에 URL로 찾은 리소스를 넣고 이를 클라이언트로 다시 보내야 합니다. GET 메소드를 사용하는 경우 요청 매개변수와 해당 값이 URL에 추가됩니다. 물음표("?")는 URL의 끝과 전달된 요청 매개변수의 시작을 나타내는 데 사용됩니다. 매개변수가 제한되어 있습니다. 예를 들어 /index.jsp?id=100&op=bind입니다.
POST: 클라이언트가 서버에 많은 정보를 제공할 때 POST 방식을 사용할 수 있다. POST 메소드는 HTTP 요청 데이터에 요청 매개변수를 캡슐화하여 이름/값의 형태로 나타나며 대량의 데이터를 전송할 수 있습니다.
(2)요청 헤더
요청 헤더는 한 줄에 한 쌍씩 키워드/값 쌍으로 구성되며, 키워드와 값은 영어 콜론 ":"으로 구분됩니다. 요청 헤더는 클라이언트의 요청에 대해 서버에 알립니다. 일반적인 요청 헤더는 다음과 같습니다.
User-Agent: 요청을 생성한 브라우저 유형입니다.
승인: 클라이언트가 인식하는 콘텐츠 유형 목록입니다.
호스트: 요청된 호스트 이름으로, 여러 도메인 이름이 동일한 IP 주소, 즉 가상 호스트에 있을 수 있습니다.
(3) 빈 줄
마지막 요청 헤더 뒤에는 빈 줄이 오고, 캐리지 리턴 및 줄 바꿈 문자는 서버에 더 이상 따라야 할 요청 헤더가 없음을 알리기 위해 전송됩니다.
(4)요청 데이터
요청 데이터는 GET 방식이 아닌 POST 방식으로 사용됩니다. POST 방법은 고객이 양식을 작성해야 하는 상황에 적합합니다. 요청 데이터와 관련하여 가장 일반적으로 사용되는 요청 헤더는 Content-Type 및 Content-Length입니다.
2. 응답 메시지
응답 메시지의 형식은 첫 번째 줄이 다르다는 점을 제외하면 일반적으로 요청 메시지와 유사합니다. 독자는 인터넷에서 이 측면에 대한 소개를 찾을 수 있으며 여기에서는 자세히 설명하지 않습니다.
2. 프로그램 실시
프로그램을 구현하는 단계는 다음과 같습니다.
1. 클라이언트 브라우저로부터 요청을 수신합니다.
2. 요청을 처리할 새 스레드를 생성합니다.
3. 메시지 데이터를 읽고, 메시지가 올바른지 확인하고, 메시지 내용을 분석합니다.
4. 응답 메시지를 작성하여 클라이언트에 보냅니다.
5. 처리 스레드를 종료하고 다른 고객 요청을 처리합니다.
프로그램 코드는 다음과 같습니다.
일반 사본을 클립보드 인쇄로 보시겠습니까?
java.net.* 가져오기;
import java.io.*;
import java.util.*;
import java.lang.*;
공개 클래스 WebServer {
공개 정적 무효 메인(문자열 [] 인수){
int 포트;
ServerSocket server_socket;
노력하다{
port=Integer.parseInt(args[0]);
}
잡기 (예외 e){
포트=8080;
}
노력하다{
server_socket=새 ServerSocket(포트);
System.out.println("포트에서 실행 중인 웹서버"+server_socket.getLocalPort());
동안(참){
소켓 소켓=server_socket.accept();
System.out.println("새 연결이 허용되었습니다."+socket.getInetAddress()+":"+socket.getPort());
//요청을 처리하기 위해 특정 요청에 대한 스레드를 생성합니다.
노력하다{
httpRequestHandler 요청=새로운 httpRequestHandler(소켓);
스레드 스레드=새 스레드(요청);
thread.start();
}
catch(예외 e){
System.out.println(e);
}
}
}
catch(IOException e){
System.out.println(e);
}
}
}
//요청 처리를 위한 스레드 클래스
httpRequestHandler 클래스는 Runnable을 구현합니다.{
최종 정적 문자열 CRLF="rn";
소켓 소켓;
InputStream 입력;
OutputStream 출력;
BufferedReader br;
//요청한 파일 형식이 올바른지 확인합니다.
부울 파일 유형=true;
//초기화 매개변수
공개 httpRequestHandler(소켓 소켓)에서 예외 발생{
this.socket=소켓;
this.input=socket.getInputStream();
this.output=socket.getOutputStream();
this.br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
//스레드 시작
공개 무효 실행(){
노력하다{
프로세스요청();
}
catch(예외 e){
System.out.println(e);
}
}
//요청 처리를 위한 핵심 함수
private void processRequest()가 예외를 발생시킵니다.{
동안(참){
문자열 headerLine=br.readLine();
System.out.println("클라이언트 요청은"+headerLine);
if(headerLine.equals(CRLF)||headerLine.equals(""))
부서지다;
StringTokenizer s=new StringTokenizer(headerLine);
문자열 임시=s.nextToken();
if(temp.equals("GET")){
문자열 파일명=s.nextToken();
파일이름="."+파일이름;
FileInputStream fis=null;
부울 fileExists=true;
if(!(fileName.endsWith(".htm")||fileName.endsWith(".html")))
{
this.fileType=false;
노력하다{
fis=new FileInputStream("error.html");
}
catch(FileNotFoundException e){
파일존재=false;
}
}
또 다른{
노력하다{
fis=new FileInputStream(파일이름);
}
catch(FileNotFoundException e){
파일존재=false;
}
}
String serverLine="서버:간단한 Java WebServer";
문자열 statusLine=null;
문자열 contentTypeLine=null;
문자열 엔터티Body=null;
문자열 contentLengthLine="오류";
if(fileExists&&this.fileType){
statusLine="HTTP/1.0 200 확인"+CRLF;
contentTypeLine="콘텐츠 유형:"+this.contentType(파일 이름)+CRLF;
contentLengthLine="콘텐츠 길이:"+(new Integer(fis.available())).toString()+CRLF;
}
또 다른{
if(fileExists&&this.fileType==false){
statusLine="HTTP/1.0 400 BadRequest"+CRLF;
contentTypeLine="텍스트/html";
엔터티Body="<HTML>400 나쁘지 않은 요청</TITLE></HEAD>"+
"<BODY>400 잘못된 요청"+
"<br>사용:http://yourHostName:port/"+
"파일이름.html</BODY></HTML>";
}
else if(fileExists==false){
statusLine="HTTP/1.0 404 찾을 수 없음"+CRLF;
contentTypeLine="텍스트/html";
엔터티Body="<HTML>404 찾을 수 없음</TITLE></HEAD>"+
"<BODY>404 찾을 수 없음"+
"<br>사용:http://yourHostName:port/"+
"파일이름.html</BODY></HTML>";
}
}
output.write(statusLine.getBytes());
output.write(serverLine.getBytes());
output.write(contentTypeLine.getBytes());
output.write(contentLengthLine.getBytes());
출력.쓰기(CRLF.getBytes());
if(fileExists&&this.fileType){
sendBytes(fis,output);
fis.close();
}
또 다른{
output.write(entityBody.getBytes());
}
}
}
노력하다{
출력.닫기();
br.닫기();
소켓.닫기();
}
catch(예외 e){}
}
//클라이언트가 요청한 페이지 보내기
private static void sendBytes(FileInputStream fis,OutputStream os)가 예외를 발생시킵니다.{
바이트[] 버퍼=새 바이트[1024];
int 바이트=0;
while((bytes=fis.read(buffer))!=-1){
os.write(버퍼,0,바이트);
}
}
//contentType의 내용을 설정합니다.
개인 정적 문자열 콘텐츠 유형(문자열 파일 이름){
if(fileName.endsWith(".htm")||fileName.endsWith(".html")){
"텍스트/html"을 반환합니다.
}
"파일 이름"을 반환합니다.
}