Baixe o app para aproveitar ainda mais
Prévia do material em texto
Trabalho 1 – Redes de comunicação I – Semestre 2020/1 (Abril de 2021) Implemetação de um servidor HTTP Multithread. Nome : João Barboza Rodrigues Matrícula : 11811ECP005 Curso : Engenharia da Computação 1. Referencial e embasamento O protocolo HTTP (Hypertext Transfer Protocol) é um protocolo utilizado em nível de aplicação que provê a leveza é rapidez necessária para sistemas distribuídos de Hypermedia. Hoje em dia, HTTP é o protocolo que tem sido utilizado pelo World-Wide Web desde 1990, onde era comumente referido como “HTTP/1.0”, primeira versão do protocolo que é definida pelo RFC 1945. O HTTP permite a obtenção de recurso, como documento HTML, e é a base de qualquer troca de dados ou informações na web, sendo um protocolo cliente-servidor, ou seja, as requisições são iniciadas por um cliente , normalmente um browser, e que são respondidas por um servidor, com um documento html, por exemplo. Clientes e servidores se comunicam por meio de troca de mensagens individuais, as mensagens enviadas pelo cliente são denominadas requests e as mensagens enviadas pelo servidor como respostas são denominadas responses. O termo multithread se caracteriza pela abertura de múltiplas “Threads”, threads agem como uma subdivisão de um processo, permitindo que um processo realize diversas tarefas de forma concorrente, sendo mais eficiente que a abertura de múltiplos processos devido a não ser necessário efetuar trocas de contexto. No caso do servidor HTTP, uma thread é aberta para cada conexão entre cliente-servidor, permitindo que múltiplos acessos ao servidos sejam efetuados ao mesmo tempo. 2. Desenvolvimento Para o desenvolvimento do servidor, foram utilizados os exemplos disponibilizados na página do microsoft teams da disciplina de Redes 1 e exemplos encontrados em páginas encontradas na web. A linguagem escolhida para o desenvolvimento foi Java, utilizando web sockets e o método do próprio Java denominado Thread para implementação do Multithreading, além de arquivos html utilizados como respostas para as solicitações de GET feitas ao servidor. 3. Código 3.1 - Código Servidor em java (Porta 3000) import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.Date; import java.util.Locale; import java.util.StringTokenizer; public final class HTTPServer implements Runnable { static final File WEB_ROOT = new File("."); static final String DEFAULT = "index.html"; static final String NOT_FOUND = "404.html"; static final String NOT_SUPPORTED = "not_supported.html"; static final int PORT = 3000; static final boolean verbose = true; private Socket connect; @Override public void run() { BufferedReader reader = null; PrintWriter writer = null; BufferedOutputStream output = null; String request = null; try { reader = new BufferedReader(new InputStreamReader(connect.getInputStream())); writer = new PrintWriter(connect.getOutputStream()); output = new BufferedOutputStream(connect.getOutputStream()); String input = reader.readLine(); StringTokenizer parsed = new StringTokenizer(input); String method = parsed.nextToken().toUpperCase(Locale.ROOT); request = parsed.nextToken().toLowerCase(Locale.ROOT); method); if (!method.equals("GET") && !method.equals("HEAD")) { if (verbose) { System.out.println("501 - Not Implemented : " + } File file = new File(WEB_ROOT, NOT_SUPPORTED); int fileLenght = (int) file.length(); String content = "text/html"; byte[] fileData = readFileData(file, fileLenght); Barboza"); //HTTP Response builder writer.println("HTTP/1.1 501 Not Implemented"); writer.println("Server: Java HTTP Server: João writer.println("Date " + new Date()); writer.println("Content-type: " + content); writer.println("Content-length" + fileLenght); writer.println(); writer.flush(); output.write(fileData, 0, fileLenght); output.flush(); } else { if (request.endsWith("/")) { request = request + DEFAULT; } File file = new File(WEB_ROOT, request); int lenght = Math.toIntExact(file.length()); String content = getContentType(request); if (method.equals("GET")) { byte[] fileData = readFileData(file, lenght); Barboza"); writer.println("HTTP/1.1 200 OK"); writer.println("Server: Java HTTP Server: João writer.println("Date " + new Date()); writer.println("Content-type: " + content); writer.println("Content-length" + lenght); writer.println(); writer.flush(); output.write(fileData, 0, lenght); output.flush(); } if (verbose) { System.out.println("File " + request + " of type " + content + " returned"); } } } catch (FileNotFoundException error404Exception) { try { fileNotFound(writer, output, request); } catch (IOException e) { System.err.println("ERROR 404 - File not found" + e.getMessage()); } } catch (IOException e) { e.printStackTrace(); } finally { try { reader.close(); writer.close(); output.close(); connect.close(); } catch (Exception e) { System.out.println("Error closing data stream : " + e.getMessage()); } if (verbose) { System.out.println("Connection Closed. \n"); } } } public HTTPServer(Socket socket) { connect = socket; } public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(PORT); System.out.println("Server started with success!\n" + "Listening for connection on port: " + PORT); while (true) { HTTPServer myServer = new HTTPServer(serverSocket.accept()); if (verbose) { System.out.println("Connection stabilised"); } Thread thread = new Thread(myServer); thread.start(); } } catch (IOException e) { e.printStackTrace(); } } private byte[] readFileData(File file, int lenght) throws IOException { FileInputStream input = null; byte[] fileData = new byte[lenght]; try { input = new FileInputStream(file); input.read(fileData); } finally { if (input != null) input.close(); } return fileData; } private String getContentType(String request) { if (request.endsWith(".htm") || request.endsWith("html")) return "text/html"; else { return "text/plain"; } } private void fileNotFound(PrintWriter writer, OutputStream output, String request) throws IOException { File file = new File(WEB_ROOT, NOT_FOUND); int length = Math.toIntExact(file.length()); String content = "text/html"; byte[] fileData = readFileData(file, length); writer.println("HTTP/1.1 404 OK"); writer.println("Server: Java HTTP Server: João Barboza"); writer.println("Date " + new Date()); writer.println("Content-type: " + content); writer.println("Content-length" + length); writer.println(); writer.flush(); output.write(fileData, 0, length); output.flush(); if (verbose) { System.out.println("File " + request + " of type " + content + " returned"); } } } 4. Teste do funcionamento do servidor Para realizar os teste no servidor foram utilizadas 3 ferramentas, o Google Chrome, um browser, o Insomnia, um software que faz requisições HTTPs e é utilizado para testar e registrar o funcionamento de servidores HTTP, e o Wireshark, ferramenta utilizada para captura de pacotes. Insomnia O Insomnia permite que o usuário defina uma rota, um ambiente em que essa rota opera (Para caso de servidores que possuam necessidade de SSH tunnel , Autenticação por JWT e variáveis ), método da requisição (GET, PUT, POST e afins ) e o corpo das requisições, além de registrar a resposta do servidor. Para os testes, foi utilizado um arquivo HTML personalizado e uma requisição denominada Get HTML, que acessa o endereço “localhost:3000” utilizando o método “GET”. Dados o cenário exposto, os resultados obtidos foram os seguintes: Arquivo HTML contido na response, tempo de resposta, tamanho da resposta e código HTTP da response(200 – OK) Nome do servidor acessado pela requisição HTTP “Java HTTP Server: João Barboza”, momento da resposta e Content-type. Fluxo da requisição HTTP realizada pelo Insomnia Google Chrome O Google Chrome se trata de um browser comum, que pode acessar o servidor HTTP e teoricamente deve receber o arquivo HTTP e mostra-lo. O browser também permite obter informações relativas as requisições através das ferramentas de desenvolvedor, na aba NETWORK. Arquivo HTTP obtido no endereço “localhost:3000” exibido pelo google Chrome Informações sobre a requisição HTTP realizada pelo browser Wireshark O Wireshark se trata de uma ferramenta que permite capturar pacotes e obter as informações relativas a esses pacotes, através da utilização do filtro “http” do Wireshark, é possível filtrar todas as capturas para que apenas as que utilizem o protocolo HTTP sejam mostradas. Essa configuração foi utilizada, e múltiplas requisições foram feitas ao servidor utilizando o Insomnia. Nenhum navegador estava aberto no momento, dessa maneira todos os pacotes capturados são do servidor implementado. Captura de pacotes realizada pelo Wireshark Informações relativas a request feita pelo Insomnia ao servidor. Nota-se que a porta de destino é a porta 3000, onde funciona o servidor. Informações mais detalhadas da resquest. Informações relativas a response do servidor. Nota-se que a porta de origem é a porta 3000, onde está funcionando o servidor. Informações mais detalhadas da response. Além desses testes mais detalhados, também foi utilizado o acesso ao servidor através do comando do POSIX “curl”, o resultado obtido foi o seguinte (O arquivo HTML utilizado é diferente dos utilizados nos testes anteriores): Durante os teste, o funcionamento do servidor se deu da seguinte maneira (Captura realizada no IntelliJ, IDE para desenvolvimento em Java): Nota-se que para cada requisição recebida, uma thread é aberta para processar a requisição, e é fechada logo após gerar uma response. Teste de sobrecarga Para testar a efetividade do multithreading e recepção de múltiplas requisições simultaneamente, foi desenvolvido um Scrip em python que envia requisições simultaneamente para todas as URLs de uma lista. Abaixo o Script (batchSize indica o numero de requisições enviadas). import grequests batchSize = 8 url = 'http://127.0.0.1:3000' urls = [] urls += batchSize * [url] rs = (grequests.get(u) for u in urls) grequests.map(rs) A imagem detalha o funcionamento do servidor ao receber as requests semelhantes, note que foi adicionada uma linha de código que printa o momento do estabelecimento da conexão e encerramento. Devido as multithreads, várias requisições puderam ser processadas simultaneamente, na imagem o número de requisições simultâneas foi 8. No arquivo .txt enviado junto com este arquivo, está o comportamento do servidor ao receber 1024 requisições simultaneamente. Devido a extensão superior a 4000 linhas, a reposta do servidor para esse casa está sendo enviada em um arquivo separado.
Compartilhar