Java Socketは複数の画像を連続的に転送する(リンクを開く)
9860 ワード
一、问题说明私达は画像の伝送をする时、通常すべて1つのhttpの要求を开いて、それから1枚の画像をダウンロードして、ダウンロードが完成した后に、このリンクは切れて、このような情况の下で、私达は最大1枚の画像を伝送することを许して、それでは、さっきのようなやり方はだめです.私の需要は、例えば私は100枚の画像を持っていて、私は100枚の画像を転送してリンクを切っていると思って、あるいはもっと誇張して、私は1つのスクリーン放送プログラムをして、スクリーンショットから来た画像を1枚1枚クライアントに送る必要があります.このようなニーズは、httpリクエストではあまりお得ではありません.リクエストリンクが絶えず切断されているため、伝送効率が低下します.では、さっきのようなニーズを実現する方法は何でしょうか.方法はきっとあるに違いありません.次に、私が蓄積した方法を皆さんに教えます.
二、方法といえば、最も原始的な通信ツールを使用しなければなりません.それはSocketです.リンク上で絶えず画像(スクリーンブロードキャストなど)を伝送するには、アプリケーション層プロトコルをカスタマイズする必要があります.このようなニーズを実現するには、くだらないことを言わないで、自分で定義したプロトコルを見てください.
プロトコルの解釈:画像のデータのパケットヘッダを定義します:このパケットヘッダを定義するのは私が1枚の画像のデータの始まりであることを示して、私がこのヘッダを見つけた後に、やっと画像のデータの情報を読み取ることができて、勝手に1バイトを探して読み始めるのではありませんて、そのようにすれば、あなたは永遠に1枚の画像を得ることができません.パッケージはあなた自身が勝手に定義した特殊なデータで、画像データと分けることができます.
画像番号:なぜ4 byteなのか、実はint型の値で、プロトコルの中で私はこれらの単位を書いて、すべてJavaの基本的なデータ型(パッケージとパリティを除いて)で、相応して、画像のデータの長さはlong型です.
他のプロトコルセグメントは説明されず、理解できると信じています.もしあなたの画像番号がintが耐えられる値を超えたら、8 byte、つまりlongタイプを使用することができます.このプロトコルがあれば、画像を転送することができます.
三、実現コード
1、まずプロトコルパッケージを定義します.これはランダムで、どのように定義したいかはどのように定義します.特殊であればあるほどいいです.
2、プロトコルをパッケージ化し、socketで送信します.ここではJavaのツールクラスDataOutputStreamを使用して、基本データ型データを送信することができ、使いやすく、効率も高いです.
TCPリンクを確立すると、さっきのコードを使って画像データを送信することができます.
3、クライアントは画像データを受信し、解析を行う.
以上の2つの方法により,同じリンクでループした送信画像を実現でき,効率も高い.ここでは、私が使用しているときにDataInputStreamとDataOutputStreamという2つのクラス、あるプロジェクトではこの2つのクラスを使用できない場合、対応するバイト数だけを読み出してint、long、stringなどのタイプのデータに変換することもできます.より効率的にするには、2つのサブスレッドを使用して画像データを処理することができます.1つのスレッドはデータを受信する責任を負います.readメソッドはブロックされているので、1つのデータキューを構築する必要があります.もう1つのスレッドは絶えずこのキューにデータを取りに行きます.私はこのような方法を試したことがあります.技術に対する要求が高いです.特にメモリの制御は厳しい.組み込み機器ではさらに課題です.興味があればやってみてください.コードの量が大きいので、これ以上貼るのは不便で、核心関数だけを貼って、皆さんはこの思想を理解して、自分で書くことができて、もし本当に理解していないならば、私と討論することができます.私がやっている過程でもう一つの問題に直面したのは、私のクライアントとサービス側が画像データを送信するだけでなく、私はいくつかのメッセージを送信しなければならないということです.つまり、画像データだけでなく、他のものもあります.この場合、1つのチャネルで2つのデータを送信すると、きっと事故が起こるに違いありません.だから、最善の方法は、2つのtcpリンクチャネルを構築することです.画像データを専用に送信し、メッセージデータを専用に送信する.これでさっきの問題を解決することができます.
注意:私はネット上であるネットユーザーが直接DataInputStreamで画像データを行って、パケットヘッダを定義しないのを見て、もしそれが読み取ったデータがちょうどあなたが書いたデータではないならば、このデータが読み取ったのは間違いに違いないので、必ずヘッダを見つけてから、データを取ることができます.
編集者の技術力が限られているため、文の中に間違いがあるのは避けられない.
二、方法といえば、最も原始的な通信ツールを使用しなければなりません.それはSocketです.リンク上で絶えず画像(スクリーンブロードキャストなど)を伝送するには、アプリケーション層プロトコルをカスタマイズする必要があります.このようなニーズを実現するには、くだらないことを言わないで、自分で定義したプロトコルを見てください.
(8byte) | (4byte) | (4byte) | | (8byte) | | (4byte)
プロトコルの解釈:画像のデータのパケットヘッダを定義します:このパケットヘッダを定義するのは私が1枚の画像のデータの始まりであることを示して、私がこのヘッダを見つけた後に、やっと画像のデータの情報を読み取ることができて、勝手に1バイトを探して読み始めるのではありませんて、そのようにすれば、あなたは永遠に1枚の画像を得ることができません.パッケージはあなた自身が勝手に定義した特殊なデータで、画像データと分けることができます.
画像番号:なぜ4 byteなのか、実はint型の値で、プロトコルの中で私はこれらの単位を書いて、すべてJavaの基本的なデータ型(パッケージとパリティを除いて)で、相応して、画像のデータの長さはlong型です.
他のプロトコルセグメントは説明されず、理解できると信じています.もしあなたの画像番号がintが耐えられる値を超えたら、8 byte、つまりlongタイプを使用することができます.このプロトコルがあれば、画像を転送することができます.
三、実現コード
1、まずプロトコルパッケージを定義します.これはランダムで、どのように定義したいかはどのように定義します.特殊であればあるほどいいです.
//
public final static byte[] PICTURE_PACKAGE_HEAD = {(byte) 0xFF, (byte) 0xCF,
(byte) 0xFA, (byte) 0xBF, (byte) 0xF6, (byte) 0xAF, (byte) 0xFE,
(byte) 0xFF};
public final static byte[] PICTURE_PACKAGE_END = {(byte) 0xEF, (byte) 0xDA,
(byte) 0xFF, (byte) 0xFD};
2、プロトコルをパッケージ化し、socketで送信します.ここではJavaのツールクラスDataOutputStreamを使用して、基本データ型データを送信することができ、使いやすく、効率も高いです.
/**
*
* int pngNumber = 1001;
* String pngName = "Image01.png";
* byte[] pictures; // , byte ,
**/
DataOutputStream outputData = new DataOutputStream(pictureSocket.getOutputStream());
//
outputData.write(PICTURE_PACKAGE_HEAD);
//
outputData.writeInt(pngNumber);
// , , byte ,
outputData.writeInt(pngName.getBytes().length);
//
outputData.write(pngName.getBytes());
//
outputData.writeLong(pictures.length);
//
outputData.write(pictures, 0, pictures.length);
//
outputData.write(Command.PICTURE_PACKAGE_END);
TCPリンクを確立すると、さっきのコードを使って画像データを送信することができます.
3、クライアントは画像データを受信し、解析を行う.
/**
*
**/
public void receivePicture() {
// null
if (pictureSocket == null)
return;
try {
//
boolean isHead = true;
// PICTURE_PACKAGE_HEAD.length ,
for (int i = 0; i < PICTURE_PACKAGE_HEAD.length; ++i) {
byte head = (byte) inputStream.read();
// , ,
if (head != Command.PICTURE_PACKAGE_HEAD[i]) {
isHead = false;
break;
}
}
// ,
if (isHead) {
DataInputStream inputData = new DataInputStream(inputStream);
//
int picNumber = inputData.readInt();
//
int picNameLen = inputData.readInt();
//
byte[] data = new byte[picNameLen];
inputData.readFully(data);
//
String picName = new String(data,0,data.length);
// data
data = null;
//
long picLeng = inputData.writeLong();
//new
ByteArrayOutputStream fos = new ByteArrayOutputStream();
// CACHE_SIZE ,CACHE_SIZE , 1024 * 2
byte[] buffer = new byte[CACHE_SIZE];
int len = -1;
//
while (picLeng > 0
&& (len = inputData.read(buffer, 0,
picLeng < buffer.length ? (int) picLeng
: buffer.length)) != -1) {
fos.write(buffer, 0, len);
fos.flush();
// ,picLeng len , picLeng = 0
picLeng -= len;
}
fos.flush();
buffer = null;
// picLeng 0, ,
if (picLeng > 0) {
return;
}
// ,
byte[] pictures = fos.toByteArray();
// pictures
fos.close();
fos = null;
}
// , , , ,
} catch (IOException e) {
e.printStackTrace();
}
}
以上の2つの方法により,同じリンクでループした送信画像を実現でき,効率も高い.ここでは、私が使用しているときにDataInputStreamとDataOutputStreamという2つのクラス、あるプロジェクトではこの2つのクラスを使用できない場合、対応するバイト数だけを読み出してint、long、stringなどのタイプのデータに変換することもできます.より効率的にするには、2つのサブスレッドを使用して画像データを処理することができます.1つのスレッドはデータを受信する責任を負います.readメソッドはブロックされているので、1つのデータキューを構築する必要があります.もう1つのスレッドは絶えずこのキューにデータを取りに行きます.私はこのような方法を試したことがあります.技術に対する要求が高いです.特にメモリの制御は厳しい.組み込み機器ではさらに課題です.興味があればやってみてください.コードの量が大きいので、これ以上貼るのは不便で、核心関数だけを貼って、皆さんはこの思想を理解して、自分で書くことができて、もし本当に理解していないならば、私と討論することができます.私がやっている過程でもう一つの問題に直面したのは、私のクライアントとサービス側が画像データを送信するだけでなく、私はいくつかのメッセージを送信しなければならないということです.つまり、画像データだけでなく、他のものもあります.この場合、1つのチャネルで2つのデータを送信すると、きっと事故が起こるに違いありません.だから、最善の方法は、2つのtcpリンクチャネルを構築することです.画像データを専用に送信し、メッセージデータを専用に送信する.これでさっきの問題を解決することができます.
注意:私はネット上であるネットユーザーが直接DataInputStreamで画像データを行って、パケットヘッダを定義しないのを見て、もしそれが読み取ったデータがちょうどあなたが書いたデータではないならば、このデータが読み取ったのは間違いに違いないので、必ずヘッダを見つけてから、データを取ることができます.
編集者の技術力が限られているため、文の中に間違いがあるのは避けられない.