プログラムを通じて実際の概念を確立した後、ソケットとは何ですか?という元の質問に戻る必要があります。これはコンピューター通信を実現する方法であることに疑いの余地はありません。しかし、最も理解しやすい言語を使用するにはどうすればよいでしょうか。画像を比較してみてください。その原理について公平な説明はどうでしょうか?
BrUCe Eckel は著書「Java Programming Thoughts」でソケットについて説明しています。
ソケットは、2 つのマシン「ターミナル」間の接続を表現するために使用されるソフトウェアの抽象化です。特定の接続に対して、各マシンにソケットが存在します。あるいは、マシン間に仮想の「ケーブル」があり、「ケーブル」の両端がソケットに差し込まれていると考えることもできます。もちろん、物理的なハードウェアやマシン間のケーブル配線はまったく不明です。抽象化の目的は、私たちが知る必要のない詳細を知ることを防ぐことです
私の理解によれば、抽象的な観点から見ると、ソケットは電話の受信機であり、人です。あなたが話している相手も持っていますが、その中の人は1人だけです。ある人の受信者はServerSocketと呼ばれ、もう1人の受信者はSocketと呼ばれます。誰がServerSocketで誰がSocketであるかは関係ありません。クライアントとサーバーは本質的に相対的であり、お互いに変換できるため、会話している 2 人の人が 2 つの受信機を受信できるかどうかは、両方の受信者が受信機を受信したかどうかによって決まります。片方だけが受信機を取った場合は、ビープ音が聞こえるだけで、チャネルが異なることが確認されます。受信機を取るプロセスは、チャネルが確立された後のソケットの初期化プロセスです。全員が受話器を取った後、チャネルの両端の人々が会話を開始できます。つまり、A が B に話し、B が A に応答し、A が聞きます。この 2 つのプロセスは完了します。この 2 つの回線で送信されるのはストリームです。ストリームは送信の詳細をすべて隠します。これは、サーバー側ではなく音声であると認識させます。以前に作成したプログラムは、実際にはシングルタスク バージョンです。handleConnection では、1 つの接続を検出して処理するため、同じ時間内に 1 つの接続しか処理できません。同時に複数の接続が要求されている場合、そのようなプログラムは 1 つの接続だけを保証できないため、順番に待つことしかできません。クライアントは同時にサーバーへの接続リクエストを行うため、複数のクライアント接続を処理するためにブロッキング メソッドを使用すると速度が低下することが考えられます
これにより、マルチ接続指向のバージョンが誕生しました。明らかに、私たちの要件は次のとおりです。
解決すべき問題はクライアント接続を処理することなので、原理は推測するのは難しくありません。つまり、接続が検出された後です。そのため、handleConnection の元のコードをそのままスレッドの実行コードに組み込み、handleConnection を作成するだけで済みます。新しいスレッドのコードは非常に簡単です
前の記事と同じスタイルで、各部分のコードの詳細を見てみましょう
まず、このマルチスレッド バージョンのクラス MultiThreadRemoteFileServer を作成します
この定義を見てください。 class
import java.io.*;
import java.net.*;
public class MultiThreadRemoteFileServer{
PROtected int listenPort;
public MultiThreadRemoteFileServer(int aListenPort){
}
public static void main(String[] args) {
}
public void acceptConnections() {
}
public void handleConnection(Socket incomingConnection) {
}
}
は RemoteFileServer とほぼ同じです。唯一の違いは、今作成したクラスにコンストラクターが追加されていることです。これは、リスニングポート番号を自分で決定できるようにするために、次のように定義します
public MultithreadedRemoteFileServer(int aListenPort) {
listenPort = aListenPort;
}
まずmain()を見てみましょう
public static void main(String[] args) {
MultithreadedRemoteFileServer server = new MultithreadedRemoteFileServer(3000);
server.acceptConnections();
}
違いはありません。RemoteFileServer の main() 関数と同じですが、ポートが異なります。番号はメインプログラムの作成時に指定されます。
私たちが主に懸念している変更は背面にあります
次に、acceptConnection リスナー プログラムを見てください
public void acceptConnections() {
try {
ServerSocket server = new ServerSocket(listenPort, 5);//サーバーの作成には多くの時間がかかります。 Socket A パラメーターは、同時に適用できる接続の最大数を指定するために使用されます。デフォルト値は 50 です。
Socket incomingConnection = null;
while (true) {
incomingConnection = server .accept();
handleConnection(incomingConnection);
}
} catch (BindException e) {
System.out.println("ポートにバインドできません " + listenPort);
} catch (IOException e) {
System. out.println("ポートで ServerSocket をインスタンス化できません: " + listenPort);
}
}
唯一の変更点はパラメータが 1 つ増えたことです。これがその動作メカニズムです。バックログ値を 5 に指定し、サーバーへの接続を要求しているクライアントが 5 つあるとします。サーバーは最初の接続の処理を開始しますが、その接続の処理には長い時間がかかります。保留中の値は 5 なので、一度に 5 つのリクエストをキューに入れることができます。現在 1 件の作業を進めているということは、他に 5 件が待機していることになります。合計 6 つが待機中で処理されています。サーバーが接続番号 1 の受け入れでまだ忙しいとき (キューにはまだ番号 2 ~ 6 が残っていることに注意してください)、7 番目のクライアントが接続要求を行うと、7 番目のクライアントは拒否されます
それでは、次の変更を見てください。明らかに、監視対象のスレッドを処理するメソッド handleConnection 内にあります。マルチスレッド版では、接続要求を検出すると、すぐにスレッドを生成し、それを無視します。その後、新しいスレッドを作成する文がここにあります。 thread.
public void handleConnection(Socket connectionToHandle) {
new Thread(new ConnectionHandler(connectionToHandle)).start();
}
新しいクラス ConnectionHandler があることに気付きました。このクラスは Runnable、つまりインターフェースクラス (これはインターフェースを使用して実装されたスレッドです。理解できない場合は、17 番のスレッドについて参照してください)。ConnectionHandler を使用して新しいスレッドを作成し、それを開始します。先ほど述べたように、RemoteFileServer の handleConnection 内のコードは、このインターフェイス クラス ConnectionHandler の run() メソッドにそのまま転送されています。
次に、ConnectionHandler クラス全体の定義を見てみましょう。
class ConnectionHandler は Runnable を実装します {
protected SocketソケットToHandle;
public ConnectionHandler(Socket aSocketToHandle) {
socketToHandle = aSocketToHandle;//コンストラクターを通して、パラメータとして処理されるSocketインスタンスを渡します
}
public void run() { //Socket の読み取り/書き込みのための元のコードはすべてここにあります
try {
PrintWriter streamWriter = new PrintWriter(socketToHandle.getOutputStream());
BufferedReader streamReader = new BufferedReader(new InputStreamReader(socketToHandle.getInputStream()));
String fileToRead = streamReader.readLine();
BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead));
String line = null;
while ((line = fileReader.readLine()) != null)
streamWriter 。 println(line);
fileReader.close();
streamWriter.close();
streamReader.close();
} catch (Exception e) {
System.out.println("クライアント処理エラー: " + e);
}
}
}
ConnectionHandler の run() メソッドが行うことは、RemoteFileServer の handleConnection() が行うことと同じです。まず、InputStream と OutputStream (Socket の getOutputStream() と getInputStream() を使用) をそれぞれ BufferedReader と PrintWriter にラップします。次に、これらのコードを使用してターゲット ファイルを 1 行ずつ読み取ります。ファイル パスは InputStream にロードされるため、FileReader ストリームを使用してファイル パスを途中でラップし、BufferedReader パッケージを通じてそれを読み取る必要があります。
マルチスレッド サーバーの調査は終了しました。もう一度、サーバーの「マルチスレッド バージョン」を作成して使用する手順を確認してみましょう。
1. acceptConnections() を変更して、デフォルトの 50 で ServerSocket をインスタンス化します。 (または 1 より大きい任意の指定された数値)。
2. ServerSocket の handleConnection() を変更して、ConnectionHandler のインスタンスを含む新しいスレッドを生成します。
3. RemoteFileServer の handleConnection() メソッドのコードを借用して、ConnectionHandler クラスの run() 関数を実装します。