Maison  >  Article  >  Java  >  Explication détaillée des principes selon lesquels Java implémente le mécanisme de communication par socket natif

Explication détaillée des principes selon lesquels Java implémente le mécanisme de communication par socket natif

黄舟
黄舟original
2017-08-20 09:10:401801parcourir

Cet article présente principalement le principe d'implémentation du mécanisme de communication par socket natif en JAVA. L'éditeur pense que c'est plutôt bien, je vais donc le partager avec vous maintenant et le donner comme référence. Suivons l'éditeur et jetons un coup d'œil.

Cet article présente le principe du mécanisme de communication de socket natif en JAVA et le partage avec tout le monde. Les détails sont les suivants :

Environnement actuel.

jdk == 1.8

Points de connaissance

  • Traitement des connexions de socket

  • Traitement des flux d'entrée et de sortie IO

  • Traitement du format de données de demande

  • Optimisation du modèle de demande

Scénario

Aujourd'hui, parlons des problèmes de communication des sockets en JAVA. Nous prenons ici comme exemple le modèle le plus simple, une demande, une réponse, en supposant que nous devons maintenant communiquer avec le site Baidu. Comment pouvons-nous utiliser le socket natif de JAVA pour y parvenir ?

Établir une connexion socket

Tout d'abord, nous devons établir une connexion socket (code principal)


import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
    
// 初始化 socket
Socket socket = new Socket();
// 初始化远程连接地址
SocketAddress remote = new InetSocketAddress(host, port);
// 建立连接
socket.connect(remote);

Traitement des flux d'entrée et de sortie de socket

Après avoir réussi à établir une connexion de socket, nous pouvons obtenir ses flux d'entrée et de sortie. L'essence de la communication est le traitement. des flux d’entrée et de sortie. Grâce au flux d'entrée, les données de la connexion réseau sont lues et via le flux de sortie, les données locales sont envoyées à l'extrémité distante.

La connexion socket est en fait quelque peu similaire au traitement des flux de fichiers, les deux effectuent des opérations d'E/S.

Le code d'obtention des flux d'entrée et de sortie est le suivant :


// 输入流
InputStream in = socket.getInputStream();
// 输出流
OutputStream out = socket.getOutputStream();

Concernant le traitement des flux IO, nous utilisons généralement les classes de packaging correspondantes pour traiter les flux IO, s'ils sont traités directement, nous devons opérer sur byte[], ce qui est relativement fastidieux. Si nous utilisons une classe wrapper, nous pouvons la traiter directement avec des types tels que string et int, ce qui simplifie les opérations sur les octets IO.

est traité ci-dessous en utilisant BufferedReader et PrintWriter comme classes d'empaquetage d'entrée et de sortie.


// 获取 socket 输入流
private BufferedReader getReader(Socket socket) throws IOException {
  InputStream in = socket.getInputStream();
  return new BufferedReader(new InputStreamReader(in));
}

// 获取 socket 输出流
private PrintWriter getWriter(Socket socket) throws IOException {
  OutputStream out = socket.getOutputStream();
  return new PrintWriter(new OutputStreamWriter(out));
}

Demande et réponse de données

Avec la connexion par socket et le flux d'entrée et de sortie IO, les éléments suivants devraient être fait Envoyer les données de la demande à et obtenir les résultats de la réponse à la demande.

Avec la prise en charge de la classe d'empaquetage IO, nous pouvons transmettre directement au format chaîne, et la classe d'empaquetage nous aidera à convertir les données en flux d'octets correspondant.

Étant donné que nous accédons au site baidu via HTTP, nous n'avons pas besoin de définir de formats de sortie supplémentaires. En utilisant le format de transmission HTTP standard, vous pouvez effectuer des réponses aux requêtes (certains frameworks RPC spécifiques peuvent avoir des formats de communication personnalisés).

Le contenu des données demandées est traité comme suit :


public class HttpUtil {

  public static String compositeRequest(String host){

    return "GET / HTTP/1.1\r\n" +
        "Host: " + host + "\r\n" +
        "User-Agent: curl/7.43.0\r\n" +
        "Accept: */*\r\n\r\n";
  }
  
}

Le code d'envoi des données de la demande est le suivant :


// 发起请求
PrintWriter writer = getWriter(socket);
writer.write(HttpUtil.compositeRequest(host));
writer.flush();
接收响应数据代码如下:

// 读取响应
String msg;
BufferedReader reader = getReader(socket);
while ((msg = reader.readLine()) != null){
  System.out.println(msg);
}

Jusqu'à présent, nous avons fini de parler de tous les codes de base pour créer des connexions, envoyer des requêtes et recevoir des réponses sous des sockets natifs.

