[畢設(shè)記錄](méi) socket自定義數(shù)據(jù)包發(fā)送圖片

寫(xiě)在前面

還是畢業(yè)設(shè)計(jì)的事兒停做,還是發(fā)送圖片的事兒晤愧。前面已經(jīng)用了python實(shí)現(xiàn)圖片的發(fā)送,但是很蛋疼的是蛉腌,上位機(jī)用的是vb.net來(lái)進(jìn)行開(kāi)發(fā)官份,python的在圖片發(fā)送和接收又變得不好使了。實(shí)驗(yàn)了若干種方法烙丛,下面記錄一下舅巷。

目標(biāo):在vb.net的環(huán)境下和Android之間使用socket進(jìn)行圖片的傳輸。

實(shí)現(xiàn):自定義一個(gè)簡(jiǎn)單的傳輸協(xié)議河咽,將圖片用字節(jié)流的方式發(fā)送和接收钠右。

環(huán)境:在vb.net和Android上已經(jīng)試驗(yàn)成功,但是本文用java進(jìn)行演示(原因是:1.懶得連接Android了忘蟹;2.實(shí)在很討厭vb的語(yǔ)言....)飒房。邏輯是一樣的搁凸,只是具體的語(yǔ)法會(huì)有些少出入。

原理介紹

這次狠毯,在具體的實(shí)現(xiàn)之前护糖,先扒一下原理。網(wǎng)絡(luò)傳輸嚼松,使用的時(shí)socket嫡良,這是一個(gè)建立在tcp協(xié)議上的通信機(jī)制。所以献酗,有必要了解一下什么是tcp協(xié)議寝受,什么是socket。

TCP / IP 協(xié)議

Transmission Control Protocol/Internet Protocol的簡(jiǎn)寫(xiě)罕偎,中譯名為傳輸控制協(xié)議/因特網(wǎng)互聯(lián)協(xié)議很澄,又名網(wǎng)絡(luò)通訊協(xié)議,是Internet最基本的協(xié)議锨亏、Internet國(guó)際互聯(lián)網(wǎng)絡(luò)的基礎(chǔ)痴怨,由網(wǎng)絡(luò)層的IP協(xié)議和傳輸層的TCP協(xié)議組成。TCP/IP 定義了電子設(shè)備如何連入因特網(wǎng)器予,以及數(shù)據(jù)如何在它們之間傳輸?shù)臉?biāo)準(zhǔn)浪藻。協(xié)議采用了4層的層級(jí)結(jié)構(gòu),如下圖所示乾翔,每一層都呼叫它的下一層所提供的協(xié)議來(lái)完成自己的需求爱葵。(來(lái)自百度百科)

tcp ip協(xié)議層級(jí)示意圖

通俗而言:IP協(xié)議是給因特網(wǎng)的每一臺(tái)聯(lián)網(wǎng)設(shè)備規(guī)定一個(gè)地址,以便能夠在因特上上定位到該設(shè)備反浓。TCP負(fù)責(zé)發(fā)現(xiàn)傳輸?shù)膯?wèn)題萌丈,一有問(wèn)題就發(fā)出信號(hào),要求重新傳輸雷则,直到所有數(shù)據(jù)安全正確地傳輸?shù)侥康牡亍?/p>

socket

我們知道辆雾,程序在設(shè)備當(dāng)中運(yùn)行時(shí)是以進(jìn)程的形式被操作系統(tǒng)管理的。在本地月劈,兩個(gè)進(jìn)程之間要通信度迂,首先必須知道的是它們的PID。在網(wǎng)絡(luò)上猜揪,依靠PID來(lái)識(shí)別不同的進(jìn)程惭墓,顯然是不科學(xué)的,因?yàn)樗鼈儠?huì)重復(fù)而姐。從上面腊凶,我們了解到每個(gè)連接到因特網(wǎng)的設(shè)備的ip都是唯一的。所以,我們可以借助ip再加上一個(gè)端口號(hào)钧萍,來(lái)進(jìn)行進(jìn)程的唯一識(shí)別褐缠。當(dāng)網(wǎng)絡(luò)進(jìn)程可以唯一識(shí)別之后,就可以使用socket進(jìn)行通信了划煮。

