Les sockets TCP


L'API utilisée pour accéder au protocole TCP se décompose en deux classes, une utilisée par les clients et l'autre par les serveurs.


La classe Socket

La classe Socket est utilisée par les clients TCP.

Pour créer un objet de la classe Socket, il est nécessaire d'utiliser un des constructeurs suivant :

La création d'un objet Socket entraîne la création d'un point de connexion (la socket) et la connexion vers une autre socket (le serveur).
L'adresse du serveur est composée de l'adresse d'une machine (sous forme d'un nom ou d'un objet de la classe InetAddress) et d'un numéro de port.

L'adresse locale et le port local peuvent également être spécifiés.
Par défaut, l'appel au constructeur est bloquant tant que la connexion TCP n'est pas établie.

Une fois la connexion établie, il est possible de récupérer le flux d'entrée et le flux de sortie de la connexion TCP vers le serveur au moyen des méthodes :

Il est alors possible d'echanger des données avec le serveur au moyen de toutes les primitives de lecture et d'écriture des differentes classes du package java.io.

Par défaut les primitives de lecture, tel que read(),  sont bloquantes tant que rien n'est lisible sur le flux.
La primitive suivante permet de définir un temps maximal d'attente :

public synchronized void setSoTimeout(int timeout) throws SocketException

Une fois la connexion terminée il est très important de fermer le flux d'entrée et le flux de sortie, mais aussi la socket au moyen de la méthode :

public synchronized void close() throws IOException

Par défaut cette primitive n'est pas bloquante mais la socket reste ouverte tant qu'il reste des paquets à envoyer (en paticulier le datagramme FIN).
La primitive suivante permet de définir un temps maximum d'attente avant de vraiment fermer la socket.

public void setSoLinger(boolean on,
                        int val) throws SocketException

Par défaut, les implémentations du protocole TCP essaye de remplir au maximum chaque paquet (Nagle algorithm) afin de diminuer le trafic réseau. Pour éviter ce comportement, ce qui est souvent souhaitable dans le cas d'application interactives¸ une primitive est disponible :

public void setTcpNoDelay(boolean on) throws SocketException

D'autres méthodes sont également disponibles et en particulier :

Remarque : une applet peut (pour des questions de sécurité) se connecter uniquement à la machine depuis laquelle elle a été chargée.
Exemple :
package test;

import java.io.*;
import java.net.*;

public class SmtpClient {
  public static void main(String argv[]) throws Exception {
    sendmail("aaa","roussel@univ-mlv.fr");
  }
  static void sendmail(String message, String to) throws Exception  {
    Socket s = new Socket(InetAddress.getByName("pixel.univ-mlv.fr"),25);
    PrintStream output = new PrintStream(s.getOutputStream());
    output.println("HELO qqcvd.univ-mlv.fr\r");
    output.println("MAIL FROM: \r");
    output.println("RCPT TO:<" + to + ">\r");
    output.println("DATA\r");
    output.println(message);
    output.println("\r\n.\r");
  }
}

La classe ServerSocket

Cette classe est utilisée (comme son nom l'indique) pour créer une socket du coté serveur.

La classe ServerSocket permet de créer un point de communication, sur un port particulier, en attente de connexions en provenance de clients. Contrairement à la classe Socket elle n'ouvre pas de connexion.
Pour créer un tel objet, il faut utiliser un des constructeurs suivants, où port correspond au port d'attente et count au nombre maximum de connexions en attente, non encore acceptées.

Une fois la socket créée, on attend les connexions de clients avec la méthode bloquante : Il est possible de spécifier un temps maximal d'attente. Pour cela il faut appeler la méthode suivante avant l'appel à accept() :

public synchronized void setSoTimeout(int timeout) throws SocketException

La méthode accept() retourne un nouvel objet de la classe Socket qui est connecté avec un client particulier, celui qui a demandé la connexion. Il est alors possible de récupérer le flôt d'entrée et de sortie comme pour la socket du client.

Exemple :

public class DaytimeServeur {
  public static void main(String argv[]) throws Exception {
    ServerSocket s = new ServerSocket(0);
    System.out.println(s.getLocalPort());
    while(true) {
      Socket serviceSocket =  s.accept();
      PrintStream output = new PrintStream(serviceSocket.getOutputStream());
      output.println(new Date());
      serviceSocket.close();
    }
  }
}
D'autres méthodes de manipulation sont également disponibles :