TCP接続
TCP の基本は Socket です。TCP 接続では、クライアントとサーバーが接続を確立した後は、基本的に I/O の制御を行います。
まず、クライアントとサーバーに分けられる単純な TCP 通信を見てみましょう。
クライアントのコードは次のとおりです。
public static void main(String[] args) が IOException をスローする
{
ソケットソケット = null;
BufferedReader br = null;
PrintWriter pw = null;
BufferedReader brTemp = null;
試す
{
ソケット = 新しいソケット(InetAddress.getLocalHost(), 5678);
br = 新しい BufferedReader(新しい InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream());
brTemp = 新しい BufferedReader(新しい InputStreamReader(System.in));
その間(真)
{
文字列行 = brTemp.readLine();
pw.println(行);
pw.flush();
if (line.equals("end")) ブレーク;
System.out.println(br.readLine());
}
}
catch(例外例)
{
System.err.println(ex.getMessage());
}
ついに
{
if (ソケット != null) ソケット.close();
if (br != null) br.close();
if (brTemp != null) brTemp.close();
if (pw != null) pw.close();
}
}
}
public static void main(String[] args) が IOException をスローする
{
ServerSocket サーバー = null;
ソケットクライアント = null;
BufferedReader br = null;
PrintWriter pw = null;
試す
{
サーバー = 新しいサーバーソケット(5678);
クライアント = サーバー.accept();
br = 新しい BufferedReader(新しい InputStreamReader(client.getInputStream()));
pw = 新しい PrintWriter(client.getOutputStream());
その間(真)
{
文字列行 = br.readLine();
pw.println("応答:" + line);
pw.flush();
if (line.equals("end")) ブレーク;
}
}
catch(例外例)
{
System.err.println(ex.getMessage());
}
ついに
{
if (サーバー != null) server.close();
if (クライアント != null) client.close();
if (br != null) br.close();
if (pw != null) pw.close();
}
}
}
上記のコードは基本的に、TCP 通信プロセス中のクライアントとサーバーの主要なフレームワークを概説しています。上記のコードでは、サーバーは常に 1 つのクライアントからのリクエストしか処理できないことがわかります。これは、クライアントのリクエストが受信されたときに、サーバーにマルチスレッドを追加して、対応するリクエストを処理する方法とは異なります。
改善されたサーバー側のコードは次のとおりです。
クラス ServerThread は Thread を拡張します
{
プライベートソケットソケット = null;
public ServerThread(ソケットソケット)
{
this.socket = ソケット;
}
public void run() {
BufferedReader br = null;
PrintWriter pw = null;
試す
{
br = 新しい BufferedReader(新しい InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream());
その間(真)
{
文字列行 = br.readLine();
pw.println("応答:" + line);
pw.flush();
if (line.equals("end")) ブレーク;
}
}
catch(例外例)
{
System.err.println(ex.getMessage());
}
ついに
{
if (ソケット != null)
試す {
ソケット.クローズ();
キャッチ (IOException e1) {
e1.printStackTrace();
}
if (br != null)
試す {
br.close();
} キャッチ (IOException e) {
e.printStackTrace();
}
if (pw != null) pw.close();
}
}
}
プログラミングのプロセスでは、「リソース」という概念があります。たとえば、データベース接続は、パフォーマンスを向上させるために、通常、データベース接続を直接破棄せず、複数のデータベース接続を管理します。データベースは管理され、再利用されます。ソケット接続の場合、プログラムで多数のソケット接続が必要な場合、各接続を再確立する必要がある場合、これは非常に非効率なアプローチになります。
データベース接続プールと同様に、TCP 接続プールも設計できます。ここでの考え方は、配列を使用して複数のソケット接続を維持し、別のステータス配列を使用して、プログラムがソケット接続を必要とするときに各ソケット接続が使用されているかどうかを記述することです。ソケット接続。ステータス配列を調べて、最初の未使用のソケット接続を取り出します。すべての接続が使用中の場合、例外がスローされます。これは非常に直感的でシンプルな「スケジューリング戦略」であり、多くのオープンソースまたは商用フレームワーク (Apache/Tomcat) には同様の「リソース プール」があります。
TCP 接続プールのコードは次のとおりです。
プライベート InetAddress アドレス = null;
プライベート int ポート。
private Socket[] arrSockets = null;
プライベートブール[] arrStatus = null;
プライベート int カウント;
public TcpConnectionPool(InetAddress アドレス、int ポート、int カウント)
{
this.address = 住所;
this.port = ポート;
この .count = カウント;
arrSockets = 新しいソケット[数];
arrStatus = 新しいブール値[カウント];
init();
}
プライベート void init()
{
試す
{
for (int i = 0; i < count; i++)
{
arrSockets[i] = 新しいソケット(アドレス.getHostAddress(), ポート);
arrStatus[i] = false;
}
}
catch(例外例)
{
System.err.println(ex.getMessage());
}
}
パブリックソケット getConnection()
{
if (arrSockets == null) init();
int i = 0;
for(i = 0; i < count; i++)
{
if (arrStatus[i] == false)
{
arrStatus[i] = true;
壊す;
}
}
if (i == count) throw new RuntimeException("現在利用可能な接続がありません。");
arrSockets[i] を返します。
}
public void releaseConnection(ソケットソケット)
{
if (arrSockets == null) init();
for (int i = 0; i < count; i++)
{
if (arrSockets[i] == ソケット)
{
arrStatus[i] = false;
壊す;
}
}
}
public void reBuild()
{
init();
}
パブリック void destroy()
{
if (arrSockets == null) が返される。
for(int i = 0; i < count; i++)
{
試す
{
arrSockets[i].close();
}
catch(例外例)
{
System.err.println(ex.getMessage());
続く;
}
}
}
}
UDP は TCP とは異なる接続方法であり、通常、オンライン ビデオなど、高いリアルタイム パフォーマンスと低い精度が必要な状況で使用されます。 UDP では「パケットロス」が発生します。サーバーが起動していないと、クライアントがメッセージを送信するときに例外が報告されますが、UDP では例外は生成されません。
UDP 通信に使用される 2 つのクラスは DatagramSocket と DatagramPacket で、後者は通信の内容を格納します。
以下は単純な UDP 通信の例で、TCP と同様にクライアントとサーバーの 2 つの部分に分かれています。クライアントのコードは次のとおりです。
public static void main(String[] args)
{
試す
{
InetAddress ホスト = InetAddress.getLocalHost();
int ポート = 5678;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
その間(真)
{
文字列行 = br.readLine();
byte[] メッセージ = line.getBytes();
DatagramPacket パケット = 新しい DatagramPacket(メッセージ、メッセージの長さ、ホスト、ポート);
DatagramSocket ソケット = new DatagramSocket();
ソケット.send(パケット);
ソケット.クローズ();
if (line.equals("end")) ブレーク;
}
br.close();
}
catch(例外例)
{
System.err.println(ex.getMessage());
}
}
}
public static void main(String[] args)
{
試す
{
int ポート = 5678;
DatagramSocket dsSocket = 新しい DatagramSocket(ポート);
byte[] バッファ = 新しいバイト [1024];
DatagramPacket パケット = 新しい DatagramPacket(buffer,buffer.length);
その間(真)
{
dsSocket.receive(パケット);
文字列メッセージ = new String(buffer, 0, packet.getLength());
System.out.println(packet.getAddress().getHostName() + ":" + message);
if (message.equals("end")) ブレーク;
packet.setLength(buffer.length);
}
dsSocket.close();
}
catch(例外例)
{
System.err.println(ex.getMessage());
}
}
}
マルチキャストは、UDP と同様の方法を使用し、クラス D IP アドレスと標準の UDP ポート番号を使用します。224.0.0.0 から 239.255.255.255 までのアドレスを指します。
マルチキャストで使用されるクラスは MulticastSocket ですが、これには、joinGroup と LeaveGroup という 2 つの注目すべきメソッドがあります。
マルチキャストの例は次のとおりです。
public static void main(String[] args)
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
試す
{
InetAddress アドレス = InetAddress.getByName("230.0.0.1");
int ポート = 5678;
その間(真)
{
文字列行 = br.readLine();
byte[] メッセージ = line.getBytes();
DatagramPacket パケット = 新しい DatagramPacket(メッセージ、メッセージの長さ、アドレス、ポート);
マルチキャストソケット multicastSocket = new MulticastSocket();
multicastSocket.send(パケット);
if (line.equals("end")) ブレーク;
}
br.close();
}
catch(例外例)
{
System.err.println(ex.getMessage());
}
}
}
public static void main(String[] args)
{
int ポート = 5678;
試す
{
MulticastSocket multicastSocket = 新しい MulticastSocket(ポート);
InetAddress アドレス = InetAddress.getByName("230.0.0.1");
multicastSocket.joinGroup(アドレス);
byte[] バッファ = 新しいバイト [1024];
DatagramPacket パケット = 新しい DatagramPacket(buffer,buffer.length);
その間(真)
{
multicastSocket.receive(パケット);
文字列メッセージ = new String(buffer, packet.getLength());
System.out.println(packet.getAddress().getHostName() + ":" + message);
if (message.equals("end")) ブレーク;
packet.setLength(buffer.length);
}
multicastSocket.close();
}
catch(例外例)
{
System.err.println(ex.getMessage());
}
}
}
NIO は、JDK1.4 で導入された新しい IO API セットで、バッファ管理、ネットワーク通信、ファイル アクセス、および文字セット操作において新しい設計が施されています。ネットワーク通信の場合、NIO はバッファーとチャネルの概念を使用します。
以下は NIO の例ですが、上で説明したコーディング スタイルとは大きく異なります。
public static void main(String[] args)
{
文字列ホスト = "127.0.0.1";
int ポート = 5678;
SocketChannel チャネル = null;
試す
{
InetSocketAddress アドレス = 新しい InetSocketAddress(ホスト,ポート);
文字セット charset = Charset.forName("UTF-8");
CharsetDecoder デコーダ = charset.newDecoder();
CharsetEncoder エンコーダ = charset.newEncoder();
ByteBuffer バッファ = ByteBuffer.allocate(1024);
CharBuffer charBuffer = CharBuffer.allocate(1024);
チャネル = SocketChannel.open();
チャンネル.接続(アドレス);
文字列リクエスト = "GET / /r/n/r/n";
channel.write(encoder.encode(CharBuffer.wrap(request)));
while((channel.read(buffer)) != -1)
{
バッファ.フリップ();
decoder.decode(buffer, charBuffer, false);
charBuffer.flip();
System.out.println(charBuffer);
バッファ.クリア();
charBuffer.clear();
}
}
catch(例外例)
{
System.err.println(ex.getMessage());
}
ついに
{
if (チャンネル != null)
試す {
チャンネル.close();
} キャッチ (IOException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
}
}
}