O que é um socket?
De uma forma muito simples e rápida (visto isto ser matéria que diz respeito a Comunicações por Computador), um socket é uma das pontas de um canal de comunicação entre duas máquinas.
Exercício 1: Servidor de eco
Para implementar o servidor de eco vamos usar a classe java.net.ServerSocket, que serve para receber conexões externas com o método accept. Este método devolve um objecto do tipo java.net.Socket que comunica com o cliente cuja conexão foi aceite com accept.
public class EchoServer {
    public void start() throws IOException {
        ServerSocket server = new ServerSocket(5000);   // Iniciar o servidor
                                                     // à escuta na porta 5000
        Socket client = server.accept();    // Aceitar uma ligação
        while (true) {
            serve(client);  // Servir este cliente
            client = server.accept();   // Aceitar uma ligação nova
        }
    }
    private void serve(Socket client) throws IOException {
        // ...
    }
}
Para efectuar qualquer tipo de comunicação temos à nossa disposição duas streams: uma para enviar dados (output) e outra para receber dados (input). Como queremos enviar e receber linhas de texto, não vamos utilizar estas streams directamente, porque o seu interface é bastante primitivo (só permite o uso directo de arrays de bytes). Vamos embrulhar estas streams em objectos que disponibilizam métodos que trabalham com strings: BufferedReader e PrintWriter:
    private BufferedReader getSocketReader(Socket client) throws IOException {
        return new BufferedReader(new InputStreamReader(client.getInputStream()));
    }
    private PrintWriter getSocketWriter(Socket client) throws IOException {
        return new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
    }
Com isto, para servir o cliente basta passar linhas da stream de input para a de output:
    private void pump(BufferedReader reader, PrintWriter writer) throws IOException {
        String line = reader.readLine();
        while (line != null) {    // Repetir enquanto existirem linhas para ler
            writer.println(line);
            writer.flush();    // Necessário para forçar envio
            line = reader.readLine();
        }
    }
A chamada a flush() é necessária porque PrintWriter usa um buffer interno para minimizar o número de operações de escrita. Se pedirmos para escrever uma linha pequena o buffer não enche e essa linha só será enviada quando o buffer acumular linhas suficientes para encher. Ao chamar flush(), estamos a forçar a escrita e a linha é enviada imediatamente.
Só falta agora escrever o método serve. Com todas as suas partes preparadas isto é bastante simples:
    private void serve(Socket client) throws IOException {
        try {
            BufferedReader reader = getSocketReader(client);
            PrintWriter writer = getSocketWriter(client);
            pump(reader, writer);
        } finally {
            client.close();
        }
    }
Reparem no uso de try-finally para garantir que a ligação é fechada tanto em caso de sucesso ou de falha.
Testar
Para testar isto podemos usar o seguinte método main:
    public static void main(String[] args) {
        try {
            new EchoServer().start();
        } catch (IOException ex) {
            logger.log(Level.SEVERE, "Ooops", ex);
        }
    }
E, conforme sugerido no enunciado, usamos o comando telnet como cliente:
$ telnet localhost 5000
Olá!
Olá!
Yupeee! Tá a funcionar!
Yupeee! Tá a funcionar!

1 commentários:
Obrigado pela massagem inicial!
A ver se consigo chegar ao chat até ao dia do teste.
Enviar um comentário