欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android使用socket進(jìn)行二進(jìn)制流數(shù)據(jù)傳輸

 更新時(shí)間:2023年04月11日 09:54:53   作者:haohulala  
這篇文章主要介紹了Android使用socket進(jìn)行二進(jìn)制流數(shù)據(jù)傳輸,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧

引言

使用socket流傳輸二進(jìn)制流數(shù)據(jù),比如文件或者視頻圖片等等信息的時(shí)候,我們通常使用tcp協(xié)議傳輸,因?yàn)閠cp協(xié)議可以保證二進(jìn)制流按序到達(dá),并且保證交付,這樣子就可以保證我們傳輸二進(jìn)制流的完整性。

使用tcp協(xié)議進(jìn)行二進(jìn)制流傳輸?shù)臅r(shí)候通常會(huì)有兩個(gè)問題:

由于tcp進(jìn)行信息傳輸?shù)臅r(shí)候是沒有邊界的,所以可能會(huì)產(chǎn)生粘包半包問題。所謂粘包就是指接收的一段數(shù)據(jù)包含了下一段數(shù)據(jù)的信息,所謂半包就是指一段數(shù)據(jù)沒有接收完整,實(shí)際上都是邊界不明確產(chǎn)生的問題。

并且在傳輸一段很大的二進(jìn)制流數(shù)據(jù)的時(shí)候,我們可能需要對(duì)超大的二進(jìn)制流分段處理,也就是分段來傳輸。

在輸出端將二進(jìn)制流分段,輸入端在接收到各個(gè)片段后再將整個(gè)流信息拼接起來,就構(gòu)成了完整的傳輸流程。

為了解決上述的兩個(gè)問題,同時(shí)也為了能夠統(tǒng)一這個(gè)傳輸流程,我們需要自定義一個(gè)簡(jiǎn)單的傳輸協(xié)議。

簡(jiǎn)單的自定義協(xié)議

我們自定義一個(gè)簡(jiǎn)單的通信協(xié)議,協(xié)議一共傳輸兩種信息,第一種是文字,第二種是二進(jìn)制流(其實(shí)文字也可以用二進(jìn)制流表示),傳輸過程如下圖所示。

我們定義的簡(jiǎn)單通信協(xié)議規(guī)則如下

1.首先發(fā)送一個(gè)字節(jié)的信息(就是圖中的type),表示一段消息的開始,同時(shí)也表明了后面二進(jìn)制數(shù)據(jù)的類型(文字信息還是二進(jìn)制流數(shù)據(jù))

2.每一個(gè)chunk都由三個(gè)字節(jié)的長(zhǎng)度信息和相應(yīng)的二進(jìn)制流信息組成,接收方在接收到三個(gè)字節(jié)的長(zhǎng)度信息后,繼續(xù)使用相應(yīng)大小的緩沖區(qū)接收后面的流數(shù)據(jù)

3.當(dāng)接收到三個(gè)字節(jié)的000的時(shí)候表示數(shù)據(jù)接收完成,接收方將二進(jìn)制流數(shù)據(jù)拼接起來即可

我們規(guī)定一個(gè)最大的分段長(zhǎng)度,一旦發(fā)送的數(shù)據(jù)超過這個(gè)分段長(zhǎng)度就需要進(jìn)行分段發(fā)送。

