質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

ソケット

TCP/IPにおいて、IPアドレスとサブアドレスであるポート番号を組み合わせたネットワークアドレスのことを呼びます。また、ソフトウェアアプリケーションにおいて、TCP/IP通信を行う為の仮想的なインターフェースという意味もある。

ネットワーク

ネットワークとは、複数のコンピューター間を接続する技術です。インターネットが最も主流なネットワークの形態で、TCP/IP・HTTP・DNSなどの様々なプロトコルや、ルータやサーバーなどの様々な機器の上に成り立っています。

Q&A

解決済

2回答

5548閲覧

Javaのソケット通信におけるオブジェクトの受け渡しと値型データの受け渡しの両立

yukiamezaki

総合スコア13

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

ソケット

TCP/IPにおいて、IPアドレスとサブアドレスであるポート番号を組み合わせたネットワークアドレスのことを呼びます。また、ソフトウェアアプリケーションにおいて、TCP/IP通信を行う為の仮想的なインターフェースという意味もある。

ネットワーク

ネットワークとは、複数のコンピューター間を接続する技術です。インターネットが最も主流なネットワークの形態で、TCP/IP・HTTP・DNSなどの様々なプロトコルや、ルータやサーバーなどの様々な機器の上に成り立っています。

0グッド

0クリップ

投稿2019/04/23 02:47

前提・実現したいこと

javaを使ってソケット通信を用いたプログラムを書いています。クライアントとサーバ間の通信において、単なる文字列の受け渡しだけでなく文字列の配列の受け渡しを実現したいです。

発生している問題・エラーメッセージ

以下に示す、クライアントが送信した文字列をそのままサーバが返すプログラムを実行したところ、ObjectInputStreamクラスのインスタンスを作成するところでプログラムがそれ以上実行されなくなりました。(中断されたわけではありません)
上記の現象の後、サーバ側でプログラムを中断したところJabberClient側には以下のメッセージが表示されました。

Exception in thread "main" java.net.SocketException: Connection reset at java.base/java.net.SocketInputStream.read(Unknown Source) at java.base/java.net.SocketInputStream.read(Unknown Source) at java.base/java.io.ObjectInputStream$PeekInputStream.read(Unknown Source) at java.base/java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source) at java.base/java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source) at java.base/java.io.ObjectInputStream.readStreamHeader(Unknown Source) at java.base/java.io.ObjectInputStream.<init>(Unknown Source) at JabberClient.main(JabberClient.java:13)

該当のソースコード

JabberClient.java

1import java.io.*; 2import java.net.*; 3 4public class JabberClient{ 5 6 public static void main(String[] args) throws IOException{ 7 InetAddress addr = InetAddress.getByName("hostname"); 8 System.out.println("addr = " + addr); 9 Socket socket = new Socket(addr, 8080); 10 try{ 11 System.out.println("socket = " + socket); 12 BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 13 ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); //13行目。この一行が実行されないままプログラムの実行状態が続く 14 PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true); 15 16 for(int i = 0; i < 10; i++){ 17 out.println("howdy " + i); 18 String str = in.readLine(); 19 System.out.println(str); 20 } 21 out.println("END"); 22 23 } 24 finally{ 25 System.out.println("closing..."); 26 socket.close(); 27 } 28 } 29}

JabberServer.java

1import java.io.*; 2import java.net.*; 3 4public class JabberServer{ 5 public static int PORT; 6 7 public static void main(String[] args) throws IOException{ 8 PORT = Integer.parseInt(args[0]); 9 ServerSocket s = new ServerSocket(PORT); 10 System.out.println("Started: " + s); 11 try{ 12 Socket socket = s.accept(); 13 try{ 14 System.out.println("Connection accepted:" + socket); 15 BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 16 17 PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true); 18 19 while(true){ 20 String str = in.readLine(); 21 if(str.equals("END")) break; 22 System.out.println("Echoing : "); 23 out.println(str); 24 } 25 26 } 27 finally{ 28 System.out.println("closing..."); 29 socket.close(); 30 } 31 } 32 finally{ 33 s.close(); 34 } 35 } 36} 37

試したこと

ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());を
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());に書き換えたところ、
意図せぬ文字列がサーバから返ってきました。これをもとにgetInputStreamやgetOutputStreamを複数回呼び出すことで何か不都合が生じているのかとあたりを付けて調べてみましたが、参考になりそうな文献を発見できませんでした。

補足情報(FW/ツールのバージョンなど)

上記の置換を行った際の実行結果

addr = DESKTOP-V3E0T82/192.168.56.1 socket = Socket[addr=DESKTOP-V3E0T82/192.168.56.1,port=8080,localport=56731] ャ? howdy 0 howdy 1 howdy 2 howdy 3 howdy 4 howdy 5 howdy 6 howdy 7 howdy 8 howdy 9 closing...

