您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關在Java中使用Socket通信會遇到哪些問題,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
Socket通信模型如圖所示:
不管Socket通信的功能有多復雜,任何socket通信過程的基本結構都是一樣的。
其基本步驟為:
①分別在客戶端和服務器端創建Socket和ServerSocket實例;服務器端通過.accept()方法等待請求并阻塞。請求收到后,建立連接Socket對象。
②通過getInputStream和getOutputStream方法分別在客戶端和服務器端打開輸入輸出流
③利用IO流進行讀寫操作
④關閉所有的流資源和套接字資源。
其中,編程工作主要集中在第三步,其他的部分代碼基本相同。所有步驟都可能拋出IO異常!
我在編寫一個簡單的socket程序時,使用的Socket通信出現了一個問題:我在客戶端寫入的數據,在服務器端無法輸出。當我從客戶端斷開連接時,之前寫入的所有數據立刻在服務器端輸出出來了。經過反復的驗證和求解,以下是我的結論和解決方法。希望有同樣問題的小伙伴看完可以解決問題。
通過一端的Socket建立了PrintWriter類來寫入數據,通過另一端的Socket建立了BufferedReader類來讀取數據并輸出。
如果數據寫入后沒有被顯示,可能的原因有兩種:
一、寫入的數據存儲在緩沖區中,沒有被寫入IO流中:
如果不主動的干涉,寫入的數據會一直堆在緩沖區中,直到緩沖區滿了引發JVM自動刷新緩沖區。顯然這不符合我們的需求。對于這種情況,PrintWriter類提供了flush()方法來強制刷新緩沖區,將緩沖區數據寫入IO流中。另外,PrintWriter類的構造器有一個參數”boolean autoflush“,這個參數默認為false,如果設置為true,則會開啟自動刷新緩沖區功能。但是請注意,這里的自動刷新是有觸發條件的,那就是:PrintWriter類寫入數據的方法必須是println、printf或者format方法時,才會觸發自動刷新。如果是調用write()這類方法寫入數據,是不會觸發自動刷新的!總結起來,就是三點:autoflush參數設置,write和println方法的選擇,flush方法的使用。對這三個進行組合,就能保證在Socket通信的某一端寫入數據時,數據一定能成功地寫入到IO流中!
二、讀取數據使用了readLine()方法,該方法沒有正常的結束:
請注意,BufferedReader類的readLine()方法是一個阻塞函數!也就是說,這個方法本身是讀取一行數據,但是它自己識別不了什么叫做“一行”!當調用該方法讀取完一段數據后,它會阻塞,而不會return它的讀取數據。這就是為什么有的時候明明已經刷新了緩沖區正確的寫入數據了,還是通過輸入流讀取數據并顯示出來的原因。
對于readLine()方法,它解除阻塞、正確結束并返回讀取的值,只有以下幾種情況:
①讀取的數據里含有回車符"\r"或者換行符"\n"或者回車換行符"\r\n";
②讀取的數據是在另一端通過println方法寫入的,因為println方法自帶換行符;
③BufferedReader類的緩沖區滿了,那么JVM會自動刷新緩沖區從而釋放“積攢”的數據(但是鑒于默認緩沖區大小為8192個字符,對于小數據量的通信,顯然觸發不了);
④對于讀取的數據,寫入這些數據的流發生異常或者直接關閉,那么readLine()就會把它吃的數據全部吐出來。這就剛好解釋了,為什么在我的程序中,斷開客戶端Socket連接,服務器端立刻輸出所有客戶端消息的原因。
綜上,在Socket通信過程中,保證某一端輸出流的緩沖被刷新,保證另一端的readLine方法能正常停止,即可解決寫入的數據在另一端無法輸出的問題。
以下是我修改后能成功運行的代碼,分別是服務器端Socket和客戶端Socket。
over!
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.io.BufferedWriter; import java.io.OutputStreamWriter; public class ShakingServer{ public static void main(String[] args) throws IOException { //創建服務器套接字實例,設置監聽端口為2000 ServerSocket server=new ServerSocket(2000); //開始監聽客戶端的請求,并阻塞 Socket socket=server.accept(); //請求收到后,自動建立連接。通過IO流進行數據傳輸 System.out.println("連接建立成功"); OutputStream os=socket.getOutputStream(); PrintWriter pw=new PrintWriter(new BufferedWriter(new OutputStreamWriter(os)),true); pw.write("歡迎訪問搖頭耶穌的世界!"); pw.flush(); //因為我關閉了輸出流,所以另一端的readLine方法才正常結束了 socket.shutdownOutput(); InputStream is=socket.getInputStream(); InputStreamReader isr=new InputStreamReader(is); BufferedReader br=new BufferedReader(isr); while(true) { String str=br.readLine(); if(str.equals("quit")) { break; } System.out.println("Client said: "+str); } socket.shutdownInput(); //socket.shutdownOutput(); socket.close(); server.close(); } }
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; public class ShakingClient{ public static void main(String[] args) throws IOException{ //創建客戶端的套接字,設置連接的服務器的IP地址和端口號 Socket socket=new Socket("169.254.132.203",2000); //輸入流讀取服務器發送的信息 BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream())); //開啟自動刷新緩沖區 PrintWriter pw=new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true); //從鍵盤讀取數據 BufferedReader ii=new BufferedReader(new InputStreamReader(System.in)); System.out.println(br.readLine()); //因為開啟了自動刷新,且調用的是println方法,所以可以不調用flush方法 pw.println("請求進入搖頭耶穌的世界"); //pw.flush(); while(true) { String str=ii.readLine(); //使用了回車符來保證另一端的readLine方法正常結束 pw.write(str+"\r"); pw.flush(); //如果輸入quit則退出聊天室 if(str.equals("quit")) { break; } } socket.shutdownInput(); socket.shutdownOutput(); socket.close(); } }
以上就是在Java中使用Socket通信會遇到哪些問題,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。