Java socket通訊實現(xiàn)過程及問題解決
這篇文章主要介紹了Java socket通訊實現(xiàn)過程及問題解決,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
本來是打算驗證java socket是不是單線程操作,也就是一次只能處理一個請求,處理完之后才能繼續(xù)處理下一個請求。但是在其中又發(fā)現(xiàn)了許多問題,在編程的時候需要十分注意,今天就拿出來跟大家分享一下。
首先先建立一個服務(wù)端代碼,運行時也要先啟動此程序。
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)聽端口
private static int port = 8081;
public static void main(String[] args) throws InterruptedException {
try {
//1.得到一個socket服務(wù)端
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
// 2.等待socket客戶端的請求。accept方法在有連接請求時才會返回
System.out.println("等待客戶端請求。。。");
Socket socket = serverSocket.accept();
System.out.println("客戶端請求來了。。。");
// 3.獲取socket輸入流
InputStream inputStream = socket.getInputStream();
/* BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
System.out.println("接收到的請求數(shù)據(jù)為:" + bufferedReader.readLine());*/
// 讀取請求內(nèi)容的緩沖區(qū)
byte[] bytes = new byte[1024];
int length = 0;
StringBuilder sb = new StringBuilder();
//獲取客戶端請求的內(nèi)容
while ((length = inputStream.read(bytes)) != -1) {
sb.append(new String(bytes, 0, length, "utf-8"));
}
System.out.println("接收到的請求數(shù)據(jù)為:" + sb.toString());
//Thread.sleep(50000); // 4.獲取socket輸出流
OutputStream outputStream = socket.getOutputStream();
PrintWriter printWriter = new PrintWriter(outputStream);
String backStr = "服務(wù)端接收到了請求";
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)聽失敗!" + e);
}
}
}
此代碼模擬了正常系統(tǒng)成socket服務(wù)端的方式,就是一個無限循環(huán)監(jiān)聽我們綁定的端口,當(dā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請求ip地址
private static String host = "127.0.0.1";
//socket請求端口
private static int port = 8081;
public static void main(String[] args) {
try {
//1.建立一個客戶端
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("接收到的返回數(shù)據(jù)為:" + sb);
//4.關(guān)閉資源
printWriter.close();
outputStream.close();
inputStream.close();
socket.close();
} catch (Exception e) {
System.err.println("socket請求失敗" + e);
}
}
}
客戶端代碼主要就是向服務(wù)端發(fā)送數(shù)據(jù),然后等待服務(wù)端的響應(yīng),打印出服務(wù)端的響應(yīng)內(nèi)容。
最終打印結(jié)果如下。服務(wù)端:

客戶端:

首先明確幾個概念,下面將會用到。
flush()方法:用于清空緩沖區(qū)的數(shù)據(jù)流,進行流的操作時,數(shù)據(jù)先被讀到內(nèi)存緩沖區(qū)中,然后再用數(shù)據(jù)寫到文件中。
socket.shutdownOutput()方法:他是一種單向關(guān)閉流的方法,即關(guān)閉客戶端的輸出流并不會關(guān)閉服務(wù)端的輸出流。通過shutdownOutput()方法只是關(guān)閉了輸出流,但socket仍然是連接狀態(tài),連接并未關(guān)閉。
printWriter.close()方法:如果直接關(guān)閉輸入或者輸出流,即:in.close()或者out.close(),會直接關(guān)閉socket。
流中的關(guān)閉順序:一般情況下是:先打開的后關(guān)閉,后打開的先關(guān)閉。另一種情況:看依賴關(guān)系,如果流a依賴流b,應(yīng)該先關(guān)閉流a,再關(guān)閉流b,例如處理流a依賴節(jié)點流b,應(yīng)該先關(guān)閉處理流a,再關(guān)閉節(jié)點流b。當(dāng)然完全可以只關(guān)閉處理流,不用關(guān)閉節(jié)點流。處理流關(guān)閉的時候,會調(diào)用其處理的節(jié)點流的關(guān)閉方法。如果將節(jié)點流關(guān)閉以后再關(guān)閉處理流,會拋出IO異常。
下面總結(jié)下我遇到的問題。
1.客戶端發(fā)送數(shù)據(jù)部分的代碼,printWriter.flush(); socket.shutdownOutput(); 這兩句代碼十分的重要,flush()方法如果不添加的話,服務(wù)端接收到的數(shù)據(jù)將為空,shutdownOutput()方法不添加的話,服務(wù)端將一直等待讀取客戶端的數(shù)據(jù),不會往下進行,大家可以自測一下。我自己的理解是flush()的作用是為了把數(shù)據(jù)從內(nèi)存中刷新到socket流中,shutdownOutput()方法是告訴服務(wù)端,我沒有東西要傳輸了,所以服務(wù)端也就會停止等待讀取客戶端發(fā)送的內(nèi)容,程序就可以繼續(xù)向下走。
2.打開服務(wù)端中的sleep方法,在新建一個客戶端,同時開啟請求服務(wù)端,會發(fā)現(xiàn)服務(wù)端確實是一個連接一個連接的處理,所以這也是socket性能所在的問題。
3.如果不用字符流讀取,客戶端發(fā)送數(shù)據(jù)直接用outputStream.write(sendStr.getBytes());,可以發(fā)現(xiàn)此時不用調(diào)用flush()方法,但是socket.shutdownOutput()依然需要。這是因為直接讀取到socket的輸出流,并沒有讀到內(nèi)存中。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
MyBatis-Flex BaseMapper的接口基本用法小結(jié)
本文主要介紹了MyBatis-Flex BaseMapper的接口基本用法小結(jié),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-02-02
postman測試post請求參數(shù)為json類型的實例講解
下面小編就為大家分享一篇postman測試post請求參數(shù)為json類型的實例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-03-03
Java基于Google zxing生成帶logo的二維碼圖片
zxing是一個開放源碼的,用java實現(xiàn)的多種格式的1D/2D條碼圖像處理庫,本文主要介紹了Java基于Google zxing生成帶logo的二維碼圖片,具有一定的參考價值,感興趣的可以了解一下2023-10-10