發(fā)送的代碼示例如下

    // 發(fā)送文件
    public void sendFile(int size) {
        new Thread(()->{
            try {
                // 表示發(fā)送文件
                outputStream.write("2".getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
            for(int i=0; i<size/SocketUtil.MAX_CHUNK+1; i++) {
                StringBuffer sb = new StringBuffer();
                if (i!=size / SocketUtil.MAX_CHUNK) {
                    for (int j = 0; j < SocketUtil.MAX_CHUNK; j++) {
                        sb.append('a');
                    }
                }
                else if(i==size/SocketUtil.MAX_CHUNK && size%SocketUtil.MAX_CHUNK==0) {
                    break;
                }
                else {
                    for (int j = 0; j < size % SocketUtil.MAX_CHUNK; j++) {
                        sb.append('a');
                    }
                }
                try {
                    SocketUtil.sendInfo("[客戶端]發(fā)送一個(gè)數(shù)據(jù)包,大小" + sb.toString().getBytes().length + "B");
                    // 發(fā)送chunk的長(zhǎng)度
                    outputStream.write(SocketUtil.intToStr(sb.toString().getBytes().length).getBytes());
                    // 發(fā)送chunk塊
                    outputStream.write(sb.toString().getBytes());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            // 最后發(fā)送000表示結(jié)束
            try {
                outputStream.write("000".getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }

接收二進(jìn)制流的代碼示例如下

    // 讀取二進(jìn)制信息
    public static byte[] readBytes(InputStream inputStream, String log) {
        byte[] len = new byte[3];
        byte[] allbytes = new byte[10000];
        int idx = 0;
        try {
            inputStream.read(len);
            // 然后再根據(jù)讀取的長(zhǎng)度信息讀取二進(jìn)制流
            // 只要不是最后一個(gè)二進(jìn)制流就繼續(xù)讀取
            while (SocketUtil.parseLen(len) != 0) {
                byte[] temp = new byte[SocketUtil.parseLen(len)];
                inputStream.read(temp);
                idx = SocketUtil.appendBytes(allbytes, temp, idx);
                String info = "[" + log + "]接收一個(gè)數(shù)據(jù)包,大小" + SocketUtil.parseLen(len) + "B";
                SocketUtil.sendInfo(info);
                inputStream.read(len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return SocketUtil.getNewArr(allbytes, idx);
    }

其實(shí)我理解的所謂的通信協(xié)議,就是發(fā)送方和接收方都遵守的某種規(guī)則,按照這種規(guī)則發(fā)送和接收數(shù)據(jù)就可以保證數(shù)據(jù)的完整性。

完整的代碼

這段代碼只有四個(gè)java文件,非常簡(jiǎn)單,只是一個(gè)極簡(jiǎn)的通信協(xié)議模型。

首先來看一下界面定義

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <EditText
        android:id="@+id/text_file_size"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="請(qǐng)輸入待發(fā)送文件大小"/>
    <Button
        android:id="@+id/button_send"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="發(fā)送文件"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="消息欄:"/>
    <TextView
        android:id="@+id/text_info"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

然后是MainActivity

public class MainActivity extends AppCompatActivity {
    private EditText text_file_size;
    private Button button_send;
    private TextView text_info;
    private BroadcastReceiver broadcastReceiver;
    private SocketClient socketClient;
    private SocketServer socketServer;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        SocketUtil.context = MainActivity.this;
        // 初始化控件
        initView();
        // 注冊(cè)廣播接收器
        register();
        socketServer = new SocketServer();
        socketClient = new SocketClient();
    }
    private void initView() {
        text_file_size = findViewById(R.id.text_file_size);
        button_send = findViewById(R.id.button_send);
        button_send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Integer size = Integer.parseInt(text_file_size.getText().toString());
                socketClient.sendFile(size);
            }
        });
        text_info = findViewById(R.id.text_info);
        text_info.setMovementMethod(new ScrollingMovementMethod());
    }
    private void register() {
        broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String info = intent.getStringExtra("info");
                text_info.append(info + "\n");
            }
        };
        IntentFilter filter = new IntentFilter("main.info");
        registerReceiver(broadcastReceiver, filter);
    }
}

我們將需要重復(fù)用到的一些代碼都放到工具類中

public class SocketUtil {
    public static Context context;
    // 一次最多傳輸多少字節(jié)
    public static int MAX_CHUNK = 100;
    public static void sendInfo(String info) {
        Intent intent = new Intent("main.info");
        intent.putExtra("info", info);
        context.sendBroadcast(intent);
    }
    // 讀取二進(jìn)制信息
    public static byte[] readBytes(InputStream inputStream, String log) {
        byte[] len = new byte[3];
        byte[] allbytes = new byte[10000];
        int idx = 0;
        try {
            inputStream.read(len);
            // 然后再根據(jù)讀取的長(zhǎng)度信息讀取二進(jìn)制流
            // 只要不是最后一個(gè)二進(jìn)制流就繼續(xù)讀取
            while (SocketUtil.parseLen(len) != 0) {
                byte[] temp = new byte[SocketUtil.parseLen(len)];
                inputStream.read(temp);
                idx = SocketUtil.appendBytes(allbytes, temp, idx);
                String info = "[" + log + "]接收一個(gè)數(shù)據(jù)包,大小" + SocketUtil.parseLen(len) + "B";
                SocketUtil.sendInfo(info);
                inputStream.read(len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return SocketUtil.getNewArr(allbytes, idx);
    }
    // 將int轉(zhuǎn)成String
    public static String intToStr(int len) {
        StringBuffer sb = new StringBuffer();
        if(len < 100) {
            sb.append("0");
        }
        else if (len < 10) {
            sb.append("00");
        }
        sb.append(Integer.toString(len));
        return sb.toString();
    }
    public static int parseLen(byte[] len) {
        return Integer.parseInt(new String(len, 0, len.length));
    }
    public static int appendBytes(byte[] arr1, byte[] arr2, int st) {
        for(int i=st; i<arr2.length; i++) {
            arr1[i] = arr2[i-st];
        }
        return arr2.length+st;
    }
    public static byte[] getNewArr(byte[] arr, int idx) {
        byte[] newarr = new byte[idx];
        for(int i=0; i<idx; i++) {
            newarr[i] = arr[i];
        }
        return newarr;
    }
}

最后是定義我們的客戶端和服務(wù)端

public class SocketClient {
    private final String HOST = "localhost";
    private final int PORT = 50055;
    private Socket socket = null;
    private OutputStream outputStream = null;
    private InputStream inputStream = null;
    public SocketClient() {
        conn();
        while(socket == null) {}
        SocketUtil.sendInfo("服務(wù)端連接成功...");
        try {
            outputStream = socket.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    // 連接服務(wù)端
    private void conn() {
        new Thread(()->{
            try {
                socket = new Socket(HOST, PORT);
                inputStream = socket.getInputStream();
                while(true) {
                    // 接收服務(wù)端消息0
                    byte[] type = new byte[1];
                    inputStream.read(type);
                    if (new String(type, 0, 1).equals("1")) {
                        byte[] infobytes = SocketUtil.readBytes(inputStream, "客戶端");
                        String info = "[客戶端]接收消息:" + new String(infobytes, 0, infobytes.length);
                        SocketUtil.sendInfo(info);
                        SocketUtil.sendInfo("====================================");
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }
    // 發(fā)送文件
    public void sendFile(int size) {
        new Thread(()->{
            try {
                // 表示發(fā)送文件
                outputStream.write("2".getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
            for(int i=0; i<size/SocketUtil.MAX_CHUNK+1; i++) {
                StringBuffer sb = new StringBuffer();
                if (i!=size / SocketUtil.MAX_CHUNK) {
                    for (int j = 0; j < SocketUtil.MAX_CHUNK; j++) {
                        sb.append('a');
                    }
                }
                else if(i==size/SocketUtil.MAX_CHUNK && size%SocketUtil.MAX_CHUNK==0) {
                    break;
                }
                else {
                    for (int j = 0; j < size % SocketUtil.MAX_CHUNK; j++) {
                        sb.append('a');
                    }
                }
                try {
                    SocketUtil.sendInfo("[客戶端]發(fā)送一個(gè)數(shù)據(jù)包,大小" + sb.toString().getBytes().length + "B");
                    // 發(fā)送chunk的長(zhǎng)度
                    outputStream.write(SocketUtil.intToStr(sb.toString().getBytes().length).getBytes());
                    // 發(fā)送chunk塊
                    outputStream.write(sb.toString().getBytes());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            // 最后發(fā)送000表示結(jié)束
            try {
                outputStream.write("000".getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }
}
public class SocketServer {
    private final int PORT = 50055;
    private ServerSocket serverSocket = null;
    public SocketServer() {
        // 啟動(dòng)服務(wù)端監(jiān)聽
        start();
        while(serverSocket == null) {}
        SocketUtil.sendInfo("服務(wù)端啟動(dòng)...");
    }
    // 啟動(dòng)服務(wù)端監(jiān)聽程序
    private void start() {
        new Thread(()->{
            try {
                serverSocket = new ServerSocket(PORT);
                Socket socket = serverSocket.accept();
                InputStream inputStream = socket.getInputStream();
                OutputStream outputStream = socket.getOutputStream();
                while(true) {
                    byte[] type = new byte[1];
                    inputStream.read(type);
                    String typeinfo = new String(type, 0, 1);
                    if(typeinfo.equals("2")) {
                        byte[] file = SocketUtil.readBytes(inputStream, "服務(wù)端");
                        String filetxt = new String(file, 0, file.length);
                        String info = "[服務(wù)端]接收完文件,大小" + file.length + "B" + "\n";
                        info = info + "[服務(wù)端]具體內(nèi)容如下:" + "\n" + filetxt;
                        SocketUtil.sendInfo(info);
                        // 給客戶端發(fā)送一個(gè)響應(yīng)信息表示接收成功
                        String typetxt = "1";
                        outputStream.write(typetxt.getBytes());
                        String successinfo = "文件接收成功";
                        String lentxt = SocketUtil.intToStr(successinfo.getBytes().length);
                        outputStream.write(lentxt.getBytes());
                        outputStream.write(successinfo.getBytes());
                        outputStream.write("000".getBytes());
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

上述代碼中,服務(wù)端只負(fù)責(zé)接收二進(jìn)制流,客戶端只負(fù)責(zé)發(fā)送二進(jìn)流,并且服務(wù)端在接收完二進(jìn)制流數(shù)據(jù)后,會(huì)給服務(wù)端返回一個(gè)表示接收成功的文字信息。

結(jié)語(yǔ)

以上就是一個(gè)極簡(jiǎn)的自定義通信協(xié)議模型,這個(gè)協(xié)議非常簡(jiǎn)單,并且功能非常單一,可以根據(jù)上述的邏輯自定義通信協(xié)議以符合各種需求。

到此這篇關(guān)于Android使用socket進(jìn)行二進(jìn)制流數(shù)據(jù)傳輸?shù)奈恼戮徒榻B到這了,更多相關(guān)Android二進(jìn)制流數(shù)據(jù)傳輸內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Android自定義View 實(shí)現(xiàn)鬧鐘喚起播放鬧鐘鈴聲功能

    Android自定義View 實(shí)現(xiàn)鬧鐘喚起播放鬧鐘鈴聲功能

    這篇文章主要介紹了Android自定義View 實(shí)現(xiàn)鬧鐘喚起播放鬧鐘鈴聲的效果,本文通過實(shí)例代碼給大家詳解,需要的朋友可以參考下
    2016-12-12
  • Android計(jì)算器編寫代碼

    Android計(jì)算器編寫代碼

    這篇文章主要為大家分享了Android計(jì)算器編寫代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-07-07
  • Android中ListView的幾種常見的優(yōu)化方法總結(jié)

    Android中ListView的幾種常見的優(yōu)化方法總結(jié)

    Android中的ListView應(yīng)該算是布局中幾種最常用的組件之一,本篇文章主要做了三種優(yōu)化總結(jié),有興趣的可以了解一下。
    2017-02-02
  • Android制作漂亮自適布局鍵盤的方法

    Android制作漂亮自適布局鍵盤的方法

    最近做了個(gè)自定義鍵盤,但面對(duì)不同分辨率的機(jī)型其中數(shù)字鍵盤不能根據(jù)界面大小自已鋪滿,但又不能每種機(jī)型都做一套吧,所以要做成自適應(yīng),那這里主講思路,感興趣的朋友一起學(xué)習(xí)吧
    2015-12-12
  • Flutter?Widget?之StatefulBuilder構(gòu)建方法詳解

    Flutter?Widget?之StatefulBuilder構(gòu)建方法詳解

    這篇文章主要為大家介紹了Flutter?Widget?之StatefulBuilder構(gòu)建方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • Android 暫停和恢復(fù)Activity

    Android 暫停和恢復(fù)Activity

    在正常的應(yīng)用程序使用,前臺(tái)activity有時(shí)會(huì)被其他可視化組件遮擋,從而 造成activity的暫停。例如,當(dāng)一個(gè)半透明的activity打開時(shí)(如在一個(gè)風(fēng)格對(duì)話框),以前的activity就暫停了。只要 activity仍然是部分可見,但目前沒有獲得焦點(diǎn),它就依然處于暫停狀態(tài)
    2016-03-03
  • Android 通過jni返回Mat數(shù)據(jù)類型方法

    Android 通過jni返回Mat數(shù)據(jù)類型方法

    今天小編就為大家分享一篇Android 通過jni返回Mat數(shù)據(jù)類型方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • android?studio后臺(tái)服務(wù)使用詳解

    android?studio后臺(tái)服務(wù)使用詳解

    這篇文章主要為大家詳細(xì)介紹了android?studio后臺(tái)服務(wù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • Android實(shí)現(xiàn)View的拖拽

    Android實(shí)現(xiàn)View的拖拽

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)View的拖拽,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • TabLayout關(guān)聯(lián)ViewPager后不顯示文字的解決方法

    TabLayout關(guān)聯(lián)ViewPager后不顯示文字的解決方法

    這篇文章主要為大家詳細(xì)介紹了TabLayout關(guān)聯(lián)ViewPager后不顯示文字的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11

最新評(píng)論