이전 기사에서는 일부 정적 리소스 요청 만 처리 할 수있는 간단한 Java 웹 서버 구현을 작성했습니다. 이 기사에서 구현 된 서블릿 컨테이너는 이전 서버를 기반으로 약간 수정되어 서블릿 요청 처리를 추가했습니다.
프로그램 실행 단계
1. 서버 소켓 객체를 만듭니다.
2. 서버 소켓 객체의 수락 메소드를 호출하고 연결을 기다립니다. 연결이 성공하면 소켓 객체가 반환됩니다. 그렇지 않으면 차단되고 기다립니다.
3. 소켓 객체에서 입력 스트림 및 출력 스트림 바이트 스트림을 가져 오고이 두 스트림은 각각 요청 요청 및 응답 응답에 해당합니다.
4. 요청 처리 : 입력 스트림 바이트 스트림 정보를 읽고 문자열 양식으로 변환 한 다음 구문 분석하십시오. 여기서 구문 분석은 비교적 간단하며 URI (균일 자원 식별자) 정보 만 얻습니다.
5. 응답 처리 (두 가지 유형의 정적 리소스 요청 응답 또는 서블릿 요청 응답) : 구문 분석 된 URI 정보를 기반으로 정적 리소스 요청 인 경우 web_Root 디렉토리에서 요청 된 리소스 리소스 파일을 찾아서 리소스 파일을 읽고 출력 스트림 바이트 스트림에 쓰십시오. 서블릿 요청 인 경우 먼저 UrlClassLoader 클래스 로더를 생성하고 요청 된 서블릿 클래스를로드하고 서블릿 객체를 작성하고 서비스 메소드를 실행합니다 (응답 데이터를 outputStream에 쓰기).
6. 소켓 객체를 닫습니다.
7. 2 단계로 이동하여 연결 요청을 계속 기다리십시오.
코드 구현 :
종속성 추가 :
<!-https://mvnrepository.com/artifact/javax.servlet/servlet-api-> <groupid> javax.servlet </groupid> <artifactid> servlet-api </artifactid> </version> </fectionement>
서버 코드 :
패키지 ex02.pyrmont.first; import java.net.socket; import java.net.serversocket; import java.net.inetaddress; import java.io.inputstream; import java.io.outputstream; import java.io.ioexception; import ex02.pyrmont.request; ex02.pyrmont.rsponge; ex02.pyrmont.staticResourceProcessor; public class httpserver1 {// service command private static final string_command = "/shutdown"; public static void main (String [] args) {httpserver1 server = new httpserver1 (); // 연결 요청 Server.await ()을 기다리고 있습니다. } public void await () {serversocket serversocket = null; int port = 8080; {// Server Socket Object Serversocket = New Serversocket (포트, 1, inetAddress.getByName ( "127.0.0.1")); } catch (ioexception e) {e.printstacktrace (); System.exit (1); } // (true) {소켓 소켓 = null; inputStream input = null; 출력 스트림 출력 = NULL; 시도 {// 연결을 기다리십시오. 연결이 성공한 후 소켓 객체 소켓 = serversocket.accrect ()를 반환합니다. 입력 = socket.getInputStream (); output = socket.getoutPutStream (); // 요청 객체 및 구문 분석 요청 요청 = 새 요청 (입력); request.parse (); // 종료 서비스 명령인지 확인 if (request.getUri (). Equals (shutdown_command)) {break; } // 응답 생성 객체 응답 = 새 응답 (출력); Response.SetRequest (요청); if (request.geturi (). startStwith ( "/servlet/")) {// request uri는/servlet/로 시작하여 servlet request servletprocessor1 processor = new servletprocessor1 ()을 나타냅니다. 프로세서 프로세서 (요청, 응답); } else {// static resourceSourceProcessor processor = new staticResourceProcessor (); 프로세서 프로세서 (요청, 응답); } // socket.close ()를 닫습니다. } catch (예외 e) {e.printstacktrace (); System.exit (1); }}}}상수 수업 :
패키지 ex02.pyrmont; import java.io.file; public class 상수 {public static final String web_root = system.getProperty ( "user.dir") + file.separator + "webroot"; public static final String web_servlet_root = system.getProperty ( "user.dir") + file.separator + "target" + file.separator + "class"; }요구:
패키지 ex02.pyrmont; import java.io.inputStream; import java.io.ioexception; import java.io.bufferedReader; import java.io.unsupportedencodingException; import java.util.enumeration; import java.util.locale; import java.uttdip; import javax; javax.servlet.servletinputstream; import javax.servlet.servletrequest; 공개 클래스 요청 구현 servletrequest {private inputstream input; 개인 문자열 URI; 공개 요청 (inputStream input) {this.input = 입력; } public String getUri () {return uri; } /** * * requestString의 형태는 다음과 같습니다. * get /index.html http /1.1 * 호스트 : 로컬 호스트 : 8080 * 연결 : Keep-alive * 캐시-콘트롤 : max-age = 0 * ... *이 함수의 목적은 /index.html 문자열 * /private string parseuri (string requestring) {int indely 2,; index1 = requestString.indexof ( ''); if (index1! = -1) {index2 = requestString.indexof ( '', index1 + 1); if (index2> index1) return returestring.substring (index1 + 1, index2); } return null; } // 소켓에서 문자 세트를 읽습니다. StringBuffer 요청 = New StringBuffer (2048); int i; 바이트 [] 버퍼 = 새로운 바이트 [2048]; try {i = input.read (버퍼); } catch (ioexception e) {e.printstacktrace (); i = -1; } for (int j = 0; j <i; j ++) {request.append ((char) buffer [j]); } system.out.print (request.toString ()); uri = parseuri (request.toString ()); } / * servletrequest의 구현 * / public object getAttribute (String attribute) {return null; } 공개 열거 <?> getAttributeNames () {return null; } public String getRealPath (문자열 경로) {return null; } public requestDispatcher getRequestDispatcher (문자열 경로) {return null; } public boolean issecure () {return false; } public String getCharacterEncoding () {return null; } public int getContentLength () {return 0; } public String getContentType () {return null; } public servletInputStream getInputStream ()은 ioException {return null; } public locale getLocale () {return null; } 공개 열거 <?> getLocales () {return null; } public String getParameter (문자열 이름) {return null; } public map <?,?> getParameterMap () {return null; } 공개 열거 <?> getParameterNames () {return null; } public String [] getParametErvalues (문자열 매개 변수) {return null; } public String getProtocol () {return null; } public bufferedReader getReader ()가 IoException {return null; } public String getRemoteaddr () {return null; } public String getRemoteHost () {return null; } public String getScheme () {return null; } public String getServerName () {return null; } public int getServerport () {return 0; } public void removeattribute (문자열 속성) {} public void setattribute (문자열 키, 객체 값) {} public void setcharacterencoding (String encoding)은 UnsupportedEncodingException {}}을 던졌습니다.응답:
패키지 ex02.pyrmont; import java.io.outputStream; import java.io.ioexception; import java.io.fileinputstream; import java.io.filenotfoundException; import java.io.file; import java.io.printwriter; import java.util.locale; javax.servletrpronge; javax.servlet.servletoutputStream; 공개 클래스 응답 구현 ServletResponse {private static final int buffer_size = 1024; 요청 요청; 출력 스트림 출력; 인쇄기 작가; 공개 응답 (OutputStream 출력) {this.output = 출력; } public void setRequest (요청 요청) {this.request = request; } // outputStream byte 스트림에 웹 파일을 쓰기 public void sendstaticResource ()는 ioexception {byte [] bytes = new Byte [buffer_size]; fileInputStream fis = null; try { / * request.geturi는 request.getRequesturi * / file file = 새 파일 (constants.web_root, request.geturi ())로 대체되었습니다. fis = new FileInputStream (파일); / * * http response = 상태 라인 ((일반 헤더 | 응답 헤드 | * 엔티티 헤더) CRLF) CRLF [메시지-바디] 상태-라인 = * http-version sp status-code sp rase-phrase crlf */ int ch = fis.read (bytes, 0, buffer_size); while (ch! = -1) {output.write (bytes, 0, ch); ch = fis.read (바이트, 0, buffer_size); }} catch (filenotfoundException e) {String errormessage = "http/1.1 404 파일 찾기 없음/r/n" + "content-type : text/html/r/n" + "내용-길이 : 23/r/n" + "/r/n" + "<h1> 파일을 찾지 못했습니다 </h1>"; output.write (errormessage.getBytes ()); } 마침내 {if (fis! = null) fis.close (); }} / ** servletResponse의 구현* / public void FlushBuffer ()는 ioException {} public int getBuffersize () {return 0; } public String getCharacterEncoding () {return null; } public locale getLocale () {return null; } public servletoutputStream getOutputStream ()가 IoException {return null; } public printwriter getwriter ()는 ioexception {// autoflush가 true, println ()은 flush, //하지만 print ()는 그렇지 않습니다. Writer = New Printwriter (output, true); 귀환 작가; } public boolean iscommitted () {return false; } public void reset () {} public void resetbuffer () {} public void setbuffersize (int size) {} public void setContentLength (int length) {} public void setContentType (스트링 유형) {} public void setlocale (Locale locale) {}}정적 리소스 요청 처리 :
패키지 ex02.pyrmont; import java.io.ioexception; public class staticResourceProcessor {public void process (요청, 응답 응답) {try {response.sendStaticResource (); } catch (ioexception e) {e.printstacktrace (); }}}서블릿 요청 처리 :
패키지 ex02.pyrmont.first; import java.net.url; import java.net.urlclassloader; import java.net.urlstreamhandler; import java.io.ioexception; import javax.servlet.servlet; import javax.servlet.servletrequest; import javax.servlet ex02.pyrmont.constants; import ex02.pyrmont.request; import ex02.pyrmont.response; public class servletprocessor1 {public void process (요청 요청, 응답 응답) {String uri = request.geturi (); 문자열 servletname = uri.substring (uri.lastindexof ( "/") + 1); // ClassLoader, 지정된 JAR 파일 또는 디렉토리 로더에서 클래스 UrlClassLoader를로드하는 데 사용됩니다. = null; {urlstreamhandler streamhandler = null; // 클래스 로더 로더 생성 = new URLClassLoader (new URL [] {new url (null,”파일 : " + constants.web_servlet_root, Streamhandler)); } catch (ioexception e) {System.out.println (e.toString ()); } class <?> myclass = null; {// 해당 서블릿 클래스 MyClass = loader.loadClass (servletname)를로드합니다. } catch (classNotFoundException e) {System.out.println (e.toString ()); } servlet servlet = null; try {// servlet instance servlet = (servlet) myclass.newinstance (); // ervlet servlet.service ((servletrequest) request, (servletResponse) 응답의 서비스 메소드를 실행합니다. } catch (예외 e) {system.out.println (e.toString ()); } catch (Throwable e) {System.out.println (e.toString ()); }}}서블릿 클래스 :
import javax.servlet.*; import java.io.ioexception; import java.io.printwriter; public class primitiveServlet empliced servlet {public void init (servletconfig config) servletexception {system.out.println ( "int"); } public void Service (ServletRequest 요청, ServletRepponse 응답) servletexception, ioexception {System.out.println ( "Service"); printwriter out = response.getwriter (); out.println ( "안녕하세요. 장미는 빨간색입니다."); out.print ( "바이올렛은 파란색입니다."); } public void destroy () {System.out.println ( "Destroy"); } public String getServletInfo () {return null; } public servletconfig getServletConfig () {return null; }} 결과 테스트 :
정적 리소스 요청 :
Servlet Request (브라우저로 플러시되는 첫 번째 문자열이기 때문에 두 번째 문자열 제비꽃이 파란색 인 것을 볼 수 없습니다. 나중에 컨테이너를 개선 할 수 없습니다) :
개선하다
이전에 구현 된 서블릿 컨테이너에는 심각한 문제가 있습니다. Servlet에서 사용자는 ServletRequest 및 ServletREpponse를 요청 및 응답 유형으로 직접 변환하고 내부 공개 방법을 직접 호출 할 수 있습니다. 이것은 나쁜 디자인입니다. 개선 방법은 외관 클래스를 요청 및 응답을 추가하여 사용자가 모양 클래스에 정의 된 공개 메소드에만 액세스 할 수 있도록하는 것입니다.
외관 클래스를 요청하십시오
패키지 ex02.pyrmont.second; import java.io.ioexception; import java.io.bufferedReader; import java.io.unsupportedEncodingException; import java.util.enumeration; import java.util.locale; import java.util.map; import javax.servlet.revercepcher; javax.servlet.servletinputstream; import javax.servlet.servletrequest; import ex02.pyrmont.request; public class requestfacade emplices servletrequest {private servletrequest request = null; public requestfacade (요청 요청) {this.request = request; } / * servletRequest의 구현 * / public object getAttribute (String attribute) {return request.getAttribute (attribute); } 공개 열거 <?> getAttributeNames () {return request.getAttributeNames (); } @SuppressWarnings ( "감가 상각") public String getRealPath (String Path) {return request.getRealPath (PATH); } public requestDispatcher getRequestDispatcher (문자열 경로) {return request.getRequestDispatcher (Path); } public boolean issecure () {return request.issecure (); } public String getCharacterEncoding () {return request.getCharacterEncoding (); } public int getContentLength () {return request.getContentLength (); } public String getContentType () {return request.getContentType (); } public servletInputStream getInputStream ()는 ioException {return request.getInputStream (); } public locale getLocale () {return request.getLocale (); } 공개 열거 <?> getLocales () {return request.getLocales (); } public String getParameter (문자열 이름) {return request.getParameter (이름); } public map <?,?> getParameterMap () {return request.getParamEterMap (); } public enumeration <?> getParameterNames () {return request.getParameterNames (); } public String [] getParametErvalues (문자열 매개 변수) {return request.getParametErvalues (매개 변수); } public String getProtocol () {return request.getProtocol (); } public bufferedReader getReader ()는 ioException {return request.getReader (); } public String getRemoteaddr () {return request.getRemoteaddr (); } public String getRemoteHost () {return request.getRemoteHost (); } public string getScheme () {return request.getScheme (); } public String getServerName () {return request.getServerName (); } public int getServerport () {return request.getServerport (); } public void removeattribute (string attribute) {request.removeattribute (attribute); } public void stattribute (문자열 키, 개체 값) {request.setAttribute (키, 값); } public void setCharacterEncoding (String encoding)은 supportedencodingexception {request.setcharacterencoding (인코딩)을 던집니다. }} 응답 모양 클래스
패키지 ex02.pyrmont.second; import java.io.ioexception; import java.io.printwriter; import java.util.locale; import javax.servlet.servletresponse; import javax.servlet.servletoutputStream; ex02.pyrmont.response {private serveletresponsponsponsponsponsponsponspronst repriver response empress repless repless repless repless repless replentsponse public responskfacade (응답 응답) {this.response = 응답; } public void flushbuffer ()는 ioexception {response.flushbuffer (); } public int getBuffersize () {return response.getBuffersize (); } public String getCharacterEncoding () {return response.getCharacterEncoding (); } public locale getLocale () {return response.getLocale (); } public servletoutputStream getOutputStream ()는 ioException {return response.getOutputStream (); } public printwriter getwriter ()는 ioexception {return response.getwriter (); } public boolean iscommitted () {return response.iscommitted (); } public void reset () {response.reset (); } public void resetBuffer () {response.resetBuffer (); } public void setBuffersize (int size) {response.setbuffersize (size); } public void restbuffersize (크기); } public void restbuffersize (int size); } public void setBuffersize (크기); } public void setContentLength (int length) {response.setContentLength (길이); } public void setContentType (문자열 유형) {response.setContentType (type); } public void setLocale (Locale Locale) {response.setlocale (Locale); }}서비스 요청 클래스 처리 :
패키지 ex02.pyrmont.second; import java.net.url; import java.net.urlclassloader; import java.net.urlstreamhandler; import java.io.ioexception; import javax.servlet.servlet; import javax.servlet.servletrequest; import javax.servlet ex02.pyrmont.constants; import ex02.pyrmont.request; import ex02.pyrmont.response; public class servletprocessor2 {public void process (요청 요청, 응답 응답) {String uri = request.geturi (); 문자열 servletname = uri.substring (uri.lastindexof ( "/") + 1); // 지정된 JAR 파일 또는 디렉토리 로더에서 클래스 UrlClassLoader를로드하는 데 사용되는 클래스 로더 = null; {urlstreamhandler streamhandler = null; // 클래스 로더 로더 생성 = new URLClassLoader (new URL [] {new url (null,”파일 : " + constants.web_servlet_root, Streamhandler)); } catch (ioexception e) {System.out.println (e.toString ()); } class <?> myclass = null; {// 해당 서블릿 클래스 MyClass = loader.loadClass (servletname)를로드합니다. } catch (classNotFoundException e) {System.out.println (e.toString ()); } servlet servlet = null; // 요청 및 응답에 외관 클래스를 추가하고 보안 고려 사항이 ServletRequest 및 ServletResponse를 Servlet의 요청 및 응답 유형으로 직접 변환하지 못하도록 보안 고려 사항이 제공됩니다. // 내부 공개 메소드를 직접 호출합니다. Parse, SendStaticResource 및 RequestFacade 및 ResponseFacade에는 기타 방법이 없기 때문입니다. requestFacade requestFacade = 새 RequestFacade (요청); ResponseFacade ResponseFacade = New ResponseFacade (응답); try {servlet = (servlet) myclass.newinstance (); servlet.service ((servletrequest) requestfacade, (servletresponse) ResponseFacade); } catch (예외 e) {system.out.println (e.toString ()); } catch (Throwable e) {System.out.println (e.toString ()); }}} 다른 코드는 기본적으로 이전에 구현 된 서블릿 컨테이너와 동일합니다.
검증 프로그램은 각각 정적 자원과 서블릿을 요청하고 결과가 이전에 구현 된 컨테이너와 일치한다는 것을 발견합니다.
참조 : "Tomcat의 심층 분석"
@Author 바람과 같은 코더
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.