socket送丰,翻譯成中文是“套接字”缔俄。它是應(yīng)用層和傳輸層之間的一個(gè)抽象層弛秋,它把TCP/IP層復(fù)雜的操作抽象為幾個(gè)簡(jiǎn)單的接口供應(yīng)用層調(diào)用已實(shí)現(xiàn)進(jìn)程在網(wǎng)絡(luò)中通信,如下圖所示俐载。socket源起于UNIX蟹略,在一切皆是文件的哲學(xué)思想下,socket是“打開(kāi)-讀寫(xiě)-關(guān)閉”模式的實(shí)現(xiàn)遏佣,服務(wù)端和客戶端共同維護(hù)一個(gè)“文件”挖炬,在連接建立之后,雙方都可以對(duì)文件進(jìn)行讀寫(xiě)操作状婶;通訊結(jié)束時(shí)意敛,關(guān)閉該文件。

socket 示意圖

具體實(shí)現(xiàn)

有了上面的原理膛虫,我們可以著手來(lái)實(shí)現(xiàn)以下了草姻。以下出現(xiàn)的代碼,只是為了演示稍刀,真實(shí)開(kāi)發(fā)環(huán)境下不要這樣寫(xiě)撩独。

Java提供了socket的相關(guān)API,我們不需要知道怎么寫(xiě)socket的底層账月,通過(guò)調(diào)用API就可以建立連接了综膀。在建立連接之前,我們需要自己定義一個(gè)用于發(fā)送圖片的簡(jiǎn)單的通信協(xié)議局齿。我自己隨便定義了一個(gè)剧劝,如下圖中間部分所示。

自定義socket通信

通信協(xié)議抓歼,顧名思義讥此,是用來(lái)規(guī)定通信過(guò)程的一種約定。只有服務(wù)端和客戶端都遵循同一個(gè)協(xié)議锭部,才可以完成有效的通信暂论。就想上圖表示的一樣,我規(guī)定了如下的內(nèi)容:

  • 每次發(fā)送的數(shù)據(jù)包長(zhǎng)度為1029個(gè)字節(jié)(數(shù)據(jù)包是一個(gè)有符號(hào)的字節(jié)數(shù)組拌禾,下標(biāo)從0開(kāi)始到1028結(jié)束)
  • 第一個(gè)字節(jié)是標(biāo)志位取胎,該位為1時(shí),表示后面還有數(shù)據(jù);該位為2時(shí)闻蛀,表示這是最后一包數(shù)據(jù)
  • 數(shù)據(jù)包的[1 : 4]表示該數(shù)據(jù)包的有效數(shù)據(jù)長(zhǎng)度匪傍,因?yàn)閿?shù)據(jù)包最大可以存放1024字節(jié)的數(shù)據(jù),所以[1 : 4]每一位分別表示“個(gè)十百千”位上的數(shù)字觉痛。除去最后一包數(shù)據(jù)役衡,其他包都應(yīng)該是放滿的,最后一包的數(shù)據(jù)則很大可能都不滿1024(配合流的read方法可以避免接收冗余數(shù)據(jù))薪棒。

有了上面的約定手蝎,我們就可以寫(xiě)代碼了:

// SocketTest.java 下面代碼的運(yùn)行環(huán)境是Mac intellij14
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Created by eric_lai on 2016/3/31.
 */
public class SocketTest {

