Red de conocimiento informático - Material del sitio web - Cómo desarrollar su propio HttpServer: interpretación del código fuente de NanoHttpd

Cómo desarrollar su propio HttpServer: interpretación del código fuente de NanoHttpd

Ahora, como desarrollador, /NanoHttpd/nanoSocketAddress(hostname,?myPort)?:?new?InetSocketAddress(myPort));?

myThread =?new?{

@Override

¿público?{

¿?{?

¿intentar?{?

¿final?

registerConnection(finalAccept);?

finalAccept.setSoTimeout(SOCKET_READ_TIMEOUT);?

finalAccept.getInputStream();?

asyncRunner.exec( ¿nuevo?Runnable()?{

@Override

público?{

OutputStream?

probar{? p>outputStream?=?finalAccept.getOutputStream();?

TempFileManager?

HTTPSession?session?=?new?HTTPSession(tempFileManager,?inputStream,?outputStream,?finalAccept. getInetAddress());?

while(!finalAccept.isClosed())?{

session.execute();?

}?

}?{

// Cuando el cliente cierra el socket, lanzaremos nuestra propia SocketException.

"

/?

si?(! (e?instanceof?SocketException?amp;?" NanoHttpd?NanoHttpd?Close".equals(e.getMessage())) )? {

e.printStackTrace();?

}?

}?finalmente?{?

safeClose(outputStream);?

safeClose(inputStream);?

safeClose(finalAccept);?

unRegisterConnection(finalAccept);?

}? p>

}?

});?

}?{

}};?

}? p>

}?

} while(!myServerSocket.isClosed());?

}?

}});?

myThread.setDaemon(true);?

myThread.setName("NanoHttpd?Main?Listener");?

myThread.start();?

}?

1. Cree un ServerSocket y vincule el puerto

2. Cree el hilo principal, que es responsable de establecer una conexión con el cliente

3. Una vez establecida la conexión, se generará un objeto ejecutable y se colocará en asyncRunner.exec creará un hilo para manejar la conexión recién generada. El nuevo hilo primero crea una HttpSession y luego ejecuta httpSession.exec (verdadero).

Aquí hay una introducción al concepto de HttpSession que es la implementación del concepto de sesión en Java. En pocas palabras, la sesión es httpClient-gt; conexión httpServer, la sesión finaliza cuando se cierra la conexión, si no finaliza, la sesión siempre existirá. Esto también se puede ver en el código aquí: si el socket no está cerrado o el exec no. lanza una excepción (la excepción puede ser que el segmento de cliente esté desconectado), la sesión siempre ejecutará el método exec

HttpSession almacena información que el servidor debe guardar durante la conexión de red, como URI, MÉTODO. , PARAMÁS, ENCABEZADOS, COOKIES, etc.

5. El modelo de servidor que acepta el socket del cliente para crear un hilo separado es el modelo ThreadServer. Su característica es que una conexión creará un hilo, que es una implementación relativamente simple y común del servidor de socket. La desventaja es que el cambio de subprocesos consume muchos recursos cuando se procesa una gran cantidad de conexiones al mismo tiempo. Si está interesado, puede obtener información sobre implementaciones de NIO más eficientes.

Después de obtener el socket del cliente, es natural comenzar a procesar la solicitud http enviada por el cliente.

Análisis del encabezado de solicitud HTTP:

[plain]?ver copia simple

//?Read?

//?El encabezado completo ¿Debería caber?

//"El límite de encabezado predeterminado de Apache es 8 KB.

//"¡No crea que una sola lectura obtendrá el encabezado completo a la vez! ".

byte[]?

splitbyte?=?0;?

rlen?=?0;?

{

p>

int?-1;?

intenta{?

read?=?inputStream.read(buf,?0,?BUFSIZE); ?

}?{

safeClose(inputStream);?

safeClose(outputStream);?

lanzar?new?SocketException( "NanoHttpd?Shutdown" );?

}?

if(read == -1)?{

//?socket?was?been? ¿cerrado?

safeClose(inputStream);?

safeClose(outputStream);?

lanzar nueva SocketException("NanoHttpd?Shutdown");?

} ?

mientras(leer gt;?0)?{

rlen?

splitbyte = findHeaderEnd(buf, rlen);?

if(splitbyte?gt;?0)?

romper;?

read?=?inputStream.read(buf,?rlen,?BUFSIZE?- ?rlen);?

}?

}?

