現在、以下のようにTCP/IP通信のプログラミングを行っており、
サーバ/クライアント別々に4byteのデータ送信を10msec毎に10秒間行っております。
現在、WimdowsVista-Windows7間で各々をサーバ/クライアントとして順に起動し、
相互に4byte送信しているハズが、倍の8byteや12byteとデータが連なって送信されている
事象が発生してます。
OutputStreamではwrite後にflushを行っているので、flush契機でメモリ上に蓄えられた
送信用バッファが送信されるイメージでおりますが、4byteで送信できていないように見えます。
上記について、解決方法をご存じであればご教授お願い致します。
<Server.java>
=====
public class Server {
public static ServerSocket ss = null;
public static Socket soc = null;
private static InputStream is = null;
private static OutputStream os = null;
public static void main(String[] args) {
try {
// サーバソケット生成
ss = new ServerSocket(5000);
soc = ss.accept();
is = soc.getInputStream();
os = soc.getOutputStream();
Thread rcvTh = new ServerRcvThread(is);
rcvTh.start();
Thread sndTh = new ServerSndThread(os);
sndTh.start();
// 10秒スリープ
try{
Thread.sleep(10000);
} catch ( Exception e){
e.printStackTrace();
}
// スレッド停止
rcvTh.stop();
sndTh.stop();
} catch (IOException e) {
e.printStackTrace();
} finally{
try {
is.close();
os.close();
soc.close();
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class ServerSndThread extends Thread{
private static OutputStream ous = null;
ServerSndThread( OutputStream os ){
this.ous = os;
}
public void run(){
byte sndData[] = new byte[4];
sndData[0] = 0x01;
sndData[1] = 0x02;
sndData[2] = 0x03;
sndData[3] = 0x04;
try {
while(true){
// データ書込み
ous.write(sndData);
ous.flush();
System.out.println("データ送信");
// 0.01秒スリープ
try{
Thread.sleep(10);
} catch ( Exception e){
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ServerRcvThread extends Thread{
private static InputStream ins = null;
ServerRcvThread( InputStream os ){
this.ins = os;
}
public void run(){
byte rcvData[] = new byte[16];
int size = 0;
try {
while(true){
// データ読込み
size = ins.read(rcvData);
System.out.println("size:"+size+"byte");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
=====
<Client.java>
=====
public class Client {
private static Socket soc = null;
private static OutputStream os = null;
private static InputStream is = null;
public static void main(String[] args) {
try {
// ソケット生成
soc = new Socket("192.168.3.3", 5000);
is = soc.getInputStream();
os = soc.getOutputStream();
Thread rcvTh = new ClientRcvThread(is);
rcvTh.start();
Thread sndTh = new ClientSndThread(os);
sndTh.start();
// 10秒スリープ
try{
Thread.sleep(10000);
} catch ( Exception e){
e.printStackTrace();
}
// スレッド停止
rcvTh.stop();
sndTh.stop();
} catch (IOException e) {
e.printStackTrace();
} finally{
try {
is.close();
os.close();
soc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class ClientSndThread extends Thread{
private static OutputStream ous = null;
ClientSndThread( OutputStream os ){
this.ous = os;
}
public void run(){
byte sndData[] = new byte[4];
sndData[0] = 0x04;
sndData[1] = 0x03;
sndData[2] = 0x02;
sndData[3] = 0x01;
try {
while(true){
// データ書込み
ous.write(sndData);
ous.flush();
System.out.println("データ送信");
// 0.01秒スリープ
try{
Thread.sleep(10);
} catch ( Exception e){
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ClientRcvThread extends Thread{
private static InputStream ins = null;
ClientRcvThread( InputStream os ){
this.ins = os;
}
public void run(){
byte rcvData[] = new byte[16];
int size = 0;
try {
while(true){
// データ読込み
size = ins.read(rcvData);
System.out.println("size:"+size+"byte");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
=====
No.4ベストアンサー
- 回答日時:
> なるほどー。
境界が維持されないんですねー。> ということは、パケット内にサイズを保持するとかしないといけないんですかね…。
そうなります。
TCPの機能はJava自体ではなく,OS等が実現しています。
パケット未達時に再送してくれたり,パケットの到着順が入れ替わった場合に並び替えるのもJavaではなくOS等の機能です。
着信系の機能はプロセスを横断して行う必要があるので,UNIXやWindowsといったOSではJavaに任せるのは難しいと思いますし。
# どこまでをOSと呼ぶかやデバイスドライバの実装によって実際の処理箇所が異なるのでOS等と書いています。
なので,Java側でflushしても,デバイス (デバイスドライバ含む) のバッファに転送されるだけかと思います。
さらにNagleアルゴリズムが有効な限り,最大で200ms程度 (規格上は500ms) の間の送信はデバイス側でまとめられます。
今回はACKを載せるパケットがどんどん出来るので,遅延ACKによる遅延がほとんどなく,結果としてNagleアルゴリズムも大きく働いてはいないのだと思います。
# サーバー側が送信を止めた場合,800octetくらいのデータが一気に送受信されるかもしれません。
送信側としては,Nagleアルゴリズムを無効にすることでデータを即座に送信することが出来ます。
ただし,受信側でパケット境界が維持されるとは限りませんし,4octetのデータに20octetのTCPヘッダと20octetのIPv4ヘッダ (IPv6だと40octet) が付随するので,ネットワーク効率を下げる可能性があります。
No.2
- 回答日時:
UDPという意見もありますが、今から組み替えて、到達確認、再送処理を入れ込むのは面倒ですね。
この辺、本番環境で問題になるんですよ。と言いますか、
byte rcvData[] = new byte[16];とやってるのはなぜでしょうか?これは、「最大16バイト取ろう」としていることになります。
size = ins.read(rcvData, 0, 4);とやるのがいいと思いますが。
実際にこのプログラムを動かしてみましたよ。(javacとやったら、なんとjdkインストールしとらんかった!) 8バイト取ることがありましたが、改良後は100秒動かしても4以外はありません・・・。
ちなみに、「OutputStream の flush メソッドは何も行いません。」とAPIドキュメントに書かれてます。APIドキュメントは重要です。
回答ありがとうございます!
サイズ指定しても、8byte となってしまいますね…。
改良って他にも変更されたのでしょうか…?
flushのAPI見ましたが、何もしないとは書かれていないように見えました。
ただ、実際のAPIソースコード見たら、ご指摘通り何にもしない、空APIに見えました。
No.1
- 回答日時:
TCPはパケット境界を維持しないプロトコルです。
送信側での複数のパケットが受信側では1つのパケットになることもありますし,
送信側では1つのパケットだったものが受信側では複数のパケットとなることもあります。
そのため,パケット境界が維持されないことを前提にTCP上にプロトコルを作成するか,UDPなどのパケット境界を維持するプロトコルを使う必要があります。
回答ありがとうございます!
なるほどー。境界が維持されないんですねー。
ということは、パケット内にサイズを保持するとかしないといけないんですかね…。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Java java 入力 3 4 3 出力 ABC DEFG HIJ このようなプログラムの書き方を教えてくだ 2 2022/07/15 14:18
- C言語・C++・C# 大量のデータを読み込んで表示する速度を改善したい 8 2023/05/07 13:29
- Java JavaのSingletonパターンのprivateの持つ意味が分かりません。 5 2022/06/12 10:38
- C言語・C++・C# C#テキストボックスの文字を配列にいれてその後表示する 4 2022/07/17 04:47
- C言語・C++・C# TCP/IP通信時のサーバーからの受信 2 2022/11/23 09:11
- C言語・C++・C# C# DatagridviewにExcelシートを反映するとエラーが出る 2 2023/05/06 17:12
- Java java final 1 2022/06/10 22:49
- Java Java プログラム public class Main { public static void 3 2023/08/10 23:46
- C言語・C++・C# Windows Formアプリからコンソールを呼び出して文字を出力させたい 8 2023/05/09 10:53
- C言語・C++・C# C# で、あるフォルダー内にあるすべてのテキストファイルを別のフォルダーにコピーする。 4 2022/11/21 13:23
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語のよるrsh-clientの簡潔...
-
TCP/IP通信時のサーバーからの受信
-
ソケットのクローズについて
-
Winsockで接続待ちタイムアウト...
-
Winsockを使わずにVBでSocketを...
-
UDP通信におけるbind関数について
-
メールサーバへの接続タイムア...
-
ソケットのrecvの戻り値が0
-
WinSockでチャット
-
Socket通信の0バイト受信について
-
WinSockでの通信プログラムがう...
-
Java(クライアント)とC言語(サ...
-
ソケットプログラミングに関す...
-
ポート番号0について
-
WINSOCKのWSACleanupについて
-
UdpClient 送信元のIPアドレ...
-
異なる言語間でのソケット通信...
-
同じLAN内パソコンのIPアドレス...
-
TCP/IP通信型プログラムです。 ...
-
winsockを使った通信での同時接...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
TCP/IP通信時のサーバーからの受信
-
recv関数でフリーズしてしまう
-
Winsockで接続待ちタイムアウト...
-
UDP通信におけるbind関数について
-
エクセル VBA でのCOMポート...
-
Socket通信の0バイト受信について
-
VB6のwinsockでconnectできない
-
ソケットのrecvの戻り値が0
-
ソケットを用いた1対多通信につ...
-
UdpClient 送信元のIPアドレ...
-
ソケットでクライアントのipア...
-
UDP通信する時に、相手にどうや...
-
ソケット通信 同じポート番号...
-
同じLAN内パソコンのIPアドレス...
-
Connectエラーが出てしまう・・...
-
ソケットのクローズについて
-
CreateFile関数でCOMポートが開...
-
異なる言語間でのソケット通信...
-
C言語でHTTP1.1のキープアライ...
-
送信したデータの一部が文字化...
おすすめ情報