您好,登錄后才能下訂單哦!
這篇文章主要介紹了Java socket通訊實(shí)現(xiàn)過(guò)程及問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
本來(lái)是打算驗(yàn)證java socket是不是單線程操作,也就是一次只能處理一個(gè)請(qǐng)求,處理完之后才能繼續(xù)處理下一個(gè)請(qǐng)求。但是在其中又發(fā)現(xiàn)了許多問(wèn)題,在編程的時(shí)候需要十分注意,今天就拿出來(lái)跟大家分享一下。
首先先建立一個(gè)服務(wù)端代碼,運(yùn)行時(shí)也要先啟動(dòng)此程序。
package com.test.some.Socket; import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; /** * @Description: socket服務(wù)端代碼 * @Author: haoqiangwang3 * @CreateDate: 2020/1/9 */ public class MySocketServer1 { // 服務(wù)器監(jiān)聽(tīng)端口 private static int port = 8081; public static void main(String[] args) throws InterruptedException { try { //1.得到一個(gè)socket服務(wù)端 ServerSocket serverSocket = new ServerSocket(port); while (true) { // 2.等待socket客戶端的請(qǐng)求。accept方法在有連接請(qǐng)求時(shí)才會(huì)返回 System.out.println("等待客戶端請(qǐng)求。。。"); Socket socket = serverSocket.accept(); System.out.println("客戶端請(qǐng)求來(lái)了。。。"); // 3.獲取socket輸入流 InputStream inputStream = socket.getInputStream(); /* BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); System.out.println("接收到的請(qǐng)求數(shù)據(jù)為:" + bufferedReader.readLine());*/ // 讀取請(qǐng)求內(nèi)容的緩沖區(qū) byte[] bytes = new byte[1024]; int length = 0; StringBuilder sb = new StringBuilder(); //獲取客戶端請(qǐng)求的內(nèi)容 while ((length = inputStream.read(bytes)) != -1) { sb.append(new String(bytes, 0, length, "utf-8")); } System.out.println("接收到的請(qǐng)求數(shù)據(jù)為:" + sb.toString()); //Thread.sleep(50000); // 4.獲取socket輸出流 OutputStream outputStream = socket.getOutputStream(); PrintWriter printWriter = new PrintWriter(outputStream); String backStr = "服務(wù)端接收到了請(qǐng)求"; printWriter.write(new String(backStr.getBytes(), "utf-8")); printWriter.flush(); //5.關(guān)閉資源 //bufferedReader.close(); inputStream.close(); printWriter.close(); outputStream.close(); socket.close(); } } catch (IOException e) { System.err.println("socket監(jiān)聽(tīng)失??!" + e); } } }
此代碼模擬了正常系統(tǒng)成socket服務(wù)端的方式,就是一個(gè)無(wú)限循環(huán)監(jiān)聽(tīng)我們綁定的端口,當(dāng)有客戶端請(qǐng)求來(lái)了之后進(jìn)行處理。
下面就是客戶端請(qǐng)求代碼
package com.test.some.Socket; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; /** * @Description: socket客戶端代碼 * @Author: haoqiangwang3 * @CreateDate: 2020/1/9 */ public class MySocketClient1 { //socket請(qǐng)求ip地址 private static String host = "127.0.0.1"; //socket請(qǐng)求端口 private static int port = 8081; public static void main(String[] args) { try { //1.建立一個(gè)客戶端 Socket socket = new Socket(host, port); //2.得到socket輸出流 OutputStream outputStream = socket.getOutputStream(); PrintWriter printWriter = new PrintWriter(outputStream); String sendStr = "發(fā)送數(shù)據(jù)1"; //發(fā)送數(shù)據(jù) printWriter.write(sendStr); printWriter.flush(); socket.shutdownOutput(); //3.得到socket輸入流 InputStream inputStream = socket.getInputStream(); StringBuilder sb = new StringBuilder(); byte[] bytes = new byte[1024]; while (inputStream.read(bytes) != -1) { sb.append(new String(bytes, "utf-8")); } System.out.println("接收到的返回?cái)?shù)據(jù)為:" + sb); //4.關(guān)閉資源 printWriter.close(); outputStream.close(); inputStream.close(); socket.close(); } catch (Exception e) { System.err.println("socket請(qǐng)求失敗" + e); } } }
客戶端代碼主要就是向服務(wù)端發(fā)送數(shù)據(jù),然后等待服務(wù)端的響應(yīng),打印出服務(wù)端的響應(yīng)內(nèi)容。
最終打印結(jié)果如下。服務(wù)端:
客戶端:
首先明確幾個(gè)概念,下面將會(huì)用到。
flush()方法:用于清空緩沖區(qū)的數(shù)據(jù)流,進(jìn)行流的操作時(shí),數(shù)據(jù)先被讀到內(nèi)存緩沖區(qū)中,然后再用數(shù)據(jù)寫到文件中。
socket.shutdownOutput()方法:他是一種單向關(guān)閉流的方法,即關(guān)閉客戶端的輸出流并不會(huì)關(guān)閉服務(wù)端的輸出流。通過(guò)shutdownOutput()方法只是關(guān)閉了輸出流,但socket仍然是連接狀態(tài),連接并未關(guān)閉。
printWriter.close()方法:如果直接關(guān)閉輸入或者輸出流,即:in.close()或者out.close(),會(huì)直接關(guān)閉socket。
流中的關(guān)閉順序:一般情況下是:先打開(kāi)的后關(guān)閉,后打開(kāi)的先關(guān)閉。另一種情況:看依賴關(guān)系,如果流a依賴流b,應(yīng)該先關(guān)閉流a,再關(guān)閉流b,例如處理流a依賴節(jié)點(diǎn)流b,應(yīng)該先關(guān)閉處理流a,再關(guān)閉節(jié)點(diǎn)流b。當(dāng)然完全可以只關(guān)閉處理流,不用關(guān)閉節(jié)點(diǎn)流。處理流關(guān)閉的時(shí)候,會(huì)調(diào)用其處理的節(jié)點(diǎn)流的關(guān)閉方法。如果將節(jié)點(diǎn)流關(guān)閉以后再關(guān)閉處理流,會(huì)拋出IO異常。
下面總結(jié)下我遇到的問(wèn)題。
1.客戶端發(fā)送數(shù)據(jù)部分的代碼,printWriter.flush(); socket.shutdownOutput(); 這兩句代碼十分的重要,flush()方法如果不添加的話,服務(wù)端接收到的數(shù)據(jù)將為空,shutdownOutput()方法不添加的話,服務(wù)端將一直等待讀取客戶端的數(shù)據(jù),不會(huì)往下進(jìn)行,大家可以自測(cè)一下。我自己的理解是flush()的作用是為了把數(shù)據(jù)從內(nèi)存中刷新到socket流中,shutdownOutput()方法是告訴服務(wù)端,我沒(méi)有東西要傳輸了,所以服務(wù)端也就會(huì)停止等待讀取客戶端發(fā)送的內(nèi)容,程序就可以繼續(xù)向下走。
2.打開(kāi)服務(wù)端中的sleep方法,在新建一個(gè)客戶端,同時(shí)開(kāi)啟請(qǐng)求服務(wù)端,會(huì)發(fā)現(xiàn)服務(wù)端確實(shí)是一個(gè)連接一個(gè)連接的處理,所以這也是socket性能所在的問(wèn)題。
3.如果不用字符流讀取,客戶端發(fā)送數(shù)據(jù)直接用outputStream.write(sendStr.getBytes());,可以發(fā)現(xiàn)此時(shí)不用調(diào)用flush()方法,但是socket.shutdownOutput()依然需要。這是因?yàn)橹苯幼x取到socket的輸出流,并沒(méi)有讀到內(nèi)存中。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。