Le code complet est le suivant :


import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import com.test.network.util.HttpUtil;

public class SocketHttpClient {

  public void start(String host, int port) {

    // 初始化 socket
    Socket socket = new Socket();

    try {
      // 设置 socket 连接
      SocketAddress remote = new InetSocketAddress(host, port);
      socket.setSoTimeout(5000);
      socket.connect(remote);

      // 发起请求
      PrintWriter writer = getWriter(socket);
      System.out.println(HttpUtil.compositeRequest(host));
      writer.write(HttpUtil.compositeRequest(host));
      writer.flush();

      // 读取响应
      String msg;
      BufferedReader reader = getReader(socket);
      while ((msg = reader.readLine()) != null){
        System.out.println(msg);
      }

    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        socket.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

  }

  private BufferedReader getReader(Socket socket) throws IOException {
    InputStream in = socket.getInputStream();
    return new BufferedReader(new InputStreamReader(in));
  }

  private PrintWriter getWriter(Socket socket) throws IOException {
    OutputStream out = socket.getOutputStream();
    return new PrintWriter(new OutputStreamWriter(out));
  }

}

Ci-dessous, nous montrons les résultats de la communication socket en instanciant un client.


public class Application {

  public static void main(String[] args) {

    new SocketHttpClient().start("www.baidu.com", 80);

  }
}

Sortie du résultat :

Optimisation du modèle de demande

De cette façon, même s'il n'y a aucun problème pour réaliser la fonction. Mais lorsque nous y regardons de plus près, nous constatons que le blocage des E/S se produit pendant le processus d'écriture et de lecture des E/S. Soit :


// 会发生 IO 阻塞
writer.write(HttpUtil.compositeRequest(host));
reader.readLine();

Donc si vous souhaitez demander 10 sites différents en même temps, comme suit :


public class SingleThreadApplication {

  public static void main(String[] args) {

    // HttpConstant.HOSTS 为 站点集合
    for (String host: HttpConstant.HOSTS) {

      new SocketHttpClient().start(host, HttpConstant.PORT);

    }

  }
}

Il doit compléter la première réponse à la demande avant de lancer le traitement suivant du site.

Cela est plus évident côté serveur. Bien que le code ici soit une connexion client, les opérations spécifiques sont similaires à celles côté serveur. Les demandes ne peuvent être traitées qu'en série une par une, ce qui ne respectera certainement pas la norme de temps de réponse.

  • Multi-threading

Certaines personnes pensent que ce n'est pas du tout un problème, JAVA est un langage de programmation multi-thread. Dans cette situation, un modèle multithread est le plus approprié.


public class MultiThreadApplication {

  public static void main(String[] args) {

    for (final String host: HttpConstant.HOSTS) {

      Thread t = new Thread(new Runnable() {
        public void run() {
          new SocketHttpClient().start(host, HttpConstant.PORT);
        }
      });

      t.start();

    }
  }
}

Cette méthode semble utile au début, mais lorsque la quantité de concurrence est importante, l'application utilisera beaucoup de threads. Comme nous le savons tous, sur le serveur, chaque thread occupe en fait un descripteur de fichier. Le nombre de handles sur le serveur est limité et un grand nombre de threads entraînera une consommation considérable de commutation entre les threads. Par conséquent, cette méthode doit être insupportable dans les scénarios à forte concurrence.

  • Traitement multi-thread + pool de threads

Comme trop de threads ne suffisent pas, nous pouvons simplement contrôler le nombre de threads créés. Seul un nombre fixe de threads est démarré pour le traitement des sockets, qui utilise non seulement le traitement multithread, mais contrôle également la consommation des ressources du système.


public class ThreadPoolApplication {

  public static void main(String[] args) {

    ExecutorService executorService = Executors.newFixedThreadPool(8);

    for (final String host: HttpConstant.HOSTS) {

      Thread t = new Thread(new Runnable() {
        public void run() {
          new SocketHttpClient().start(host, HttpConstant.PORT);
        }
      });

      executorService.submit(t);
      new SocketHttpClient().start(host, HttpConstant.PORT);

    }

  }
}

Concernant le nombre de threads démarrés, généralement le type gourmand en CPU sera fixé à N+1 (N est le nombre de cœurs CPU), et le Le type intensif en IO sera défini sur 2N + 1 .

Cette méthode semble être la meilleure. Y a-t-il quelque chose de mieux ? Si un thread peut gérer plusieurs connexions de socket en même temps et ne se bloque pas lorsque les données d’entrée et de sortie de chaque socket ne sont pas prêtes, est-ce mieux ? Cette technologie est appelée « multiplexage IO ». L'implémentation correspondante est fournie dans le package nio de JAVA.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn