9. Código-fonte dos programas
9.1. O cliente genérico TCP
Muitos dos serviços criados nos primórdios da Internet funcionam de acordo com o modelo do servidor de eco estudado anteriormente: as trocas cliente-servidor realizam-se através da troca de linhas de texto. Vamos escrever um cliente TCP genérico que será iniciado da seguinte forma: java cltTCPgenerique servidor porta
Este cliente TCP irá ligar-se à porta port do servidor serveur. Feito isto, irá criar duas threads:
- um thread encarregado de ler os comandos digitados no teclado e enviá-los para o servidor
- um thread encarregado de ler as respostas do servidor e de as apresentar no ecrã
Porquê duas threads? Cada serviço TCP-IP tem o seu protocolo específico e, por vezes, verificam-se as seguintes situações:
- o cliente tem de enviar várias linhas de texto antes de obter uma resposta
- a resposta de um servidor pode conter várias linhas de texto
Por isso, o ciclo de envio de uma única linha para o servidor — receção de uma única linha enviada pelo servidor — nem sempre é adequado. Vamos, portanto, criar dois ciclos separados:
- um ciclo de leitura dos comandos digitados no teclado para serem enviados ao servidor. O utilizador indicará o fim dos comandos com a palavra-chave fin.
- um ciclo de receção e exibição das respostas do servidor. Este será um ciclo infinito que só será interrompido pelo encerramento do fluxo de rede pelo servidor ou pelo utilizador, que, no teclado, digitará o comando fin.
Para que estes dois ciclos funcionem de forma independente, precisamos de dois threads independentes. Vejamos um exemplo de execução em que o nosso cliente TCP genérico se liga a um serviço SMTP (Protocolo de Transferência SendMail). Este serviço é responsável pelo encaminhamento do correio eletrónico aos seus destinatários. Funciona na porta 25 e utiliza um protocolo de comunicação do tipo troca de linhas de texto.
Dos>java clientTCPgenerique istia.univ-angers.fr 25
Commandes :
<-- 220 istia.univ-angers.fr ESMTP Sendmail 8.11.6/8.9.3; Mon, 13 May 2002 08:37:26 +0200
help
<-- 502 5.3.0 Sendmail 8.11.6 -- HELP not implemented
mail from: machin@univ-angers.fr
<-- 250 2.1.0 machin@univ-angers.fr... Sender ok
rcpt to: serge.tahe@istia.univ-angers.fr
<-- 250 2.1.5 serge.tahe@istia.univ-angers.fr... Recipient ok
data
<-- 354 Enter mail, end with "." on a line by itself
Subject: test
ligne1
ligne2
ligne3
.
<-- 250 2.0.0 g4D6bks25951 Message accepted for delivery
quit
<-- 221 2.0.0 istia.univ-angers.fr closing connection
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
Vamos comentar estas trocas entre cliente e servidor:
- o serviço SMTP envia uma mensagem de boas-vindas quando um cliente se liga a ele:
- alguns serviços dispõem de um comando help que fornece indicações sobre os comandos que podem ser utilizados com o serviço. Neste caso, não é esse o caso. Os comandos SMTP utilizados no exemplo são os seguintes:
- mail from: expéditeur, para indicar o endereço de e-mail do remetente da mensagem
- rcpt to: destinataire, para indicar o endereço de e-mail do destinatário da mensagem. Se houver vários destinatários, o comando rcpt to: é repetido tantas vezes quantas forem necessárias para cada um dos destinatários.
- data, que sinaliza ao servidor SMTP que se vai enviar a mensagem. Conforme indicado na resposta do servidor, esta consiste numa sequência de linhas terminada por uma linha que contém apenas o caractere ponto. Uma mensagem pode ter cabeçalhos separados do corpo da mensagem por uma linha em branco. No nosso exemplo, colocámos um assunto com a palavra-chave Subject:
- assim que a mensagem for enviada, é possível indicar ao servidor que terminámos com o comando quit. O servidor encerra então a ligação de rede. O thread de leitura pode detetar este evento e parar.
- O utilizador digita então «fin» no teclado para parar também o thread de leitura dos comandos digitados no teclado.
Se verificarmos o e-mail recebido, temos o seguinte (Outlook):