    // 服務(wù)端, 用來(lái)接收?qǐng)D片
    public static void ServerSocket() {

        // 調(diào)試標(biāo)志
        String TAG = "ServerSocket: ";
        // 服務(wù)端socket
        ServerSocket serverSocket = null;
        // 端口號(hào)
        int port = 9998;
        // 緩沖區(qū)
        byte[] buffer = new byte[1029];
        int len = buffer.length;
        // 實(shí)際長(zhǎng)度
        int dataLen = 0;
        // 文件輸出流
        FileOutputStream fileOutputStream = null;
        // client輸入流
        InputStream clientInputStream = null;
        // 文件路徑
        File file = new File("res/image2.jpg");
        try {
            fileOutputStream = new FileOutputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        try {
            // 綁定端口號(hào)
            serverSocket = new ServerSocket(port);
            // 堵塞線程, 等待連接
            System.out.println(TAG + "waitting for connection ...");
            Socket client = serverSocket.accept();
            System.out.println(TAG + "connection has been set up");
            // 獲取client輸入流
            clientInputStream = client.getInputStream();
            // 讀取數(shù)據(jù)
            byte[] lenInByte = new byte[4];
            clientInputStream.read(buffer, 0, len);
            byte flag = buffer[0];
            System.arraycopy(buffer, 1, lenInByte, 0, lenInByte.length);
            dataLen = ChangeByte2Int(lenInByte);
            System.out.println(TAG + "begin to receive image ...");
            while (true) {
                if (fileOutputStream != null) {
                    if (flag == 1) {
                        // 寫(xiě)入文件
                        fileOutputStream.write(buffer, 5, dataLen);
                        // 讀取數(shù)據(jù)
                        clientInputStream.read(buffer, 0, len);
                        flag = buffer[0];
                        System.arraycopy(buffer, 1, lenInByte, 0, lenInByte.length);
                        dataLen = ChangeByte2Int(lenInByte);
                    } else {
                        // 寫(xiě)入文件
                        fileOutputStream.write(buffer, 5, dataLen);
                        break;
                    }
                }
            }
            System.out.println(TAG + "image successfully received ...");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
                if (clientInputStream != null) {
                    clientInputStream.close();
                }
                if (serverSocket != null) {
                    serverSocket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

    }

    public static void ClientSocket() {

        // 調(diào)試標(biāo)志
        String TAG = "ClientSocket: ";
        // 客戶端socket
        Socket clientSocket = null;
        // 服務(wù)器地址
        String ip = "127.0.0.1";
        // 端口號(hào)
        int port = 9998;
        // 緩沖區(qū)
        byte[] buffer = new byte[1029];
        int len = buffer.length;
        byte[] fullData = {0, 1, 0, 2, 4};
        // 實(shí)際長(zhǎng)度
        int realLen = 0;
        // 文件末尾標(biāo)志
        int cf = 0;
        // 文件輸入流
        FileInputStream fileInputStream = null;
        // client輸出流
        OutputStream clientOutputStream = null;
        // 文件路徑
        File file = new File("res/image1.jpg");

        try {
            // 建立socket
            System.out.println(TAG+"set up connection to the server ...");
            clientSocket = new Socket(ip, port);
            // 獲取文件輸入流(讀取要發(fā)送的圖片)
            fileInputStream = new FileInputStream(file);
            // 獲取socket輸出流(發(fā)送數(shù)據(jù)包)
            clientOutputStream = clientSocket.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (clientOutputStream != null) {
            try {
                System.out.println(TAG+"begin to send the image ...");
                while (cf != -1) {
                    // 讀取數(shù)據(jù)放到數(shù)據(jù)區(qū)域
                    cf = fileInputStream.read(buffer, 5, len - 5);
                    if (cf == -1) {
                        // 將長(zhǎng)度轉(zhuǎn)為byte[]
                        byte[] by = ChangeInt2Byte(realLen);
                        // 將長(zhǎng)度數(shù)據(jù)整合到數(shù)據(jù)包里面
                        System.arraycopy(by, 0, buffer, 0, by.length);
                        // 標(biāo)志位置2
                        buffer[0] = 2;
                        // 發(fā)送數(shù)據(jù)
                        clientOutputStream.write(buffer, 0, realLen + 5);
                    }else {
                        // 緩存實(shí)際的數(shù)據(jù)長(zhǎng)度
                        realLen = cf;
                        System.arraycopy(fullData, 0, buffer, 0, fullData.length);
                        // 標(biāo)志位置1
                        buffer[0] = 1;
                        // 發(fā)送數(shù)據(jù)
                        clientOutputStream.write(buffer, 0, len);
                    }
                }
                System.out.println(TAG+"image has been sent ...");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static int ChangeByte2Int(byte[] bytes) {
        int result = 0;
        int[] a = {1000, 100, 10, 1};
        for (int i = 0, j = 0; i < bytes.length; i++, j++) {
            int k = (int) bytes[i];
            result += k * a[j];
        }
        return result;
    }

    public static byte[] ChangeInt2Byte(int len) {
        byte[] bytes = new byte[5];
        byte[] bytesReturn = new byte[5];
        int result = 0;
        int i;
        int j = 1;
        int z = 4;
        int k = 1;
        for (i = 4; i >= 0; i--) {
            result = len % 10;
            bytes[i] = (byte) result;
            len /= 10;
        }

        for (; j < bytes.length; j++) {
            bytesReturn[k] = bytes[j];
            k++;
        }
        return bytesReturn;
    }

    public static void PrintBytes(byte[] bytes) {
        int len = bytes.length;
        for (byte b : bytes) {
            System.out.print(b + " ");
        }
    }

}
// main.java 
import java.net.Socket;
import java.util.Timer;

public class Main {

    public static void main(String[] args) {
        beginTest();
    }

    private static void beginTest() {
        Thread server = new Thread(new Runnable() {
            @Override
            public void run() {
                SocketTest.ServerSocket();
            }
        });
        Thread client = new Thread(new Runnable() {
            @Override
            public void run() {
                SocketTest.ClientSocket();
            }
        });
        server.start();

        // 確保服務(wù)端先啟動(dòng)
        try {
            server.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        client.start();
    }
}

運(yùn)行上述代碼后,結(jié)果如下:

運(yùn)行結(jié)果

注意俐芯,需要自己準(zhǔn)備圖片棵介!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市吧史,隨后出現(xiàn)的幾起案子邮辽,更是在濱河造成了極大的恐慌,老刑警劉巖贸营,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吨述,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡钞脂,警方通過(guò)查閱死者的電腦和手機(jī)揣云,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)芳肌,“玉大人灵再,你說(shuō)我怎么就攤上這事∫隗裕” “怎么了翎迁?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)净薛。 經(jīng)常有香客問(wèn)我汪榔,道長(zhǎng),這世上最難降的妖魔是什么肃拜? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任痴腌,我火速辦了婚禮,結(jié)果婚禮上燃领,老公的妹妹穿的比我還像新娘沛慢。我一直安慰自己杭攻,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著嘀趟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天略板,我揣著相機(jī)與錄音,去河邊找鬼慈缔。 笑死叮称,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的藐鹤。 我是一名探鬼主播瓤檐,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼教藻!你這毒婦竟也來(lái)了距帅?” 一聲冷哼從身側(cè)響起右锨,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤括堤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后绍移,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體悄窃,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年蹂窖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了轧抗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瞬测,死狀恐怖横媚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情月趟,我是刑警寧澤灯蝴,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站孝宗,受9級(jí)特大地震影響穷躁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜因妇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一问潭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧婚被,春花似錦狡忙、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春删顶,著一層夾襖步出監(jiān)牢的瞬間竖螃,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工逗余, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留特咆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓录粱,卻偏偏與公主長(zhǎng)得像腻格,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子啥繁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • 網(wǎng)絡(luò)概念第一天 兩臺(tái)電腦怎么通過(guò)網(wǎng)絡(luò)傳輸數(shù)據(jù)菜职?怎樣才能知道傳輸?shù)氖菙?shù)據(jù)?誰(shuí)摸過(guò)網(wǎng)線旗闽? 看電影酬核,怎么看的?通過(guò)電流适室,...
    小吖朱閱讀 1,549評(píng)論 0 1
  • 簡(jiǎn)介 用簡(jiǎn)單的話來(lái)定義tcpdump嫡意,就是:dump the traffic on a network,根據(jù)使用者...
    保川閱讀 5,948評(píng)論 1 13
  • 有時(shí)我們需要懂的應(yīng)該是自己所愛(ài)的人捣辆。# Z的這幅圖讓我看了很久蔬螟,我一直在試圖揣測(cè)他畫(huà)圖時(shí)的所思所想。整幅圖畫(huà)的非常...
    周米啊閱讀 377評(píng)論 0 1
  • asdfasdfsdaf sdf sdf sdf sadf sadfsad dfasdf dsf sdf sdf
    venuss閱讀 531評(píng)論 0 50
  • 1 正則表達(dá)式 2 元字符介紹 3 BRE 和 ERE 1 BRE(基礎(chǔ)正則表達(dá)式) 2 ERE (擴(kuò)展正則表達(dá)式...
    喜歡蘿莉的逗逼青年閱讀 683評(píng)論 0 50