En el artículo anterior, escribí una implementación simple del servidor web Java, que solo puede manejar algunas solicitudes de recursos estáticos. El contenedor de servlet implementado en este artículo se ha modificado ligeramente en función del servidor anterior, agregando el procesamiento de las solicitudes de servlet.
Pasos de ejecución del programa
1. Cree un objeto Serversocket;
2. Llame al método de aceptación del objeto Serversocket y espere la conexión. Si la conexión es exitosa, se devolverá un objeto de socket, de lo contrario se bloqueará y esperará;
3. Obtenga las secuencias de byte InputStream y OutputStream desde el objeto Socket, y estas dos secuencias corresponden a la solicitud de solicitud y respuesta de respuesta respectivamente;
4. Procese la solicitud: lea la información de secuencia de bytes de entrada de InputStream, conviértala en un formulario de cadena y análisis. El análisis aquí es relativamente simple, y solo obtiene la información URI (identificador de recursos uniformes);
5. Procese la respuesta (en dos tipos, respuesta de solicitud de recursos estáticos o respuesta de solicitud de servlet): si se trata de una solicitud de recurso estático, basada en la información de URI analizada, busque el archivo de recursos solicitado en el directorio web_root, lea el archivo de recursos y la escriba en la secuencia de byte de salida de salida; Si se trata de una solicitud de servlet, primero genere un cargador de clase URLClassLoader, cargue la clase Servlet solicitada, cree el objeto Servlet y ejecute el método de servicio (escriba los datos de respuesta a OutputStream);
6. Cierre el objeto Socket;
7. Vaya al paso 2 y continúe esperando la solicitud de conexión;
Implementación del código:
Agregar dependencias:
<
Código de servidor:
paquete ex02.pyrmont.first; import java.net.socket; import java.net.serversocket; import java.net.inetaddress; import java.io.inputstream; import java.io.outputputputstream; import java.io.ioexception; import ex02.pyrmont.staticResourceProcessor; public class httpserver1 {// Cerrar comando de servicio Private static string final shutdown_command = "/shutdown"; public static void main (string [] args) {httpserver1 servidor = new httpserver1 (); // esperando solicitud de conexión servidor.await (); } public void await () {Serversocket Serversocket = null; Int Port = 8080; Pruebe {// Servidor Socket Object Serversocket = New Serversocket (puerto, 1, inetaddress.getByName ("127.0.0.1")); } catch (ioException e) {E.PrintStackTrace (); System.exit (1); } // bucle para esperar la solicitud mientras (verdadero) {socket socket = null; InputStream Input = NULL; OutputStream output = null; Pruebe {// Espere la conexión, después de que la conexión sea exitosa, devuelva un objeto Socket Socket = Serversocket.accept (); input = Socket.getInputStream (); output = Socket.getOutputStream (); // crear un objeto de solicitud y analizar solicitud de solicitud = nueva solicitud (entrada); request.Parse (); // Verifique si es un comando de servicio de cierre if (request.geturi (). Equals (shutdown_command)) {break; } // Crear respuesta del objeto Respuesta de respuesta = nueva respuesta (salida); respuesta.setRequest (solicitud); if (request.geturi (). startswith ("/servlet/")) {// request Uri comienza con/servlet/, lo que indica que servlet solicitud servletprocessor1 procesador = new ServletProcessor1 (); procesador.process (solicitud, respuesta); } else {// Procesador Static ResourSourceProcessor = new staticResourceProcessor (); procesador.process (solicitud, respuesta); } // Cerrar socket Socket.close (); } catch (Exception e) {E.PrintStackTrace (); System.exit (1); }}}}Clase constante:
paquete ex02.pyrmont; import 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"; }Pedido:
paquete 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.util.map; import javax.servlet; javax.servlet.servletInputStream; import javax.servlet.servletRequest; Public Class Solicement implementa ServLetRequest {private InputStream Entrada; Uri de cadena privada; Solicitud pública (entrada InputStream) {this.input = input; } public String geturi () {return uri; } /** * * The form of requestString is as follows: * GET /index.html HTTP/1.1 * Host: localhost:8080 * Connection: keep-alive * Cache-Control: max-age=0 * ... * The purpose of this function is to obtain /index.html string*/ private String parseUri(String requestString) { int index1, index2; index1 = requestString.IndexOf (''); if (index1! = -1) {index2 = requestString.IndexOf ('', index1 + 1); if (index2> index1) return requitstring.substring (index1 + 1, index2); } return null; } // Leer un conjunto de caracteres de la solicitud de Socket StringBuffer = new StringBuffer (2048); int i; byte [] buffer = new Byte [2048]; intente {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 ()); } / * Implementación de ServLetRequest * / Public Object getAttribute (atributo de cadena) {return null; } public enumeración <?> getAttributeNames () {return null; } public String getRealPath (ruta de cadena) {return null; } Public SoldDIspatcher GetRequestDispatcher (ruta de cadena) {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 () lanza ioexception {return null; } public locale getLocale () {return null; } public enumeración <?> getLocales () {return null; } public String getParameter (nombre de cadena) {return null; } mapa público <?,?> getparametermap () {return null; } public enumeración <?> getParamTernames () {return null; } public String [] getparametervalues (parámetro de cadena) {return null; } public String getProTocol () {return null; } public bufferedReader getReader () lanza 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 (atributo de cadena) {} public void setAttribute (clave de cadena, valor de objeto) {} public void setCharacterEncoding (codificación de cadena) lanza UnspportedEncodingException {}}Respuesta:
paquete 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.preintwriter; import java.util.locale; import jave.servlet.servlet.servlet; javax.servlet.servletOutputStream; Public Class Respuesta implementa ServletResponse {private static final int buffer_size = 1024; Solicitud de solicitud; Salida de salida de salida; Escritor de printwriter; Respuesta pública (salida de salida) {this.output = output; } public void setRequest (solicitud de solicitud) {this.request = request; } // Escribe el archivo web en la transmisión de byte OutputStream public void sendstaticResource () lanza ioexception {byte [] bytes = new byte [buffer_size]; FileInputStream fis = null; Prueba { / * request.geturi ha sido reemplazado por request.getRequesturi * / archivo archivo = nuevo archivo (constants.web_root, request.geturi ()); fis = nuevo fileInputStream (archivo); / * * Http respuesta = status-line ((General-Header | Response-Header | * entidad-header) CRLF) CRLF [Mensaje-Body] Status-line = * Http-Version SP-Código de estado SP Phrase 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 archivo no encontrado/r/n" + "Tipo de contenido: texto/html/r/n" + "Content-longitud: 23/r/n" + "/r/n" + "<h1> Archivo no encontrado </h1>"; output.write (errorMessage.getBytes ()); } finalmente {if (fis! = null) fis.close (); }} / ** Implementación de ServletResponse* / public void FlushBuffer () lanza ioexception {} public int getBufferSize () {return 0; } public String getCharacterEncoding () {return null; } public locale getLocale () {return null; } public ServLetOutputStream getOutputStream () lanza ioException {return null; } public printwriter getWriter () lanza ioexception {// autoflush es verdadero, println () se descargará, // pero imprime () no lo hará. escritor = nuevo PrintWriter (salida, verdadero); escritor de regreso; } public boolean isCommited () {return false; } public void reset () {} public void ResetBuffer () {} public void setBufferSize (int size) {} public void setContentLength (int long) {} public void setContentType (type de cadena) {} public void setLocale (local locale) {}}}Procesamiento de solicitudes de recursos estáticos:
paquete ex02.pyrmont; import java.io.ioException; public class staticResourceProcessor {Public void Process (solicitud de solicitud, respuesta de respuesta) {try {Response.sendStaticResource (); } catch (ioException e) {E.PrintStackTrace (); }}}Procesamiento de solicitudes de servlet:
paquete 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.servletRessesse; ex02.pyrmont.constants; import ex02.pyrmont.request; import ex02.pyrmont.Response; public class ServletProcessor1 {Public void Process (solicitud de solicitud, respuesta de respuesta) {String uri = request.getUri (); Cadena servletname = uri.substring (uri.lastindexof ("/") + 1); // ClassLoader, utilizado para cargar UrlClassLoader de clase desde un archivo jAR especificado o directorio loader = null; intente {urlstreamHandler streamHandler = null; // Crear class Loader Loader = new UrlClassLoader (new URL [] {new URL (null, "archivo:" + constants.web_servlet_root, streamHandler)}); } catch (ioException e) {System.out.println (e.ToString ()); } Class <?> Myclass = null; Pruebe {// Cargue la clase Servlet correspondiente myClass = Loader.LoadClass (ServletName); } Catch (ClassNotFoundException e) {System.out.println (e.ToString ()); } Servlet servlet = null; intente {// producir instancia de servlet servlet = (servlet) myclass.newinstance (); // Ejecutar el método de servicio de la solicitud Ervlet Servlet.Service ((ServLetRequest), (ServLetResponse) Respuesta); } catch (Exception e) {System.out.println (e.ToString ()); } catch (Throwable e) {System.out.println (e.ToString ()); }}}Clase de servlet:
import javax.servlet.*; import java.io.ioException; import java.io.printwriter; public class PrimitiveServlet implementa servlet {public void init (servletconfig config) lanza ServletException {System.out.println ("init"); } Servicio público void (solicitud de servletRequest, respuesta de servicio de servicio) lanza ServletException, ioexception {system.out.println ("del servicio"); PrintWriter out = Response.getWriter (); out.println ("Hola. Las rosas son rojas"); out.print ("Las violetas son azules"); } public void destruir () {System.out.println ("destruir"); } public String getServletInfo () {return null; } public servletConfig getServletConfig () {return null; }} Prueba de resultados:
Solicitud de recurso estático:
Solicitud de servlet (porque es solo la primera cadena que se actualiza al navegador, no puede ver que las violetas de la segunda cadena son azules. Refinaremos el contenedor más adelante):
mejorar
El contenedor de servlet implementado anteriormente tiene un problema grave. En el servlet, el usuario puede transformar directamente ServLetRequest y ServLetResponse en tipos de solicitudes y respuesta, y llamar directamente a sus métodos públicos internos. Este es un mal diseño. El método de mejora es agregar clases de apariencia para solicitar y responder, para que los usuarios solo puedan acceder a los métodos públicos definidos en la clase de apariencia.
Clase de apariencia de solicitud
paquete 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.Requestdispatcher; javax.servlet.servletinputStream; import javax.servlet.servletRequest; import ex02.pyrmont.request; public class SolicFacade implementa servletRequest {private ServletRequest solicitud = nulo; Public SolicFacade (solicitud de solicitud) {this.request = request; } / * Implementación de ServLetRequest * / Public Object getAttribute (atributo de cadena) {return request.getAttribute (atributo); } public enumeración <?> getAttributeNames () {return request.getAtTributeneames (); } @SupplesWarnings ("Deprecation") String public GetRealPath (string ruta) {return request.getRealPath (ruta); } Public RequestDispatcher getRequestDispatcher (string ruta) {return requit.getRequestDispatcher (ruta); } public boolean isSecure () {return requit.isseCure (); } public String getCharacterEncoding () {return request.getCharacterEncoding (); } public int getContentLength () {return request.getContentLength (); } public String getContentType () {return request.getContentType (); } public ServletInputStream getInputStream () lanza IOException {return request.getInputStream (); } public locale getLocale () {return request.getLocale (); } public enumeración <?> getLocales () {return request.getLocales (); } public String getParameter (nombre de cadena) {return request.getParameter (nombre); } mapa público <?,?> getParametermap () {return request.getParametermap (); } public enumeración <?> getParamTernames () {return request.getParamTernames (); } public String [] getParametervalues (parámetro de cadena) {return request.getParametervalues (parámetro); } public String getProtocol () {return request.getProTocol (); } public BufferedReader getReader () lanza 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 requit.getServerPort (); } public void RemoveAttribute (atributo de cadena) {request.removeAttribute (atributo); } public void setAttribute (clave de cadena, valor de objeto) {request.setAttribute (clave, valor); } public void setCharacterEncoding (codificación de cadena) lanza no compatible encodingException {request.setcharacterEncoding (codificación); }} Clase de apariencia de respuesta
paquete ex02.pyrmont.second; import java.io.ioException; import java.io.printwriter; import java.util.locale; import javax.servlet.servletResponse; import javax.servlet.servletutstream; import ex02.pyrmont.Respesse; public class ResponseFacadeStementsSespesse {private ServletRespesSeutStream; import Ex02.pyrmont.Response; Respuesta de la clase public; Respuesta públicaFacade (respuesta de respuesta) {this.Response = Respuesta; } public void FlushBuffer () lanza IOException {Response.FlushBuffer (); } public int getBufferSize () {return respuesta.getBufferSize (); } public String getCharacterEncoding () {return response.getCharacterEncoding (); } public locale getLocale () {return respuesta.getLocale (); } public ServLetOutputStream getOutputStream () lanza ioexception {return respuesta.getOutputStream (); } public PrintWriter getWriter () lanza ioException {return respuesta.getwriter (); } public boolean iSCommited () {return respuesta.IsCommited (); } public void reset () {Response.reset (); } public void ResetBuffer () {Response.ResetBuffer (); } public void setBufferSize (int tamaño) {Respuesta.SetBufferSize (tamaño); } public void resetBufferSize (tamaño); } public void resetbufferSize (int tamaño); } public void setBufferSize (tamaño); } public void setContentLength (int longitud) {Response.SetContentLength (longitud); } public void setContentType (tipo de cadena) {Response.SetContentType (type); } public void setLocale (locale locale) {Response.setLocale (locale); }}Clase de solicitud de servlet de procesamiento:
paquete 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.servletRessesse; ex02.pyrmont.constants; import ex02.pyrmont.request; import ex02.pyrmont.Response; public class ServletProcessor2 {Public Void Process (solicitud de solicitud, respuesta de respuesta) {String uri = request.getUri (); Cadena servletname = uri.substring (uri.lastindexof ("/") + 1); // cargador de clase, utilizado para cargar UrlClassLoader de clase desde un archivo jAR especificado o directorio cargador = null; intente {urlstreamHandler streamHandler = null; // Crear class Loader Loader = new UrlClassLoader (new URL [] {new URL (null, "archivo:" + constants.web_servlet_root, streamHandler)}); } catch (ioException e) {System.out.println (e.ToString ()); } Class <?> Myclass = null; Pruebe {// Cargue la clase Servlet correspondiente myClass = Loader.LoadClass (ServletName); } Catch (ClassNotFoundException e) {System.out.println (e.ToString ()); } Servlet servlet = null; // Agregar clases de apariencia para solicitar y respuesta, y se proporcionan consideraciones de seguridad para evitar que los usuarios transformen directamente ServletRequest y ServletResponse en tipos de solicitudes y respuesta en servlets, // y llamen directamente a sus métodos públicos internos, porque no habrá versiones, sendstaticResource y otros métodos de solicitud y respuesta de respuesta; RequestFacade requestFacade = nuevo requestFacade (solicitud); RespuestaFacade RespuestaFacade = nueva respuestafacade (respuesta); intente {servlet = (servlet) myclass.newinstance (); servlet.service ((servletRequest) requestFacade, (servletResponse) RespuestaFacade); } catch (Exception e) {System.out.println (e.ToString ()); } catch (Throwable e) {System.out.println (e.ToString ()); }}} Los otros códigos son básicamente los mismos que el contenedor Servlet implementado anteriormente.
El programa de verificación solicita recursos y servlets estáticos respectivamente, y encuentra que los resultados son consistentes con el contenedor implementado anteriormente;
Referencia: "Análisis en profundidad de Tomcat"
@Author un codificador de viento
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.