1. Lea los primeros 8192 bytes del flujo del socket, porque la longitud máxima del encabezado en el protocolo http es 8192

2. Utilice la función findHeaderEnd para encontrar la posición de corte de los datos del encabezado y guardar la posición en splitbyte.

[java]?ver copia simple

if?(splitbyte?lt;?rlen)?{

inputStream.unread(buf,?splitbyte,?rlen ?-?splitbyte);?

}?

parms?=?new?HashMaplt;String,?Stringgt;();?

if(null ?==?headers)?{

headers?=?new?HashMaplt;String,?Stringgt;();?

}

// ?

BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf,?0,?rlen)));?

//?Decode?header?into?parms?

decodeHeader(hin,?pre,?parms,?headers);?

1. Aquí se utiliza la función no leído para retroceder el texto leído anteriormente. Inteligente, porque una vez que lee el final de la información del encabezado, debe ingresar la siguiente lógica para determinar si necesita continuar leyendo. No debe leer antes de que no haya datos

DecodeHeader(hin,? pre,?parms,?headers ); ?pre, ?parms, ?headers);;;;

DecodeHeader(hin, ?parms, ?headers);; objetos

1.El protocolo HTTP es la primera línea del método URI HTTP_VERSION

2.KEY: cada línea después del encabezado de formato VALOR

3.uri debe ser procesado por URIDecode antes de que pueda usarse

4. Si el uri contiene?, significa que hay un parámetro. El parámetro httprequest generalmente se expresa como: /index.jsp?username=xiaomingamp; 2

Lo que se debe procesar a continuación son cookies, pero la implementación de cookies aquí es relativamente simple, así que omítala. El siguiente es el método de servicio, que proporciona una buena interfaz para que los usuarios implementen su propia lógica específica del servidor https. El método de servicio en NanoHttpd implementa una función de procesamiento simple de forma predeterminada.

Este método predeterminado maneja los métodos PUT y POST, y devuelve el valor de retorno predeterminado si no se maneja.

El método parseBody utiliza el método tmpFile para guardar la información del contenido de httpRequest y luego la procesa. La lógica específica no se detallará.

Finalmente, observe la lógica de enviar una respuesta:

sendAsFixedLength(outputStream, pendiente);?

}?

outputStream.flush ();?

safeClose(datos);?

}?{

//?

}?

}?

Los pasos para enviar una respuesta son los siguientes:

1. Establezca mimeType y Time, etc.

2.

2. Cree un PrintWriter y comience a escribir contenido en el orden del protocolo HTTP

3. La primera línea es el código de retorno HTTP

4. Luego está el tipo de contenido

5. Luego la fecha y la hora

6. Seguido de otros encabezados HTTP

7. Establecer el encabezado Keep-Alive es una nueva característica de HTTP 1.1 y su función es. Mantener un vínculo largo entre el cliente y el servidor.

8. Si el cliente especifica ChunkedEncoding, la respuesta se enviará en fragmentos es otra característica nueva de Http1.1. Generalmente se usa cuando el cuerpo de la respuesta es relativamente grande. El servidor primero enviará el ENCABEZADO de la respuesta y luego enviará el cuerpo de la respuesta en fragmentos. Cada fragmento consta de la longitud del fragmento (longitud del fragmento) y los datos del fragmento (datos del fragmento), y finalmente termina con. 0\r\ n fin.

9. Si no se especifica ChunkedEncoding, se debe especificar Content-Length para permitir que el cliente especifique el tamaño del cuerpo de la respuesta y luego escriba el cuerpo hasta su finalización.

Finalmente, resumamos las partes más importantes de la implementación de HttpServer:

1. Ser capaz de aceptar conexiones tcp y leer datos de solicitud desde el socket

2. Convierta el flujo de bits de la solicitud en los datos del objeto de la solicitud

3. Procese http de acuerdo con las especificaciones del protocolo http. Maneje las solicitudes http de acuerdo con las especificaciones del protocolo http

4. Genere una respuesta http y escríbala nuevamente en el socket para entregársela al cliente.