Serveurs TCP

Principes

Principe serveur TCP

Comme pour UDP, un serveur doit écouter sur un port particulier (sur lequel les clients le solliciteront).

Contrairement à UDP, il faut que le serveur "accepte" la connexion d'un client avant de pouvoir en recevoir les données.

Donc, pour traiter un client, il faut :

  • accepter la connexion
  • communiquer avec lui via cette connexion
  • fermer la connexion

Le serveur survit à la fermeture de la connexion du client.

TCP coté serveur

La socket serveur TCP est représentée par un objet de la classe ServerSocketChannel.

// create the server socket
var serverSocketChannel = ServerSocketChannel.open();

// and bind it to its listening port
serverSocketChannel.bind(new InetSocketAddress(port));

La ServerSocketChannel est la porte d'entrée du serveur. C'est là que les clients viennent "frapper" pour établir une connexion.

Acceptation des connexions

L'attente d'une connexion cliente par accept est bloquante par défaut : un client est accepté quand la méthode retourne.

Ce client est représenté par une SocketChannel qui s'utilise de manière symétrique sur le serveur et sur le client.

while (...) {
  // waiting for incoming client connection
  SocketChannel client = ssc.accept();

  // Serve the client... 

  // then close its connexion this client
  client.close();
}

On peut fermer (définitivement) la socket serveur par l'appel à la méthode ssc.close().

Gestion des exceptions

Attention : si la communication avec un client lève une exception qui n'est pas capturée... elle "tue" le serveur !

while(!Thread.interrupted()) {
    SocketChannel client = serverSocketChannel.accept();
    try {
        logger.info("Connection accepted from " + client.getRemoteAddress());
        serve(client);
    } catch (IOException ioe) {
        logger.log(Level.INFO,"Connection terminated by IOException ", ioe.getCause());
    } catch (InterruptedException ie) {
        logger.info("Server interrupted");
        break;
    } finally {
        silentlyClose(client);
    }
}
Si l'appel à accept() lève une Exception, celle-ci est propagée.

Clients successifs

Pendant que le serveur s'occupe d'un premier client, il ne cherche pas à en accepter un deuxième.

Mais, si un autre client tente de se connecter, le système d'exploitation peut l'accepter, cependant il ne sera pas pris en compte par l'application.

On parle de connexion pendante.
Elle sera prise en compte par le prochain appel à accept().

Le nombre de connexions pendantes est borné par le système : au delà, il refuse d'en accepter de nouvelles.

Exemple (1/17)

Exemple (2/17)

Exemple (3/17)

Exemple (4/17)

Exemple (5/17)

Exemple (6/17)

Exemple (7/17)

Exemple (8/17)

Exemple (9/17)

Exemple (10/17)

Exemple (11/17)

Exemple (1/17)

Exemple (13/17)

Exemple (14/17)

Exemple (15/17)

Exemple (16/17)

Exemple (17/17)

Serveurs concurrents

Pour traiter plusieurs clients simultanément, on peut utiliser plusieurs threads.

Dans l'absolu, il faut :

  • un thread pour lire et un thread pour écrire, par client
    (2 sens de communication) ;
  • plus un thread pour accepter les nouveaux clients.

Au delà d'un certain nombre de clients, ce n'est pas raisonnable !

Serveur concurrent : un thread d'acceptation et deux threads par client