13行目全体をコメントアウトした際の実行結果(ObjectInputStream,ObjectOutputStreamクラスのインスタンスを生成しなかった場合)

addr = DESKTOP-V3E0T82/192.168.56.1 socket = Socket[addr=DESKTOP-V3E0T82/192.168.56.1,port=8080,localport=56727] howdy 0 howdy 1 howdy 2 howdy 3 howdy 4 howdy 5 howdy 6 howdy 7 howdy 8 howdy 9 closing...

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ObjectInputStreamクラスのインスタンスを作成するところでプログラムがそれ以上実行されなくなりました。

ObjectInputStream のコンストラクタAPI仕様でブロックすると記載されてます。

https://docs.oracle.com/javase/jp/8/docs/api/java/io/ObjectInputStream.html#ObjectInputStream-java.io.InputStream-

このコンストラクタは、対応するObjectOutputStreamがヘッダーを書き込んでフラッシュするまでブロックします。

https://docs.oracle.com/javase/jp/8/docs/api/java/io/ObjectOutputStream.html#ObjectOutputStream-java.io.OutputStream-

このコンストラクタは、直列化ストリーム・ヘッダーをベースとなるストリームに書き込みます。このストリームは呼出し側でただちにフラッシュしてください。ObjectInputStreamsを受け取ったコンストラクタは、ヘッダーを読み込むときにブロックするためです。

https://docs.oracle.com/javase/jp/8/docs/technotes/guides/serialization/index.html


意図せぬ文字列がサーバから返ってきました。

DataOutputStream のコンストラクタで直列化ストリーム・ヘッダーが出力されるためと思われ。

投稿2019/04/23 05:42

wtokuno

総合スコア448

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

socket.getInputStream()
で返されるソケットの入力ストリームに対して「BufferedReaderとしてもDataInputStreamとしても扱う」という意図で

Java

1BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 2ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); //13行目。この一行が実行されないままプログラムの実行状態が続く

とやっておられるのだろうと思いますがそういうことはできません。
「そういうこと」とは「同一のベースストリームを用いて複数の別のストリームインスタンスを生成しようとすること」を指します。

なぜObjectInputStreamの生成でスレッドがブロックしたか詳細な原因はわかりませんけど、いずれにせよこの2つのストリームが「ベースストリームからどのタイミングでどのようなバイト列をリードしようとするか」は実装に左右されるわけで、その実装を意識すべきでないアプリケーションから特定のストリームを異なるストリームのベースストリームとして共用しようとしてもダメとは言えると思います。

例えばServer側のソケットを

クライアントから受信: 文字ストリームで
クライアントへの送信: Objectストリームで

と仮定するならクライアント側では

サーバーから受信: Objectストリームで
サーバーへの送信: 文字ストリームで

のように双方で「特定の入出力ストリームに対して常に一貫した形式で送受信を行う」ようにすればよいと思います。

ちなみにクライアント側でObjectInputStreamを用いているのにサーバー側ではObjectOutputStreamを用いずPrintWriterとして扱ってますね。そもそもこれ自体も矛盾してます。

投稿2019/04/23 05:19

KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yukiamezaki

2019/04/23 05:47

単一のストリームを複数の意図(値型とオブジェクト)で使おうとするとそれを防ぐように制御が働くみたいですね。であればObjectOutputStreamクラスのインスタンスを生成しようとしたときにブロックされない理由が少し不思議ですが、その意図では機能しないこと自体は確かに自然ですね。 上に載せたコードは問題部分を抽出した簡易的なコードですが、手元のコードでは二次元座標をString[]オブジェクトとして渡すことになっているので、単一のコマンドを送信するときは要素数1のString[]オブジェクトとしてデータを送受信するようにしようと思います。 回答いただきありがとうございました。
KSwordOfHaste

2019/04/23 06:23

ObjectInputStreamの実装を少し見ると「最初に4バイトのマジックナンバーとバージョン番号を読もうとする」のがわかります。これがwtokumoさん回答にあるリファレンスがいわんとしていることなのでしょう。いずれにせよダメであることにはかわりないのでサーバー・クライアント双方で一貫した形式での送受信が必要という点はかわりありません。 ちなみにサーバー・クライアント間が「何を送受信するか」について意識を合わせればよいので、常に1要素のString[]を送受信する必要はありません。サーバーへ 単一のコマンド文字列を送信する場合は writeObject("command"); 文字列の配列を送信する場合は writeObject(new String[] { "a", "b" }); としたってかまわないわけです。サーバー側で Object req = in.readObject(); if (req instanceof String) {  // コマンド文字列だった } else if (req instanceof String[]) { // 文字列配列のデータだった } のように実装すれば受信したデータの型によって処理を分岐できます。Objectストリームは任意のクラスのインスタンスをやり取りできるものですのでこのような実装をするのは普通と言えましょう。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問