Java Socketは複数の画像を連続的に転送する(リンクを開く)

9860 ワード

一、问题说明私达は画像の伝送をする时、通常すべて1つのhttpの要求を开いて、それから1枚の画像をダウンロードして、ダウンロードが完成した后に、このリンクは切れて、このような情况の下で、私达は最大1枚の画像を伝送することを许して、それでは、さっきのようなやり方はだめです.私の需要は、例えば私は100枚の画像を持っていて、私は100枚の画像を転送してリンクを切っていると思って、あるいはもっと誇張して、私は1つのスクリーン放送プログラムをして、スクリーンショットから来た画像を1枚1枚クライアントに送る必要があります.このようなニーズは、httpリクエストではあまりお得ではありません.リクエストリンクが絶えず切断されているため、伝送効率が低下します.では、さっきのようなニーズを実現する方法は何でしょうか.方法はきっとあるに違いありません.次に、私が蓄積した方法を皆さんに教えます.
二、方法といえば、最も原始的な通信ツールを使用しなければなりません.それは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で画像データを行って、パケットヘッダを定義しないのを見て、もしそれが読み取ったデータがちょうどあなたが書いたデータではないならば、このデータが読み取ったのは間違いに違いないので、必ずヘッダを見つけてから、データを取ることができます.
編集者の技術力が限られているため、文の中に間違いがあるのは避けられない.