socket編程時(shí)的發(fā)送與接收數(shù)據(jù)時(shí)的問題解析
socket 編程時(shí)的發(fā)送與接收數(shù)據(jù)時(shí)的問題
在編寫一個測試方法時(shí),需要用啟動一個程序監(jiān)聽一個端口,測試發(fā)送的數(shù)據(jù)是事正常,但是總是出現(xiàn)兩個問題,一是 Socked 總是在 OutputSteam.write () 方法之前被關(guān)閉,但是沒有使用代碼調(diào)用 Socket 的 Close 方法,另一個是在接收數(shù)據(jù)時(shí),總是卡在 InputSteam.read () 方法處 (一般發(fā)生在前一個 Socket 在寫數(shù)據(jù)時(shí)異常中斷后再次有新 Socket 連接時(shí)),得不到數(shù)據(jù),直到發(fā)送端關(guān)閉 Socket, 下面是代碼
package com.zoro.example.subscribe.queue; import com.zoro.util.SendUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; /** * @author zoro * @version 1.0 */ public class TestSubscribeQueue { private static final Logger LOGGER = LoggerFactory.getLogger(TestSubscribeQueue.class); public static void main(String[] args) throws IOException { new Thread(new EventListener()).start(); } private static String test(InputStream is) { BufferedReader br = null; InputStreamReader isr = null; StringBuilder sb = new StringBuilder(); isr = new InputStreamReader(is); br = new BufferedReader(isr); String line = null; while (true) { try { line = br.readLine(); if (line == null || line.length() == 0) { break; } } catch (IOException e) { e.printStackTrace(); } sb.append(line); sb.append("\n"); line = null; LOGGER.debug("當(dāng)前讀取的數(shù)據(jù):{}", sb.toString()); } return sb.toString(); } static class EventListener implements Runnable { private final ServerSocket ss; public EventListener() throws IOException { ss = new ServerSocket(8087); } @Override public void run() { while (true) { /* 這里得到的InputStream 不能在OutputStream返回?cái)?shù)據(jù)之前關(guān)閉,因?yàn)镮nputStream關(guān)閉之后會導(dǎo)致Socket關(guān)閉,你說奇怪不奇怪,這樣一來就不能正常返回?cái)?shù)據(jù)了,報(bào)錯說Socket已經(jīng)關(guān)閉 */ try (Socket s = ss.accept(); InputStream is = s.getInputStream(); OutputStream os = s.getOutputStream()) { LOGGER.debug("等待請求..."); LOGGER.debug("新請求進(jìn)入"); if (s.isClosed() || !s.isConnected() || s.isInputShutdown()) { continue; } // String result = SendUtil.resolveInputStream(is); String result = test(is); LOGGER.debug("收到請求:{}", result); StringBuilder response = new StringBuilder(); response.append("HTTP/1.1 200 OK\r\n"); response.append("Content-Type:text/html\r\n"); response.append("\r\n"); response.append("123252321"); LOGGER.debug("即將返回?cái)?shù)據(jù):{}", response.toString()); if (!s.isClosed()) { LOGGER.debug("正在返回?cái)?shù)據(jù)"); os.write(response.toString().getBytes()); os.flush(); } } catch (IOException e) { e.printStackTrace(); } } } } }
分析原因
- 在第 76 行代碼調(diào)用的方法中 調(diào)用的 InputStream.close () 方法,導(dǎo)致了 Socket 也跟著關(guān)閉了,不清楚是什么原因引起的,但是去掉 InputStream.close () 確實(shí) OutputStream 可以向請求端改善數(shù)據(jù)了
- 在 OutputStream 的問題解決之后,InputStream 的問題也跟著沒有了,因?yàn)槲以跍y試時(shí)使用的是瀏覽器 http 發(fā)起的這個 Socket,所以我猜測是在第一次 Socket 異常中斷后瀏覽器再次觸發(fā)了重試;;
問題更新
兩天后發(fā)現(xiàn)新問題: InputStream 在包裝成 BufferedReader 整行讀取時(shí)到 http 請求的最后一行還會繼續(xù)向下一行讀,但是發(fā)送端這時(shí)已經(jīng)不發(fā)送數(shù)據(jù)了,造成服務(wù)端一直卡在這里等待
問題原因
BufferedReader.readLine () 方法在讀取文件只,因?yàn)槲募詈髸且粋€ - 1 ,所以可以知道在哪里結(jié)束,但網(wǎng)絡(luò)請求最后沒有這個 - 1 所以在讀了最后一行后不知道請求數(shù)據(jù)已經(jīng)沒有更多了,造成一直阻塞
解決思路
http 請求(只討論 get 與 post 兩種方法),get 請求只有請求頭,在讀取到第一個空行時(shí)就可以結(jié)束,post 請求有請求體,可以根據(jù)請求頭中 ContentLength 判斷是否讀取完
以上就是socket編程時(shí)的發(fā)送與接收數(shù)據(jù)時(shí)的問題解析的詳細(xì)內(nèi)容,更多關(guān)于socket發(fā)送接收數(shù)據(jù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java基于Socket實(shí)現(xiàn)簡單的多線程回顯服務(wù)器功能示例
這篇文章主要介紹了Java基于Socket實(shí)現(xiàn)簡單的多線程回顯服務(wù)器功能,結(jié)合實(shí)例形式分析了java使用socket進(jìn)行多線程數(shù)據(jù)傳輸?shù)南嚓P(guān)操作技巧,需要的朋友可以參考下2017-08-08深入解析Java的Struts框架中的控制器DispatchAction
這篇文章主要介紹了深入解析Java的Struts框架中的控制器DispatchAction,Struts是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-12-12Spring boot 默認(rèn)靜態(tài)資源路徑與手動配置訪問路徑的方法
這篇文章主要介紹了Spring boot 默認(rèn)靜態(tài)資源路徑與手動配置訪問路徑的方法,非常不錯,具有參考借鑒價(jià)值,需要的朋友可以參考下2017-05-05springboot項(xiàng)目中idea的pom.xml文件的引用標(biāo)簽全部爆紅問題解決
這篇文章主要介紹了springboot項(xiàng)目中idea的pom.xml文件的引用標(biāo)簽全部爆紅問題解決,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-12-12eclipse自動提示和自動補(bǔ)全功能實(shí)現(xiàn)方法
這篇文章主要介紹了eclipse自動提示和自動補(bǔ)全的相關(guān)內(nèi)容,文中向大家分享了二者的實(shí)現(xiàn)方法代碼,需要的朋友可以了解下。2017-09-09JAVA 實(shí)現(xiàn)延遲隊(duì)列的方法
這篇文章主要介紹了JAVA 實(shí)現(xiàn)延遲隊(duì)列的方法,文中講解非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06利用Jackson實(shí)現(xiàn)數(shù)據(jù)脫敏的示例詳解
在我們的企業(yè)項(xiàng)目中,為了保護(hù)用戶隱私,數(shù)據(jù)脫敏成了必不可少的操作,那么我們怎么優(yōu)雅的利用Jackson實(shí)現(xiàn)數(shù)據(jù)脫敏呢,本文就來和大家詳細(xì)聊聊,希望對大家有所幫助2023-05-05