No artigo anterior, escrevi uma implementação simples do Java Web Server, que só pode lidar com algumas solicitações de recursos estáticos. O contêiner do servlet implementado neste artigo foi ligeiramente modificado com base no servidor anterior, adicionando o processamento das solicitações de servlet.
Etapas de execução do programa
1. Crie um objeto ServerSocket;
2. Chame o método de aceitação do objeto ServerSocket e aguarde a conexão. Se a conexão for bem -sucedida, um objeto de soquete será retornado, caso contrário, será bloqueado e esperado;
3. Obtenha os fluxos de bytes de entrada e saída do objeto de soquete, e esses dois fluxos correspondem à solicitação de solicitação e resposta à resposta, respectivamente;
4. Processe a solicitação: leia as informações do fluxo de byte inputStream, converta -as em um formulário de string e analise -o. A análise aqui é relativamente simples e obtém apenas as informações do URI (Identificador de Recursos Uniformes);
5. Processe a resposta (em dois tipos, resposta estática de solicitação de recursos ou resposta à solicitação de servlet): Se for uma solicitação de recurso estático, com base nas informações de URI analisado, encontre o arquivo de recursos de recursos solicitado no diretório Web_Root, leia o arquivo de recursos e escreva -o no fluxo de bytes de saída; Se for uma solicitação de servlet, primeiro gere um carregador de classe de uRLClassLoader, carregue a classe de servlet solicitada, crie o objeto Servlet e execute o método de serviço (escreva os dados de resposta para o outputStream);
6. Feche o objeto de soquete;
7. Vá para a etapa 2 e continue esperando a solicitação de conexão;
Implementação de código:
Adicionar dependências:
<!-https://mvnrepository.com/artifact/javax.servlet/servlet-api-> <pendency> <voundid> javax.servlet </proupid> <stifactId> servlet-api </stifactId> <versão 2.3 </versão </dependência>
Código do servidor:
pacote ex02.pyrmont.first; importar java.net.socket; importar java.net.serversocket; importar java.net.inetaddress; importar java.io.inputStream; import java.io.outputStream; import java.io.ioException; importação ex02.pyrmont.rent.rentStream; import java.io.ioxception; importação ex02.pyrmont.PyRMont.rent.rentStream; importação; ex02.pyrmont.staticResourceProcessor; public Class httpServer1 {// Fechar o comando de serviço privado estático final string Shutdown_Command = "/Shutdown"; public static void main (string [] args) {httpServer1 server = new httpServer1 (); // aguardando a solicitação de conexão server.await (); } public void Await () {ServerSocket ServerSocket = null; int porta = 8080; tente {// servidor de soquete de servidor serversocket = new ServerSocket (porta, 1, inetaddress.getByName ("127.0.0.1")); } catch (ioexception e) {e.printStackTrace (); System.Exit (1); } // loop para aguardar a solicitação while (true) {soquete soket = null; InputStream input = null; OutputStream Output = NULL; tente {// aguarde a conexão, depois que a conexão for bem -sucedida, retorne um soquete de soquete soquete = serversocket.accept (); input = soket.getInputStream (); output = Socket.getOutputStream (); // Crie um objeto de solicitação e solicitação de parse solicitação = nova solicitação (entrada); request.parse (); // Verifique se é um comando de serviço de desligamento se (request.geturi (). Equals (Shutdown_Command)) {break; } // Crie resposta de resposta de objeto de resposta = nova resposta (saída); Response.setRequest (solicitação); if (request.geturi (). startSwith ("/servlet/")) {// request uri inicia com/servlet/, indicando que o servlet solicita servletprocessor1 processador = new servletprocessor1 (); processador.process (solicitação, resposta); } else {// estático ResourceRceRceProcessor Processor = new StaticResourceProcessor (); processador.process (solicitação, resposta); } // feche o soquete Socket.close (); } catch (Exceção e) {e.printStackTrace (); System.Exit (1); }}}}Classe constante:
pacote ex02.pyrmont; importar java.io.file; public class Constants {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 + "classes"; }Solicitar:
pacote ex02.pyrmont; importar java.io.inputStream; importar java.io.ioException; importar java.io.bufferedReader; importar java.io.unsupportEdEncodingException; importação de java.util.enumation; import.utilet.locale; importação de java.utilil.util.enumation; import.utilet.locale; importação.utililil.util.util.enumation; import.utilet.locale; importação.utililililil.util.enumeration; import.utilet.locale; importação.utililililil.util. javax.Servlet.ServletInputStream; importar javax.servlet.ServletRequest; solicitação de classe pública implementa servletRequest {inputStream entrada privada; URI de cordas privadas; solicitação pública (entrada inputStream) {this.input = input; } public string geturi () {return uri; } /** * * A forma do requeststring é a seguinte: * get /index.html http /1.1 * host: localhost: 8080 * conexão: keep-alive * cache-control: max-Arane = 0 * ... * O objetivo desta função é obtenção /indentr /html string * /private string parseury (string stringi; index1 = requestString.indexof (''); if (index1! = -1) {index2 = requestString.indexof ('', index1 + 1); if (index2> index1) retornar requeststring.substring (index1 + 1, index2); } retornar nulo; } // Leia um conjunto de caracteres do Socket StringBuffer request = new StringBuffer (2048); int i; byte [] buffer = novo byte [2048]; tente {i = input.read (buffer); } catch (ioexception e) {e.printStackTrace (); i = -1; } para (int j = 0; j <i; j ++) {request.append ((char) buffer [j]); } System.out.print (request.toString ()); uri = parseuri (request.toString ()); } / * Implementação do servletRequest * / public Object getAttribute (atributo string) {return null; } enumeração pública <?> getAttributenames () {return null; } public String getRealPath (String Path) {return null; } public requestdispatcher getRequestDispatcher (string path) {return null; } public boolean isSCURE () {return false; } public string getCharacterencoding () {return null; } public int getContentLength () {return 0; } public string getContentType () {return null; } public servletInputStream getInputStream () lança ioexception {return null; } public loce getLocale () {return null; } enumeração pública <?> getLocales () {return null; } public string getParameter (nome da string) {return null; } mapa público <?,?> getParameterMap () {return null; } enumeração pública <?> getParameterNames () {return null; } public string [] getParameTervAlas (parâmetro string) {return null; } public string getProtocol () {return null; } public buffarredReader getReader () lança 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 (atribuições string) {} public void setAttribute (chave da string, valor do objeto) {} public void setCharacterencoding (String coding) lança sem suportesCodingingException {}}Resposta:
pacote ex02.pyrmont; importar java.io.OutputStream; importar java.io.ioException; importar java.io.fileInputStream; importar java.io.filenotfoundException; import java.locale; Javax.Servlet.ServletOutputStream; classe pública Resposta implementa o servletResponse {private static final int buffer_size = 1024; Solicitação de solicitação; Saída de saída de saída; PrintWriter Writer; Resposta pública (saída de outputStream) {this.output = output; } public void setRequest (solicitação de solicitação) {this.request = request; } // Escreva o arquivo da web no fluxo de saída de outputStream public void sendStaticResource () lança IoException {byte [] bytes = new Byte [buffer_size]; FileInputStream fis = null; tente { / * request.geturi foi substituído por request.getRequesturi * / arquivo file = new File (constantes.web_root, request.geturi ()); fis = new FileInputStream (arquivo); / * * HTTP Resposta = linha de status ((cabeçalho geral | Header de resposta | * Chefe de Entidade) CRLF) CRLF [Message-Body] status-line = * http-version sp-código de status sp sp raciocer-frase crlf */ int ch = fis.read (bytes, 0, buffer_size); while (ch! = -1) {output.write (bytes, 0, ch); ch = fis.read (bytes, 0, buffer_size); }} catch (filenotfoundException e) {string errorMessage = "http/1.1 404 arquivo não encontrado/r/n" + "tipo conteúdo: text/html/r/n" + "comprimento de conteúdo: 23/r/n" + "/r/n" "<h1> arquivo não encontrado </h1>"; output.Write (errorMessage.getBytes ()); } finalmente {if (fis! = null) fis.close (); }} / ** Implementação do servletResponse* / public void FlushBuffer () lança IoException {} public int getBuffersize () {return 0; } public string getCharacterencoding () {return null; } public loce getLocale () {return null; } public servletOutputStream getOutputStream () lança ioexception {return null; } public printWriter getWriter () lança ioexception {// autoflush é true, println () será lançado, // mas print () não. writer = new PrintWriter (saída, true); escritor de retorno; } public boolean isCommitd () {return false; } public void reset() { } public void resetBuffer() { } public void setBufferSize(int size) { } public void setContentLength(int length) { } public void setContentType(String type) { } public void setLocale(Locale locale) { }}Processamento de solicitação de recurso estático:
pacote ex02.pyrmont; importar java.io.ioException; public class StaticResourceProcessor {public void Process (solicitação de solicitação, resposta de resposta) {try {Response.sendStaticResource (); } catch (ioexception e) {e.printStackTrace (); }}}Processamento de solicitação de servlet:
pacote ex02.pyrmont.first; importar java.net.url; importar java.net.urlclassloader; importar java.net.urlstreamhandler; importar java.io.ioException; import javax.servlet.servlet.ervlet. ex02.pyrmont.constants; importar ex02.pyrmont.request; import ex02.pyrmont.Response; public class ServletProcessor1 {public void Process (solicitação de solicitação, resposta de resposta) {string uri = request.geturi (); String servletname = uri.substring (uri.lastindexof ("/") + 1); // ClassLoader, usado para carregar o URLClassLoader da classe de um arquivo jar especificado ou carregador de diretório = null; tente {urlstreamhandler streamHandler = null; // Criar classe Loader Loader = new UrlClassLoader (novo URL [] {new URL (NULL, "FILE:" + Constants.Web_Servlet_root, StreamHandler)}); } catch (ioexception e) {System.out.println (e.toString ()); } Classe <?> Myclass = null; tente {// Carregar a classe servlet correspondente myclass = carregador.loadclass (servletname); } catch (classNotFoundException e) {System.out.println (e.toString ()); } Servlet servlet = null; tente {// Produza a instância do servlet servlet = (servlet) myclass.newInstance (); // execute o método de serviço da solicitação Ervlet Servlet.Service ((ServletRequest), (servletResponse)); } catch (Exceção e) {System.out.println (e.toString ()); } catch (throwable e) {System.out.println (e.toString ()); }}}Classe de servlet:
importar javax.servlet.*; importar java.io.ioException; importar java.io.printwriter; public class primitiveServlet implementa servlet {public void init (servletConfig config) lança servletexception {System.out.println ("init"); } public void Service (Solicitação de servletRequest, resposta servletResponse) lança servletexception, ioexception {System.out.println ("do serviço"); PrintWriter out = Response.getWriter (); out.println ("Olá. Rosas são vermelhas"); out.print ("violetas são azuis"); } public void Destro () {System.out.println ("Destroy"); } public string getServletinfo () {return null; } public servletConfig getServletConfig () {return null; }} Teste de resultados:
Solicitação de recurso estático:
Solicitação de servlet (porque é apenas a primeira corda que está sendo liberada para o navegador, você não pode ver que as segundas strings violetas são azuis. Vamos refinar o contêiner mais tarde):
melhorar
O contêiner de servlet implementado anteriormente tem um problema sério. No servlet, o usuário pode transformar diretamente o ServletRequest e o ServletResponse em tipos de solicitação e resposta e chamar diretamente seus métodos públicos internos. Este é um design ruim. O método de melhoria é adicionar classes de aparência à solicitação e resposta, para que os usuários possam acessar apenas os métodos públicos definidos na classe de aparência.
Solicitar a classe de aparência
pacote ex02.pyrmont.second; importar java.io.ioException; importar java.io.bufferedReader; importar java.io.unsupportEnCodingException; importar java.util.enumeration; importar java.uta.uta.locale; importação java.util.map; javax.Servlet.ServletInputStream; importar javax.servlet.servletRequest; import ex02.pyrmont.request; public class RequestFacade implementa servletRequest {private servletRequest request = null; public requestfacade (solicitação de solicitação) {this.request = request; } / * Implementação do servletRequest * / public objeto getAttribute (atributo string) {return request.getAttribute (attribute); } enumeração pública <?> getAttributenames () {return request.getAttributenames (); } @Suppresswarnings ("deprecação") public string getRealPath (string path) {return request.getRealPath (path); } public requestDispatcher getRequestDispatcher (string path) {return request.getRequestDispatcher (caminho); } public boolean isSCURE () {return request.issecure (); } public string getCharacterencoding () {return request.getcharacterencoding (); } public int getContentLength () {return request.getContentLength (); } public string getContentType () {return request.getContentType (); } public servletInputStream getInputStream () lança ioexception {return request.getInputStream (); } public loce getLocale () {return request.getLocale (); } enumeração pública <?> getLocales () {return request.getLocales (); } public string getParameter (nome da string) {return request.getParameter (nome); } mapa público <?,?> getParameterMap () {return request.getParameterMap (); } enumeração pública <?> getParameterNames () {return request.getParameterNames (); } public string [] getParameTervAlas (parâmetro string) {return request.getParameterValues (parâmetro); } public string getProtocol () {return request.getProtocol (); } public buffarredReader getReader () lança 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 (atributo string) {request.removeattribute (attribute); } public void SetAttribute (chave da string, valor do objeto) {request.SetAtTribute (chave, valor); } public void setCharacterencoding (codificação de string) lança UnsupportEdEnCodingException {request.SetcharAcTerEncoding (codificação); }} Classe de aparência de resposta
package ex02.pyrmont.second;import java.io.IOException;import java.io.PrintWriter;import java.util.Locale;import javax.servlet.ServletResponse;import javax.servlet.ServletOutputStream;import ex02.pyrmont.Response;public class ResponseFacade implements ServletResponse { private ServletResponse response; public ResponseFacade (resposta da resposta) {this.Response = Response; } public void FlushBuffer () lança IoException {Response.flushBuffer (); } public int getBuffersize () {return Response.getBuffersize (); } public string getCharacterencoding () {return Response.getCharacterencoding (); } public loce getLocale () {return Response.getLocale (); } public servletOutputStream getOutputStream () lança IoException {return Response.getOutputStream (); } public printWriter getWriter () lança IoException {return Response.getWriter (); } public boolean isCommitd () {return Response.iscommited (); } public void reset () {Response.Reset (); } public void resetBuffer () {Response.ResetBuffer (); } public void setBuffersize (int size) {Response.SetBuffersize (tamanho); } public void RESETBuffersize (tamanho); } public void RESETBUFERSIZE (INT TAMANHO); } public void setBuffersize (tamanho); } public void setContentLength (int length) {Response.SetContentLength (comprimento); } public void setContentType (String tipo) {Response.SetContentType (Type); } public void setLocale (loce loce) {Response.SetLocale (Locale); }}Classe de solicitação de servlet de processamento:
pacote ex02.pyrmont.second; importar java.net.url; importar java.net.urlclassloader; importar java.net.urlStreamHandler; importar java.io.ioException; import javax.servlet.servlet.ervlet.port.ServleTleTrex; ex02.pyrmont.constants; importar ex02.pyrmont.request; import ex02.pyrmont.Response; public class ServletProcessor2 {public void Process (solicitação de solicitação, resposta de resposta) {string uri = request.geturi (); String servletname = uri.substring (uri.lastindexof ("/") + 1); // Classe carregador, usado para carregar o URLClassLoader da classe de um arquivo JAR especificado ou carregador de diretório = NULL; tente {urlstreamhandler streamHandler = null; // Criar classe Loader Loader = new UrlClassLoader (novo URL [] {new URL (NULL, "FILE:" + Constants.Web_Servlet_root, StreamHandler)}); } catch (ioexception e) {System.out.println (e.toString ()); } Classe <?> Myclass = null; tente {// Carregar a classe servlet correspondente myclass = carregador.loadclass (servletname); } catch (classNotFoundException e) {System.out.println (e.toString ()); } Servlet servlet = null; // Adicione as classes de aparência à solicitação e resposta, e são dadas considerações de segurança para impedir que os usuários transformem diretamente o servletRequest e o servletResponse em tipos de solicitação e resposta em servlets, // e chamam diretamente seus métodos públicos internos, porque não haverá análise, sendstaticResource e outros métodos no RequestFacade e ResponseFacade; Requestfacade requestFacade = new requestFacade (request); ResponseFacade ResponsFacade = New ResponseFacade (resposta); tente {servlet = (servlet) myclass.newInstance (); Servlet.Service ((ServletRequest) requestFacade, (servletResponse) ResponseFacade); } catch (Exceção e) {System.out.println (e.toString ()); } catch (throwable e) {System.out.println (e.toString ()); }}} Os outros códigos são basicamente os mesmos que o contêiner de servlet implementado anteriormente.
O programa de verificação solicita recursos e servlets estáticos, respectivamente, e constata que os resultados são consistentes com o contêiner implementado anteriormente;
Referência: "Análise aprofundada do tomcat"
@Author um codificador semelhante ao vento
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.