Note-se que o serviço SMTP não consegue detetar se um remetente é válido ou não. Por isso, nunca se pode confiar no campo from de uma mensagem. Neste caso, o remetente machin@univ-angers.fr não existia.
Este cliente TCP genérico permite-nos descobrir o protocolo de comunicação dos serviços da Internet e, a partir daí, criar classes especializadas para os clientes desses serviços. Vamos descobrir o protocolo de comunicação do serviço POP (Post Office Protocol), que permite recuperar os e-mails armazenados num servidor. Funciona na porta 110.
Dos> java clientTCPgenerique istia.univ-angers.fr 110
Commandes :
<-- +OK Qpopper (version 4.0.3) at istia.univ-angers.fr starting.
help
<-- -ERR Unknown command: "help".
user st
<-- +OK Password required for st.
pass monpassword
<-- +OK st has 157 visible messages (0 hidden) in 11755927 octets.
list
<-- +OK 157 visible messages (11755927 octets)
<-- 1 892847
<-- 2 171661
...
<-- 156 2843
<-- 157 2796
<-- .
retr 157
<-- +OK 2796 octets
<-- Received: from lagaffe.univ-angers.fr (lagaffe.univ-angers.fr [193.49.144.1])
<-- by istia.univ-angers.fr (8.11.6/8.9.3) with ESMTP id g4D6wZs26600;
<-- Mon, 13 May 2002 08:58:35 +0200
<-- Received: from jaume ([193.49.146.242])
<-- by lagaffe.univ-angers.fr (8.11.1/8.11.2/GeO20000215) with SMTP id g4D6wSd37691;
<-- Mon, 13 May 2002 08:58:28 +0200 (CEST)
...
<-- ------------------------------------------------------------------------
<-- NOC-RENATER2 Tl. : 0800 77 47 95
<-- Fax : (+33) 01 40 78 64 00 , Email : noc-r2@cssi.renater.fr
<-- ------------------------------------------------------------------------
<--
<-- .
quit
<-- +OK Pop server at istia.univ-angers.fr signing off.
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
Os principais comandos são os seguintes:
- user login, onde se introduz o nome de utilizador na máquina que armazena os nossos e-mails
- pass password, onde se introduz a palavra-passe associada ao nome de utilizador anterior
- list, para obter a lista de mensagens com o número e o tamanho em bytes
- retr i, para ler a mensagem n.º i
- quit, para encerrar a sessão.
Vamos agora descobrir o protocolo de comunicação entre um cliente e um servidor Web, que normalmente opera na porta 80:
Dos> java clientTCPgenerique istia.univ-angers.fr 80
Commandes :
GET /index.html HTTP/1.0
<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix) (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
<--
<-- <head>
<-- <meta http-equiv="Content-Type"
<-- content="text/html; charset=iso-8859-1">
<-- <meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
<-- <title>Bienvenue a l'ISTIA - Universite d'Angers</title>
<-- </head>
....
<-- face="Verdana"> - Dernire mise jour le <b>10 janvier 2002</b></font></p>
<-- </body>
<-- </html>
<--
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
Um cliente Web envia os seus comandos ao servidor de acordo com o seguinte esquema:
Só depois de receber a linha vazia é que o servidor Web responde. No exemplo, utilizámos apenas um comando:
que solicita ao servidor o URL /index.html e indica que está a trabalhar com o protocolo HTTP versão 1.0. A versão mais recente deste protocolo é a 1.1. O exemplo mostra que o servidor respondeu devolvendo o conteúdo do ficheiro index.html e, em seguida, encerrou a ligação, uma vez que se observa que o thread de leitura das respostas terminou. Antes de enviar o conteúdo do ficheiro index.html, o servidor web enviou uma série de cabeçalhos terminada por uma linha em branco:
<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix) (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
A linha <html> é a primeira linha do ficheiro /index.html. O que precede denomina-se cabeçalhos HTTP (Protocolo de Transferência HyperText). Não vamos detalhar aqui estes cabeçalhos, mas convém lembrar que o nosso cliente genérico permite o acesso aos mesmos, o que pode ser útil para os compreender. A primeira linha, por exemplo:
indica que o servidor Web contactado compreende o protocolo HTTP/1.1 e que encontrou o ficheiro solicitado (200 OK), sendo 200 um código de resposta HTTP. As linhas
indicam ao cliente que irá receber 11251 bytes correspondentes ao texto HTML (HyperText Markup Language) e que, no final da transmissão, a ligação será encerrada.
Temos, portanto, aqui um cliente TCP muito prático. É certo que faz menos do que o programa telnet que utilizámos anteriormente, mas foi interessante escrevê-lo nós próprios. O programa do cliente TCP genérico é o seguinte:
// pacotes importados
import java.io.*;
import java.net.*;
public class clientTCPgenerique{
// recebe como parâmetro as características de um serviço na forma
// servidor porta
// liga-se ao serviço
// cria um thread para ler os comandos digitados no teclado
// estas serão enviadas para o servidor
// cria um thread para ler as respostas do servidor
// estas serão apresentadas no ecrã
// tudo termina com o comando «fin» digitado no teclado
// variável de instância
private static Socket client;
public static void main(String[] args){
// sintaxe
final String syntaxe="pg serveur port";
// número de argumentos
if(args.length != 2)
erreur(syntaxe,1);
// anota-se o nome do servidor
String serveur=args[0];
// a porta deve ser um número inteiro >0
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(args[1]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
client=null;
// podem ocorrer problemas
try{
// estabelece-se a ligação ao serviço
client=new Socket(serveur,port);
}catch(Exception ex){
// erro
erreur("Impossible de se connecter au service ("+ serveur
+","+port+"), erreur : "+ex.getMessage(),3);
// fim
return;
}//catch
// criam-se os threads de leitura/gravação
new ClientSend(client).start();
new ClientReceive(client).start();
// fim do thread principal
return;
}// main
// exibição de erros
public static void erreur(String msg, int exitCode){
// exibição de erro
System.err.println(msg);
// encerramento com erro
System.exit(exitCode);
}//erro
}//classe
class ClientSend extends Thread {
// classe responsável por ler os comandos digitados no teclado
// e de as enviar para um servidor através de um cliente TCP passado como parâmetro
private Socket client; // o cliente TCP
// construtor
public ClientSend(Socket client){
// onde se indica o cliente TCP
this.client=client;
}//construtor
// método Run da thread
public void run(){
// dados locais
PrintWriter OUT=null; // fluxo de escrita de rede
BufferedReader IN=null; // fluxo do teclado
String commande=null; // comando lido no teclado
// gestão de erros
try{
// criação do fluxo de escrita de rede
OUT=new PrintWriter(client.getOutputStream(),true);
// criação do fluxo de entrada do teclado
IN=new BufferedReader(new InputStreamReader(System.in));
// ciclo de introdução e envio de comandos
System.out.println("Commandes : ");
while(true){
// leitura do comando digitado no teclado
commande=IN.readLine().trim();
// Concluído?
if (commande.toLowerCase().equals("fin")) break;
// envio do comando para o servidor
OUT.println(commande);
// próximo comando
}//while
}catch(Exception ex){
// erro
System.err.println("Envoi : L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// fim - fecham-se os fluxos
try{
OUT.close();client.close();
}catch(Exception ex){}
// assinala-se o fim do thread
System.out.println("[Envoi : fin du thread d'envoi des commandes au serveur]");
}//execução
}//classe
class ClientReceive extends Thread{
// classe responsável por ler as linhas de texto destinadas a um
// cliente TCP passado como parâmetro
private Socket client; // o cliente TCP
// construtor
public ClientReceive(Socket client){
// regista-se o cliente TCP
this.client=client;
}//construtor
// método Run da thread
public void run(){
// dados locais
BufferedReader IN=null; // fluxo de leitura de rede
String réponse=null; // resposta do servidor
// gestão de erros
try{
// criação do fluxo de leitura de rede
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
// ciclo de leitura das linhas de texto do fluxo IN
while(true){
// leitura do fluxo de rede
réponse=IN.readLine();
// fluxo encerrado?
if(réponse==null) break;
// exibição
System.out.println("<-- "+réponse);
}//while
}catch(Exception ex){
// erro
System.err.println("Réception : L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// fim - fecham-se os fluxos
try{
IN.close();client.close();
}catch(Exception ex){}
// assinala-se o fim do thread
System.out.println("[Réception : fin du thread de lecture des réponses du serveur]");
}//execução
}//classe
9.2. O servidor TCP genérico
Agora vamos centrar-nos num servidor
- que exibe no ecrã os comandos enviados pelos seus clientes
- e lhes envia, como resposta, as linhas de texto digitadas no teclado por um utilizador. É, portanto, este último que desempenha a função de servidor.
O programa é iniciado com: java serveurTCPgenerique portEcoute, em que portEcoute é a porta à qual os clientes devem ligar-se. O serviço ao cliente será assegurado por duas threads:
- um thread dedicado exclusivamente à leitura das linhas de texto enviadas pelo cliente
- um thread dedicado exclusivamente à leitura das respostas digitadas pelo utilizador. Este indicará, através do comando «fin», que encerra a ligação com o cliente.
O servidor cria duas threads por cliente. Se houver n clientes, haverá 2n threads ativas ao mesmo tempo. O servidor, por sua vez, nunca se encerra, a não ser que o utilizador prima Ctrl-C no teclado. Vejamos alguns exemplos.
O servidor é iniciado na porta 100 e utiliza-se o cliente genérico para comunicar com ele. A janela do cliente é a seguinte:
E:\data\serge\MSNET\c#\rede\cliente tcp genérico> java clientTCPgenerique localhost 100
Commandes :
commande 1 du client 1
<-- réponse 1 au client 1
commande 2 du client 1
<-- réponse 2 au client 1
fin
L'erreur suivante s'est produite : Impossible de lire les données de la connexion de transport.
[fin du thread de lecture des réponses du serveur]
[fin du thread d'envoi des commandes au serveur]
As linhas que começam por <-- são as enviadas do servidor para o cliente; as restantes são as enviadas do cliente para o servidor. A janela do servidor é a seguinte:
Dos> java serveurTCPgenerique 100
Serveur générique lancé sur le port 100
Thread de lecture des réponses du serveur au client 1 lancé
1 : Thread de lecture des demandes du client 1 lancé
<-- commande 1 du client 1
réponse 1 au client 1
1 : <-- commande 2 du client 1
réponse 2 au client 1
1 : [fin du Thread de lecture des demandes du client 1]
fin
[fin du Thread de lecture des réponses du serveur au client 1]
As linhas que começam por <-- são as enviadas do cliente para o servidor. As linhas N: são as enviadas do servidor para o cliente n.º N. O servidor acima ainda está ativo, embora o cliente 1 já tenha terminado. Inicia-se um segundo cliente para o mesmo servidor:
Dos> java clientTCPgenerique localhost 100
Commandes :
commande 3 du client 2
<-- réponse 3 au client 2
fin
L'erreur suivante s'est produite : Impossible de lire les données de la connexion de transport.
[fin du thread de lecture des réponses du serveur]
[fin du thread d'envoi des commandes au serveur]
A janela do servidor fica então assim:
Dos> java serveurTCPgenerique 100
Serveur générique lancé sur le port 100
Thread de lecture des réponses du serveur au client 1 lancé
1 : Thread de lecture des demandes du client 1 lancé
<-- commande 1 du client 1
réponse 1 au client 1
1 : <-- commande 2 du client 1
réponse 2 au client 1
1 : [fin du Thread de lecture des demandes du client 1]
fin
[fin du Thread de lecture des réponses du serveur au client 1]
Thread de lecture des réponses du serveur au client 2 lancé
2 : Thread de lecture des demandes du client 2 lancé
<-- commande 3 du client 2
réponse 3 au client 2
2 : [fin du Thread de lecture des demandes du client 2]
fin
[fin du Thread de lecture des réponses du serveur au client 2]
^C
Vamos agora simular um servidor web, iniciando o nosso servidor genérico na porta 88:
Dos> java serveurTCPgenerique 88
Serveur générique lancé sur le port 88
Vamos agora abrir um navegador e aceder à página http://localhost:88/exemple.html. O navegador irá então ligar-se à porta 88 da máquina localhost e, em seguida, solicitar a página /exemple.html:

Vejamos agora a janela do nosso servidor:
Dos>java serveurTCPgenerique 88
Serveur générique lancé sur le port 88
Thread de lecture des réponses du serveur au client 2 lancé
2 : Thread de lecture des demandes du client 2 lancé
<-- GET /exemple.html HTTP/1.1
<-- Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword, */*
<-- Accept-Language: fr
<-- Accept-Encoding: gzip, deflate
<-- User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705; .NET CLR 1.0.2
914)
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--
Descobrimos assim os cabeçalhos HTTP enviados pelo navegador. Isto permite-nos descobrir, pouco a pouco, o protocolo HTTP. Num exemplo anterior, tínhamos criado um cliente Web que enviava apenas o comando GET. Isso tinha sido suficiente. Vemos aqui que o navegador envia outras informações ao servidor. O objetivo destas é indicar ao servidor que tipo de cliente tem diante de si. Vemos também que os cabeçalhos HTTP terminam com uma linha em branco.
Vamos elaborar uma resposta para o nosso cliente. O utilizador ao teclado é, neste caso, o verdadeiro servidor e pode elaborar uma resposta manualmente. Recordemos a resposta dada por um servidor Web num exemplo anterior:
<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix) (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
Vamos tentar dar uma resposta semelhante:
...
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--
2 : HTTP/1.1 200 OK
2 : Server: serveur tcp generique
2 : Connection: close
2 : Content-Type: text/html
2 :
2 : <html>
2 : <head><title>Serveur generique</title></head>
2 : <body>
2 : <center>
2 : <h2>Reponse du serveur generique</h2>
2 : </center>
2 : </body>
2 : </html>
2 : fin
L'erreur suivante s'est produite : Impossible de lire les données de la connexion de transport.
[fin du Thread de lecture des demandes du client 2]
[fin du Thread de lecture des réponses du serveur au client 2]
As linhas que começam por 2: são enviadas do servidor para o cliente n.º 2. O comando fin encerra a ligação do servidor ao cliente. Limitaram-nos, na nossa resposta, aos seguintes cabeçalhos HTTP:
HTTP/1.1 200 OK
2 : Server: serveur tcp generique
2 : Connection: close
2 : Content-Type: text/html
2 :
Não indicamos o tamanho do ficheiro que vamos enviar (Content-Length), mas limitamo-nos a indicar que vamos encerrar a ligação (Connection: close) após o envio do mesmo. Isso é suficiente para o navegador. Ao verificar que a ligação foi encerrada, o navegador saberá que a resposta do servidor está concluída e apresentará a página HTML que lhe foi enviada. Esta página é a seguinte:
2 : <html>
2 : <head><title>Serveur generique</title></head>
2 : <body>
2 : <center>
2 : <h2>Reponse du serveur generique</h2>
2 : </center>
2 : </body>
2 : </html>
Em seguida, o utilizador encerra a ligação ao cliente digitando o comando fin. O navegador fica então a saber que a resposta do servidor está concluída e pode, assim, apresentá-la:

Se, no exemplo acima, executarmos o comando View/Source para ver o que o navegador recebeu, obtemos:

ou seja, exatamente o que foi enviado a partir do servidor genérico.
O código do servidor TCP genérico é o seguinte:
// pacotes
import java.io.*;
import java.net.*;
public class serveurTCPgenerique{
// programa principal
public static void main (String[] args){
// recebe as solicitações dos clientes na porta de escuta
// cria um thread para ler os pedidos do cliente
// estas serão apresentadas no ecrã
// cria um thread para ler os comandos digitados no teclado
// estas serão enviadas como resposta ao cliente
// tudo termina com o comando «fin» digitado no teclado
final String syntaxe="Syntaxe : pg port";
// variável de instância
// existe algum argumento
if(args.length != 1)
erreur(syntaxe,1);
// a porta deve ser um número inteiro >0
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(args[0]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
// criamos o serviço de escuta
ServerSocket ecoute=null;
int nbClients=0; // número de clientes processados
try{
// Cria-se o serviço
ecoute=new ServerSocket(port);
// acompanhamento
System.out.println("Serveur générique lancé sur le port " + port);
// Ciclo de atendimento aos clientes
Socket client=null;
while (true){ // ciclo infinito — será interrompido com Ctrl-C
// aguarda um cliente
client=ecoute.accept();
// o serviço é executado em threads separadas
nbClients++;
// são criados os threads de leitura/escrita
new ServeurSend(client,nbClients).start();
new ServeurReceive(client,nbClients).start();
// volta-se a ficar à escuta de pedidos
}// fim do while
}catch(Exception ex){
// reporta-se o erro
erreur("L'erreur suivante s'est produite : " + ex.getMessage(),3);
}//catch
}// fim de main
// exibição dos erros
public static void erreur(String msg, int exitCode){
// exibição do erro
System.err.println(msg);
// paragem com erro
System.exit(exitCode);
}//erro
}//classe
class ServeurSend extends Thread{
// classe responsável por ler as respostas digitadas no teclado
// e de as enviar a um cliente através de um cliente TCP passado ao construtor
Socket client; // o cliente TCP
int numClient; // n.º do cliente
// construtor
public ServeurSend(Socket client, int numClient){
// regista-se o cliente TCP
this.client=client;
// e o seu n.º
this.numClient=numClient;
}//fabricante
// método Run do thread
public void run(){
// dados locais
PrintWriter OUT=null; // fluxo de escrita na rede
String réponse=null; // resposta lida no teclado
BufferedReader IN=null; // fluxo do teclado
// acompanhamento
System.out.println("Thread de lecture des réponses du serveur au client "+ numClient + " lancé");
// gestão de erros
try{
// criação do fluxo de escrita de rede
OUT=new PrintWriter(client.getOutputStream(),true);
// criação do fluxo do teclado
IN=new BufferedReader(new InputStreamReader(System.in));
// ciclo de introdução e envio de comandos
while(true){
// identificação do cliente
System.out.print("--> " + numClient + " : ");
// leitura da resposta digitada no teclado
réponse=IN.readLine().trim();
// Concluído?
if (réponse.toLowerCase().equals("fin")) break;
// envio da resposta para o servidor
OUT.println(réponse);
// resposta seguinte
}//enquanto
}catch(Exception ex){
// erro
System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// fim - fecham-se os fluxos
try{
OUT.close();client.close();
}catch(Exception ex){}
// assinala-se o fim do thread
System.out.println("[fin du Thread de lecture des réponses du serveur au client "+ numClient+ "]");
}//execução
}//classe
class ServeurReceive extends Thread{
// classe responsável por ler as linhas de texto enviadas ao servidor
// através de um cliente TCP passado ao construtor
Socket client; // o cliente TCP
int numClient; // n.º do cliente
// construtor
public ServeurReceive(Socket client, int numClient){
// regista-se o cliente TCP
this.client=client;
// e o seu n.º
this.numClient=numClient;
}//fabricante
// método Run do thread
public void run(){
// dados locais
BufferedReader IN=null; // fluxo de leitura de rede
String réponse=null; // resposta do servidor
// acompanhamento
System.out.println("Thread de lecture des demandes du client "+ numClient + " lancé");
// gestão de erros
try{
// criação do fluxo de leitura da rede
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
// ciclo de leitura das linhas de texto do fluxo IN
while(true){
// leitura do fluxo de rede
réponse=IN.readLine();
// fluxo encerrado?
if(réponse==null) break;
// exibição
System.out.println("<-- "+réponse);
}//while
}catch(Exception ex){
// erro
System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// fim - fecham-se os fluxos
try{
IN.close();client.close();
}catch(Exception ex){}
// assinala-se o fim do thread
System.out.println("[fin du Thread de lecture des demandes du client "+ numClient+"]");
}//execução
}//classe