こんにちは。明月です。
この投稿はJavaでネットワーク通信(Socket)をする方法に関する説明です。
プログラムでソケットと言えばプログラムとプログラムまたはPCとPC間に通信をするという意味です。
簡単に思えば通信する時に伝送するパケット(データ)がパソコンのLANカードによってランケーブルに伝送します。ランケーブルに伝送したデータはDNSとルータなどを通って到達しようとPCのLANカードによって最終に目標したプログラムでパケット(データ)を読み込みます。端末と端末の間にデータを通信します。
この時、我々は各端末間にデータ変換や装置間のプロトコール、規約などに関して実装してないです。この通信規約に関してはすべてOS側で設定して(OSI7階層)、我々はその上で差し込んで使うという意味でSocket通信という言います。
link - OSI参照モデル
Socket通信規約は処理プロシージャが決まっています。
先に、通信接続を待つ側をサーバという言います。サーバはPortを開いてクライアントの接続を待ちます。そして接続する側をクライアントという言います。クライアントがサーバのIPとPortに接続したら通信が開始します。
サーバとクライアント間の通信はSend、Receiveの形式でデータを送信、受信します。そして通信が終わればcloseで接続を切ります。
そのSocketの概念でJavaでソケット(Socket)通信を作成みましょう。
先にはサーバを作成してWindowのTelentプログラムを利用して接続を確認します。確認できたらその仕様に合わせてClientを作成しましょう。
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// サーバクラス
public class Server {
// バッファーサイズ設定
private final static int BUFFER_SIZE = 1024;
// 実行関数
public static void main(String[] args) {
// サーバインスタンス生成(プログラムが終了する時に自動にcloseが呼び出す。)
try (ServerSocket server = new ServerSocket()) {
// 9999ポートでサーバを待つ。
InetSocketAddress ipep = new InetSocketAddress(9999);
// サーバインスタンスにソケット情報をbind
server.bind(ipep);
// コンソール出力
System.out.println("Initialize complate");
// クライアントからメッセージを待つスレッドプール
ExecutorService receiver = Executors.newCachedThreadPool();
// クライアントリスト
List<Socket> list = new ArrayList<>();
// サーバは無限待機
while (true) {
try {
// クライアントから接続待機
Socket client = server.accept();
// クライアントリストに追加
list.add(client);
// 接続情報をコンソールに出力
System.out.println("Client connected IP address =" + client.getRemoteSocketAddress().toString());
// クライアントスレッドプールを開始
receiver.execute(() -> {
// clientが終了すればソケットをcloseする
// OutputStreamとInputStreamを受け取る。
try (Socket thisClient = client;
OutputStream send = client.getOutputStream();
InputStream recv = client.getInputStream();) {
// メッセージを作成
String msg = "Welcome server!\r\n>";
// byte変換
byte[] b = msg.getBytes();
// クライアントに伝送
send.write(b);
// バッファー
StringBuffer sb = new StringBuffer();
// メッセージを待機ループ
while (true) {
// バッファー生成
b = new byte[BUFFER_SIZE];
// メッセージを受け取る。
recv.read(b, 0, b.length);
// byteをStringに変換
msg = new String(b);
// バッファーにメッセージ追加
sb.append(msg.replace("\0", ""));
// メッセージが改行の場合(クライアントからエンターを打った場合)
if (sb.length() > 2 && sb.charAt(sb.length() - 2) == '\r' && sb.charAt(sb.length() - 1) == '\n') {
// メッセージをStringに変換
msg = sb.toString();
// バッファーをクリア
sb.setLength(0);
// メッセージをコンソールに出力
System.out.println(msg);
// exitメッセージの場合、メッセージループを終了する。
if ("exit\r\n".equals(msg)) {
break;
}
// echoメッセージ作成
msg = "echo : " + msg + ">";
// byteに変換
b = msg.getBytes();
// クライアントに伝送
send.write(b);
}
}
} catch (Throwable e) {
// エラー発生する時、コンソール出力
e.printStackTrace();
} finally {
// 接続が終了すれば接続情報をコンソールに出力
System.out.println("Client disconnected IP address =" + client.getRemoteSocketAddress().toString());
}
});
} catch (Throwable e) {
// エラー発生する時、コンソール出力
e.printStackTrace();
}
}
} catch (Throwable e) {
// エラー発生する時、コンソール出力
e.printStackTrace();
}
}
}
上のacceptはwhile(true)の無限ループに入れてクライアントを待機します。
クライアント接続すればスレッドプールにSocketを渡してクライアントからメッセージを待機します。
ここからSocketのStreamを受け取ってwrite、readを使うことになりますが、IOと同じロジックです。
link - [Java] 26. ファイル(IO)を扱う方法(ファイル作成、ファイル修正、アクセス日付変更とIOをclose(リソース返却)する理由、Closableインタフェース)
起動すればコンソールにInitialize completeメッセージがコンソールに出力してListenの状態になります。
Windowコンソールからtelnetに接続してメッセージを送信しましょう。
telnetで127.0.0.1 9999に接続してhello worldを打ったらechoメッセージが受信することを確認できます。また、exitを打ったら接続が終了します。
サーバを確認すればクライアントが接続してメッセージを受け取って終了することまで確認できます。
サーバは完了しました。このサーバの仕様でクライアントを作成しましょう。
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// クライアントクラス
public class Client {
// バッファーサイズ設定
private final static int BUFFER_SIZE = 1024;
// 実行関数
public static void main(String[] args) {
// サーバインスタンス生成(プログラムが終了する時に自動にcloseが呼び出す。)
try (Socket client = new Socket()) {
// ロカール:9999ポートのサーバに接続する。
InetSocketAddress ipep = new InetSocketAddress("127.0.0.1", 9999);
// 接続
client.connect(ipep);
// clientが終了すればソケットをcloseする
// OutputStreamとInputStreamを受け取る。
try (OutputStream send = client.getOutputStream();
InputStream recv = client.getInputStream();) {
// コンソールに出力
System.out.println("Client connected IP address =" + client.getRemoteSocketAddress().toString());
// サーバからメッセージを待つスレッドプール
ExecutorService receiver = Executors.newSingleThreadExecutor();
receiver.execute(() -> {
try {
// メッセージの無限待機
while (true) {
// バッファー生成
byte[] b = new byte[BUFFER_SIZE];
// メッセージを受け取る。
recv.read(b, 0, b.length);
// コンソールに出力
System.out.println(new String(b));
}
} catch (Throwable e) {
// エラー発生する時、コンソール出力
e.printStackTrace();
}
});
// コンソールからメッセージを受け取る。
try (Scanner sc = new Scanner(System.in)) {
// コンソールメッセージの無限待機
while (true) {
// メッセージを受け取る。
String msg = sc.next() + "\r\n";
// byte変換
byte[] b = msg.getBytes();
// サーバにメッセージを送信
send.write(b);
// exitの場合に接続終了
if ("exit\r\n".equals(msg)) {
break;
}
}
}
}
} catch (Throwable e) {
// エラー発生する時、コンソール出力
e.printStackTrace();
}
}
}
eclipseからは同時に二つのmainを実行することができないので、サーバはjarファイルでexportしてコンソールから実行します。
これからeclipseからクライアントを実行して接続しましょう。
接続が正常になりました。
メッセージを送ったらechoメッセージも正常に受信します。exitをすればサーバと接続が切れました。その後に正常に終了します。エラーExceptionが発生しましたが、正常終了で発生したものです。
サーバからも正常接続、メッセージ、終了まで確認できます。
サーバとクライアントソースをみれば差異が多くないです。サーバはServerSocketインスタンスを生成して接続すればSocketインスタンスを受け取ります。
クライアントからSocketインスタンスを生成して接続します。つまり、Socketから送信、受信はSocketクラスから行います。
ここまでJavaでネットワーク通信(Socket)をする方法に関する説明でした。
ご不明なところや間違いところがあればコメントしてください。
- [Java] 34. WindowでMariaDBをインストールする方法2019/09/25 19:58:30
- [Java] 33. オープンライブラリを参照する方法(eclipseからmavenを連結)2019/09/24 19:35:54
- [Java] 32. Reflection機能を使う方法(Annotation編)2019/09/24 00:19:25
- [Java] 31. Reflection機能を使う方法(Variable編)2019/09/20 22:34:40
- [Java] 30. Reflection機能を使う方法(Method編)2019/09/19 20:20:10
- [Java] 29. Reflection機能を使う方法(Class編)2019/09/18 20:02:14
- [Java] 28. 文字タイプ(CharacterSet)とエンディアン(endian)で変換する方法2019/09/17 20:22:02
- [Java] 27. ネットワーク通信(Socket)をする方法2019/09/16 23:42:46
- [Java] 26. ファイル(IO)を扱う方法(ファイル作成、ファイル修正、アクセス日付変更とIOをclose(リソース返却)する理由、Closableインタフェース)2019/09/13 20:03:58
- [Java] 25. Objectクラス(notify、waitの使い方)2019/09/13 00:58:31
- [Java] 24. Javaの同期化(Synchronized)とデッドロック(Deadlock)2019/09/11 23:06:09
- [Java] 23. スレッドプール(Threadpool)を使う方法2019/09/10 21:55:36
- [Java] 22.スレッド(Thread)を使う方法2019/09/06 22:30:49
- [Java] 21. アノテーション(Annotation)を使う方法2019/09/05 22:58:20
- [Java] 20. iterator(for-each)とStream APIを使う方法2019/09/04 20:11:28
- check2024/04/10 19:03:53
- [Java] 64.Spring bootとReactを連結する方法(Buildする方法)2022/03/25 21:02:18
- [Javascript] Node.jsをインストールしてReactを使う方法2022/03/23 18:01:34
- [Java] 63. Spring bootでcronスケジューラとComponentアノテーション2022/03/16 18:57:30
- [Java] 62. Spring bootでWeb-Filterを設定する方法(Spring Security)2022/03/15 22:16:37
- [Java] JWT(Json Web Token)を発行、確認する方法2022/03/14 19:12:58
- [Java] 61. Spring bootでRedisデータベースを利用してセッションクラスタリング設定する方法2022/03/01 18:20:52
- [Java] 60. Spring bootでApacheの連結とロードバランシングを設定する方法2022/02/28 18:45:48
- [Java] 59. Spring bootのJPAでEntityManagerを使い方2022/02/25 18:27:48
- [Java] 58. EclipseでSpring bootのJPAを設定する方法2022/02/23 18:11:10
- [Java] 57. EclipseでSpring bootを設定する方法2022/02/22 19:04:49
- [Python] Redisデータベースに接続して使い方2022/02/21 18:23:49
- [Java] Redisデータベースを接続して使い方(Jedisライブラリ)2022/02/16 18:13:17
- [C#] Redisのデータベースを接続して使い方2022/02/15 18:46:09
- [CentOS] Redisデータベースをインストールする方法とコマンドを使い方2022/02/14 18:33:07