INF1013 MODELAGEM DE SOFTWARE Departamento de Informática Ivan Mathias Filho ivan@inf.puc-rio.br Programa Capítulo 18 Sockets OLadoServidor O Lado Cliente Múltiplos Clientes 1
Programa Capítulo 18 Sockets OLadoServidor O Lado Cliente Múltiplos Clientes Protocolo (1) Através do TCP, é possível criar um fluxo entre dois computadores, como é mostrado na figura abaixo: 2
Protocolo (2) Pode-se conectar mais de um cliente a um mesmo servidor, como é o caso de servidores de banco de dados e servidores Web Programas Java não precisam lidar com os aspectos de baixo nível do protocolo TCP O pacote java.net disponibiliza classes para que permitem estabelecer conexões de forma simples Porta (1) Todas as aplicações que estão enviando e recebendo dados o fazem através da mesma conexão física Portas são usadas para identificar aplicações distintas em uma máquina Elas não são componentes físicos de um computador, mas sim uma abstração Elas são identificadas por um inteiro com 2 bytes de comprimento (varia de 0 a 65535) Caso todas as portas de uma máquina estejam ocupadas, nenhuma conexão adicional poderá ser estabelecida até que uma porta seja liberada 3
Porta (2) Servidores Web são configurados para aceitar conexões através da porta 80 (padrão http) Dessa forma, o endereço de uma aplicação na Web é definido pelo endereço IP do servidor acrescido do número da porta Por exemplo, o servidor Web da PUC-Rio pode ser acessado através da URL www.puc-rio.br:80 Um servidor Web aceita várias conexões, pois após a conexão ter sido estabelecida, o servidor redireciona o cliente para outra porta, liberando a porta usada na conexão inicial Em Java usa-se threads distintas para tratar múltiplas conexões Programa Capítulo 18 Sockets OLadoServidor O Lado Cliente Múltiplos Clientes 4
Servidor (1) Um servidor de chat deve, inicialmente, abrir uma porta e escutá-la até que um cliente solicite conexão import java.net.*; public class Servidor { public static void main(string args[]) throws IOException { ServerSocket servidor = new ServerSocket(12345); System.out.println("Porta 12345 aberta!"); // a continuação do servidor deve ser escrita aqui A criação de um ServerSocket significa que a porta 12345 estava fechada e foi aberta Caso a porta já estivesse em uso uma exceção seria levantada Servidor (2) Após a porta ter sido aberta é necessário aguardar por pedidos de conexões de clientes Isso é feito executando-se o método accept() da classe ServerSocket, que irá bloquear a thread Assim que um cliente se conectar a thread será liberada Socket cliente = servidor.accept(); System.out.println("Nova conexão com o cliente " + cliente.getinetaddress().gethostaddress()); // exibeo ip do cliente 5
Servidor (3) O próximo passo é ler os dados enviados pelo cliente Scanner in = new Scanner(cliente.getInputStream()); while (in.hasnextline()) { System.out.println(in.nextLine()); Por último é necessário fechar a conexão, começando pelo stream de dados in.close(); cliente.close(); servidor.close(); Servidor Versão Final import java.net.*; import java.io.*; import java.util.*; public class Servidor { public static void main(string[] args) throws IOException { ServerSocket servidor = new ServerSocket(12345); System.out.println("Porta 12345 aberta!"); Socket cliente = servidor.accept(); System.out.println("Nova conexão com o cliente " + cliente.getinetaddress().gethostaddress()); Scanner in = new Scanner(cliente.getInputStream()); while (in.hasnextline()) System.out.println(in.nextLine()); in.close(); servidor.close(); cliente.close(); System.out.println("O servidor terminou de executar!"); 6
Programa Capítulo 18 Sockets OLadoServidor O Lado Cliente Múltiplos Clientes Cliente (1) O objetivo agora é criar um programa cliente, usado para enviar mensagens para o servidor O trecho de código a seguir tenta se conectar a um servidor no IP 127.0.0.1 (localhost) porta 12345 Socket cli = new Socket("127.0.0.1",12345); System.out.println("O cliente se conectou ao servidor!"); 7
Cliente (2) Para enviar mensagens para o servidor basta ler as linhas de texto que o usuário digitar, através do buffer de entrada (in), e enviá-las para o buffer de saída PrintStream saida = new PrintStream(cli.getOutputStream()); String msg = teclado.nextline(); while (msg.compareto("###")!=0) { saida.println(msg); msg = teclado.nextline(); saida.close(); teclado.close(); cli.close(); Para testar o sistema é preciso rodar primeiro o servidor e, logo depois, o cliente Tudo o que for digitado no cliente será enviado para o servidor Cliente Versão Final import java.net.*; import java.io.*; import java.util.*; public class Cliente { public static void main(string[] args) throws UnknownHostException, IOException { Socket cli = new Socket("127.0.0.1", 12345); System.out.println("O cliente se conectou ao servidor!"); Scanner teclado = new Scanner(System.in); PrintStream saida = new PrintStream(cli.getOutputStream()); String msg = teclado.nextline(); while (msg.compareto("###")!=0) { saida.println(msg); msg = teclado.nextline(); saida.close(); teclado.close(); cli.close(); System.out.println("O cliente terminou de executar!"); 8
Programa Capítulo 18 Sockets OLadoServidor O Lado Cliente Múltiplos Clientes Múltiplos Clientes Para que o servidor seja capaz de trabalhar com dois ou mais clientes simultaneamente é necessário criar uma thread logo após executar o método accept() A thread criada será responsável pelo tratamento dessa conexão, enquanto o laço do servidor disponibilizará a porta para uma nova conexão ServerSocket servidor = new ServerSocket(12345); // servidor fica eternamente aceitando clientes... while (true) { Socket cliente = servidor.accept(); // dispara uma thread que trata esse cliente e // já espera o próximo 9
Broadcasting de Mensagens (1) Agora que vários clientes podem enviar mensagens, seria interessante que um cliente recebesse as mensagens enviadas pelos outros Logo, é preciso manter uma lista dos clientes conectados e, quando chegar uma mensagem, percorrer essa lista para repassar a mensagem para os demais clientes Uma solução é usar uma lista (List) para guardar os PrintStreams dos clientes Isto é, logo após o servidor aceitar um cliente novo, crie um PrintStream, usando o OutputStream do cliente, e o adicione à lista Quando uma nova mensagem for recebida, envie-a para os demais clientes pertencentes à lista. Broadcasting de Mensagens (2) Adição de um cliente à lista: while (true) { Socket cliente = servidor.accept(); this.lista.add(new PrintStream(cliente.getOutputStream())); // dispara uma thread que trata esse cliente e // já espera o próximo Método que distribui as mensagens: void distribuimensagem(string msg) { for (PrintStream out_cli : lista) { out_cli.println(msg); 10
Broadcasting de Mensagens (3) Como um cliente também recebe mensagens, é preciso fazer com que ele também possa receber mensagens de outros clientes enviadas pelo servidor Ou seja, precisamos de uma segunda thread no cliente, que fique recebendo mensagens do InputStream do servidor e exibindo-as no console Scanner in_serv = new Scanner(cli.getInputStream()); while (in_serv.hasnextline()) { System.out.println(in_serv.nextLine()); Broadcasting de Mensagens (4) Serão necessárias, no mínimo, duas threads para o cliente e duas para o servidor Logo, será necessário criar 4 classes O primeiro dado enviado pelo cliente deve ser o seu nickname Quando o servidor repassar uma mensagem ele deve acrescentar o nickname do remetente à mesma Analise a possibilidade de serializar objetos para enviá-los por uma conexão 11