最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java 使用Socket正確讀取數(shù)據(jù)姿勢(shì)

 更新時(shí)間:2021年10月27日 11:46:08   作者:此非夢(mèng)亦非幻  
這篇文章主要介紹了Java 使用Socket正確讀取數(shù)據(jù)姿勢(shì),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

前言

平時(shí)日常開發(fā)用得最多是Http通訊,接口調(diào)試也比較簡(jiǎn)單的,也有比較強(qiáng)大的框架支持(OkHttp)。

個(gè)人平時(shí)用到socket通訊的地方是Android與外設(shè)通訊,Android與ssl服務(wù)通訊,這種都是基于TCP/IP通訊,而且服務(wù)端和設(shè)備端協(xié)議都是不能修改的,只能按照相關(guān)報(bào)文格式進(jìn)行通信。

但使用socket通訊問題不少,一般有兩個(gè)難點(diǎn):

1、socket通訊層要自己寫及IO流不正確使用,遇到讀取不到數(shù)據(jù)或者阻塞卡死現(xiàn)象或者數(shù)據(jù)讀取不完整

2、請(qǐng)求和響應(yīng)報(bào)文格式多變(json,xml,其它),解析麻煩,如果是前面兩種格式都簡(jiǎn)單,有對(duì)應(yīng)框架處理,其它格式一般都需要自己手動(dòng)處理。

本次基于第1點(diǎn)問題做了總結(jié),歸根結(jié)底是使用read()或readLine()導(dǎo)致的問題

Socket使用流程

1、創(chuàng)建socket

2、連接socket

3、獲取輸入輸出流

字節(jié)流:

   InputStream  mInputStream = mSocket.getInputStream();
   OutputStream  mOutputStream = mSocket.getOutputStream();

字符流:

  BufferedReader mBufferedReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream(), "UTF-8"));
  PrintWriter mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream(), "UTF-8")), true);

至于實(shí)際使用字節(jié)流還是字符流,看實(shí)際情況使用。如果返回是字符串及讀寫與報(bào)文結(jié)束符(/r或/n或/r/n)有關(guān),使用字符流讀取,否則字節(jié)流。

4、讀寫數(shù)據(jù)

5、關(guān)閉socket

如果是Socket短連接,上面五個(gè)步驟都要走一遍;

如果是Socket長連接,只需關(guān)注第4點(diǎn)即可,第4點(diǎn)使用不慎就會(huì)遇到上面出現(xiàn)的問題。

實(shí)際開發(fā)中,長連接使用居多,一次連接,進(jìn)行多次收發(fā)數(shù)據(jù)。

特別注意:使用長連接不能讀完數(shù)據(jù)后立馬關(guān)閉輸入輸出流,必須再最后不使用的時(shí)候關(guān)閉

Socket數(shù)據(jù)讀寫

當(dāng)socket阻塞時(shí),必須設(shè)置讀取超時(shí)時(shí)間,防止調(diào)試時(shí),socket讀取數(shù)據(jù)長期掛起。

mSocket.setSoTimeout(10* 1000);  //設(shè)置客戶端讀取服務(wù)器數(shù)據(jù)超時(shí)時(shí)間

使用read()讀取阻塞問題

日常寫法1:

 mOutputStream.write(bytes);
 mOutputStream.flush();
byte[] buffer = new byte[1024];
int n = 0;
ByteArrayOutputStream output = new ByteArrayOutputStream();
while (-1 != (n = mInputStream .read(buffer))) {
    output.write(buffer, 0, n);
}
//處理數(shù)據(jù)
  output.close();
byte[] result = output.toByteArray();

上面看似沒有什么問題,但有時(shí)候會(huì)出現(xiàn)mInputStream .read(buffer)阻塞,導(dǎo)致while循環(huán)體里面不會(huì)執(zhí)行

日常寫法2:

mOutputStream.write(bytes);
mOutputStream.flush();
int  available = mInputStream.available();
byte[] buffer = new byte[available];
in.read(buffer);

上面雖然不阻塞,但不一定能讀取到數(shù)據(jù),available 可能為0,由于是網(wǎng)絡(luò)通訊,發(fā)送數(shù)據(jù)后不一定馬上返回。

或者對(duì)mInputStream.available()修改為:

 int available = 0;
while (available == 0) {
    available = mInputStream.available();
}

上面雖然能讀取到數(shù)據(jù),但數(shù)據(jù)不一定完整。

而且,available方法返回估計(jì)的當(dāng)前流可用長度,不是當(dāng)前通訊流的總長度,而且是估計(jì)值;read方法讀取流中數(shù)據(jù)到buffer中,但讀取長度為1至buffer.length,若流結(jié)束或遇到異常則返回-1。

最終寫法(遞歸讀?。?/strong>

 /**
     * 遞歸讀取流
     *
     * @param output
     * @param inStream
     * @return
     * @throws Exception
     */
    public void readStreamWithRecursion(ByteArrayOutputStream output, InputStream inStream) throws Exception {
        long start = System.currentTimeMillis();
        while (inStream.available() == 0) {
            if ((System.currentTimeMillis() - start) > 20* 1000) {//超時(shí)退出
                throw new SocketTimeoutException("超時(shí)讀取");
            }
        }
        byte[] buffer = new byte[2048];
        int read = inStream.read(buffer);
        output.write(buffer, 0, read);
        SystemClock.sleep(100);//需要延時(shí)以下,不然還是有概率漏讀
        int a = inStream.available();//再判斷一下,是否有可用字節(jié)數(shù)或者根據(jù)實(shí)際情況驗(yàn)證報(bào)文完整性
        if (a > 0) {
            LogUtils.w("========還有剩余:" + a + "個(gè)字節(jié)數(shù)據(jù)沒讀");
            readStreamWithRecursion(output, inStream);
        }
    }
    /**
     * 讀取字節(jié)
     *
     * @param inStream
     * @return
     * @throws Exception
     */
    private byte[] readStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        readStreamWithRecursion(output, inStream);
        output.close();
        int size = output.size();
        LogUtils.i("本次讀取字節(jié)總數(shù):" + size);
        return output.toByteArray();
    }

上面這種方法讀取完成一次后,固定等待時(shí)間,等待完不一定有數(shù)據(jù),若沒有有數(shù)據(jù),響應(yīng)時(shí)間過長,會(huì)影響用戶體驗(yàn)。我們可以再優(yōu)化一下:

 /**
     * 遞歸讀取流
     *
     * @param output
     * @param inStream
     * @return
     * @throws Exception
     */
    public void readStreamWithRecursion(ByteArrayOutputStream output, InputStream inStream) throws Exception {
        long start = System.currentTimeMillis();
        int time =500;//毫秒,間看實(shí)際情況
        while (inStream.available() == 0) {
            if ((System.currentTimeMillis() - start) >time) {//超時(shí)退出
                throw new SocketTimeoutException("超時(shí)讀取");
            }
        }
        byte[] buffer = new byte[2048];
        int read = inStream.read(buffer);
        output.write(buffer, 0, read);
       int wait = readWait();
        long startWait = System.currentTimeMillis();
        boolean checkExist = false;
        while (System.currentTimeMillis() - startWait <= wait) {
            int a = inStream.available();
            if (a > 0) {
                checkExist = true;
                //            LogUtils.w("========還有剩余:" + a + "個(gè)字節(jié)數(shù)據(jù)沒讀");
                break;
            }
        }
        if (checkExist) {
            if (!checkMessage(buffer, read)) {
                readStreamWithRecursion(output, inStream, timeout);
            }
        }        
    }
    
 /**
     * 讀取等待時(shí)間,單位毫秒
     */
    protected int readWait() {
        return 100;
    }
    
    /**
     * 讀取字節(jié)
     *
     * @param inStream
     * @return
     * @throws Exception
     */
    private byte[] readStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        readStreamWithRecursion(output, inStream);
        output.close();
        int size = output.size();
        LogUtils.i("本次讀取字節(jié)總數(shù):" + size);
        return output.toByteArray();
    }

上面這種延遲率大幅降低,目前正在使用該方法讀取,再也沒有出現(xiàn)數(shù)據(jù)讀取不完整和阻塞現(xiàn)象。不過這種,讀取也要注意報(bào)文結(jié)束符問題,何時(shí)讀取完畢問題。

使用readreadLine()讀取阻塞問題

日常寫法:

 mPrintWriter.print(sendData+ "\r\n");   
 mPrintWriter.flush();
 String msg = mBufferedReader.readLine();
 //處理數(shù)據(jù)

細(xì)心的你發(fā)現(xiàn),發(fā)送數(shù)據(jù)時(shí)添加了結(jié)束符,如果不加結(jié)束符,導(dǎo)致readLine()阻塞,讀不到任何數(shù)據(jù),最終拋出SocketTimeoutException異常

特別注意:

報(bào)文結(jié)束符:根據(jù)實(shí)際服務(wù)器規(guī)定的來添加,必要時(shí)問后端開發(fā)人員或者看接口文檔是否有說明

不然在接口調(diào)試上會(huì)浪費(fèi)很多寶貴的時(shí)間,影響后期功能開發(fā)。

使用readLine()注意事項(xiàng):

  • 1、讀入的數(shù)據(jù)要注意有/r或/n或/r/n

這句話意思是服務(wù)端寫完數(shù)據(jù)后,會(huì)打印報(bào)文結(jié)束符/r或/n或/r/n;

同理,客戶端寫數(shù)據(jù)時(shí)也要打印報(bào)文結(jié)束符,這樣服務(wù)端才能讀取到數(shù)據(jù)。

  • 2、沒有數(shù)據(jù)時(shí)會(huì)阻塞,在數(shù)據(jù)流異?;驍嚅_時(shí)才會(huì)返回null
  • 3、使用socket之類的數(shù)據(jù)流時(shí),要避免使用readLine(),以免為了等待一個(gè)換行/回車符而一直阻塞

上面長連接是發(fā)送一次數(shù)據(jù)和讀一次數(shù)據(jù),保證了當(dāng)次通訊的完整性,必須要時(shí)需要同步處理。

也有長連接,客戶端開線程循環(huán)阻塞等待服務(wù)端數(shù)據(jù)發(fā)送數(shù)據(jù)過來,比如:消息推送。平時(shí)使用長連接都是分別使用不同的命令發(fā)送數(shù)據(jù)且接收數(shù)據(jù),來完成不同的任務(wù)。

總結(jié)

實(shí)際開發(fā)中,長連接比較復(fù)雜,還要考慮心跳,丟包,斷開重連等問題。使用長連接時(shí),要特別注意報(bào)文結(jié)束符問題,結(jié)束符只是用來告訴客戶端或服務(wù)端數(shù)據(jù)已經(jīng)發(fā)送完畢,客戶端或服務(wù)端可以讀取數(shù)據(jù)了,否則客戶端或服務(wù)端會(huì)一直阻塞在read()或者readLine()方法。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • springboot對(duì)象為null的屬性在json中不顯示的解決

    springboot對(duì)象為null的屬性在json中不顯示的解決

    這篇文章主要介紹了springboot對(duì)象為null的屬性在json中不顯示的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • 最新Spring Security實(shí)戰(zhàn)教程之Spring Security安全框架指南

    最新Spring Security實(shí)戰(zhàn)教程之Spring Security安全框架指南

    SpringSecurity是Spring生態(tài)系統(tǒng)中的核心組件,提供認(rèn)證、授權(quán)和防護(hù)機(jī)制,以保護(hù)應(yīng)用免受各種安全威脅,它支持多種認(rèn)證方式,并通過攔截器和過濾器鏈進(jìn)行安全檢查,本文通過搭建SpringBoot+SpringSecurity項(xiàng)目,幫助如何快速上手并應(yīng)用SpringSecurity,感興趣的朋友一起看看吧
    2025-03-03
  • Java中兩個(gè)List之間的比較方法(差集、交集和并集)

    Java中兩個(gè)List之間的比較方法(差集、交集和并集)

    在業(yè)務(wù)的開發(fā)過程中會(huì)經(jīng)常用到兩個(gè)List集合相互取值的情況,下面這篇文章主要給大家介紹了關(guān)于Java中兩個(gè)List之間的比較方法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • idea無法打斷點(diǎn),單擊或雙擊代碼行左側(cè)區(qū)域無效的解決

    idea無法打斷點(diǎn),單擊或雙擊代碼行左側(cè)區(qū)域無效的解決

    這篇文章主要介紹了idea無法打斷點(diǎn),單擊或雙擊代碼行左側(cè)區(qū)域無效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • SpringCloud?Gateway之請(qǐng)求應(yīng)答日志打印方式

    SpringCloud?Gateway之請(qǐng)求應(yīng)答日志打印方式

    這篇文章主要介紹了SpringCloud?Gateway之請(qǐng)求應(yīng)答日志打印方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • SpringBoot定時(shí)任務(wù)調(diào)度與爬蟲的配置實(shí)現(xiàn)

    SpringBoot定時(shí)任務(wù)調(diào)度與爬蟲的配置實(shí)現(xiàn)

    這篇文章主要介紹了SpringBoot定時(shí)任務(wù)調(diào)度與爬蟲的實(shí)現(xiàn),使用webmagic開發(fā)爬蟲,繼承PageProcessor接口編寫自己的處理類,process是定制爬蟲邏輯的核心接口,在這里編寫抽取邏輯,具體實(shí)現(xiàn)配置過程跟隨小編一起看看吧
    2022-01-01
  • SpringBoot整合SpringSession實(shí)現(xiàn)分布式登錄詳情

    SpringBoot整合SpringSession實(shí)現(xiàn)分布式登錄詳情

    這篇文章主要介紹了SpringBoot整合SpringSession實(shí)現(xiàn)分布式登錄詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下
    2022-08-08
  • SpringBoot面試突擊之過濾器和攔截器區(qū)別詳解

    SpringBoot面試突擊之過濾器和攔截器區(qū)別詳解

    過濾器(Filter)和攔截器(Interceptor)都是基于?AOP(Aspect?Oriented?Programming,面向切面編程)思想實(shí)現(xiàn)的,用來解決項(xiàng)目中某一類問題的兩種“工具”,但二者有著明顯的差距,接下來我們一起來看
    2022-10-10
  • 解決response.setHeader設(shè)置下載文件名無效的問題

    解決response.setHeader設(shè)置下載文件名無效的問題

    這篇文章主要介紹了解決response.setHeader設(shè)置下載文件名無效的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • springBoot接入阿里云oss的實(shí)現(xiàn)步驟

    springBoot接入阿里云oss的實(shí)現(xiàn)步驟

    這篇文章主要介紹了springBoot接入阿里云oss的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01

最新評(píng)論

保靖县| 阜新市| 鄂托克前旗| 科尔| 民和| 葫芦岛市| 增城市| 诸城市| 高邑县| 棋牌| 武城县| 旬邑县| 措美县| 台湾省| 耒阳市| 镶黄旗| 鸡泽县| 马鞍山市| 拉萨市| 龙山县| 阿勒泰市| 德钦县| 绵竹市| 宜章县| 吴忠市| 临清市| 商都县| 晋州市| 黎城县| 灵武市| 三原县| 霸州市| 平陆县| 游戏| 基隆市| 姚安县| 张家川| 蒙自县| 双流县| 徐汇区| 灵宝市|