【Java 网络编程】TCP 数据传输示例 ( 客户端参数设置 | 服务器端参数设置 | ByteBuffer 存放读取数据类型 )
生活随笔
收集整理的這篇文章主要介紹了
【Java 网络编程】TCP 数据传输示例 ( 客户端参数设置 | 服务器端参数设置 | ByteBuffer 存放读取数据类型 )
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- I 客戶端代碼示例
- II 服務器端代碼示例
- III 運行結果
I 客戶端代碼示例
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Inet4Address; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.ByteBuffer;/*** TCP 客戶端*/ public class Client {public static void main(String[] args) {try {//I. 創建 Socket 對象并綁定本地端口//1. 創建空的 Socket 對象 , 方便之后設置參數Socket socket = new Socket();//2. 綁定本地端口socket.bind(new InetSocketAddress(Inet4Address.getLocalHost(), 8887));System.out.println("客戶端 Socket 創建完畢");//II. 設置 Socket 對象參數 , 注意這些參數只能在客戶端沒有連接服務器的時候設置 , 連接服務器之后設置是無效的//1. 設置從 Socket 對象輸入流中讀取數據的阻塞等待超時時間// 當與 Socket 對象關聯的 InputStream 輸入流執行 read() 操作時 , 其阻塞時間為這個超時時間// 如果超過了該時間還沒有收到任何數據 , 就會拋出異常socket.setSoTimeout(3000);//2. 設置是否可以復用 Socket 綁定的地址和端口號// Socket 連接在建立時 , 會使用之前綁定本地的 IP 地址和端口號// 這個端口號在使用之后 , 2 分鐘之內不允許再次使用// 進行了該設置之后 , 可以在連接關閉之后 , 馬上使用該本地 IP 地址和端口號socket.setReuseAddress(true);//3. 設置是否開啟 Nagle 算法 , Nagle 算法會導致多次發送的少量數據合并 , 即沾包情況出現// 在需要低延遲傳輸的情況下是需要關閉該算法的 , 該算法會導致數據沾包情況出現socket.setTcpNoDelay(true);//4. 在長時間 ( 2 小時 ) 沒有數據交互 , 是否需要發送心跳包確認連接socket.setKeepAlive(true);//5. 調用 Socket 對象的 close 方法之后的處理方式// 1> 默認情況 : false , 0// 如果 boolean on 設置成false , 不處理連接的緩存數據 , 調用 close 會立刻關閉連接// 系統底層會操作輸出流發送剩余緩存數據 , 將緩沖區中的數據發送給連接對方// 如果設置 false 不會產生阻塞操作// 2> setSoLinger( true , 20 ) 情況 :// 如果設置 boolean on 參數為 true , int linger 參數設置一個大于等于 0 的參數// 那么在關閉的時候 , 阻塞 linger 毫秒 , 之后緩沖區如果還有數據 , 就會被丟棄// 直接向連接對方發送結束命令 , 無需經過超時等待// 3> setSoLinger( true , 0 ) 情況 :// 如果設置成 0 , 那么其后果是不阻塞 , 也不讓系統接管輸出流// 立刻丟棄緩沖區數據 , 向對方發送 RST 命令socket.setSoLinger(true, 10);//6. 設置緊急數據是否內斂 , 默認情況時 false 關閉的// 緊急數據 : 緊急數據是 Socket 對象通過調用 sendUrgentData 發送出去的數據// 該方法參數是一個 int 值 , 僅有最低的 8 位是有效的socket.setOOBInline(true);//7. 設置發送接收緩沖區大小socket.setReceiveBufferSize(64 * 1024 * 1024);socket.setSendBufferSize(64 * 1024 * 1024);//8. 設置性能參數 : ① 連接時長 , ② 最低延遲 , ③ 帶寬// 設置的值不是具體的參數 , 而是連接的性能權重 , 對哪個性能要求比較高 ;// 上面的延遲和帶寬的性能是互斥的 , 低延遲新能好 , 帶寬性能就差socket.setPerformancePreferences(0, 2, 0);System.out.println("客戶端 Socket 參數設置完畢");//III. 連接服務器//1. 連接到服務器端的 8888 端口 , 設置連接超時 3000 毫秒socket.connect(new InetSocketAddress(Inet4Address.getLocalHost(), 8888), 3000);System.out.println("客戶端 Socket 連接服務器完畢");//IV. 數據發送與接收//1. 獲取輸出流和輸入流OutputStream outputStream = socket.getOutputStream();InputStream inputStream = socket.getInputStream();//2. 使用 ByteBuffer 向 byte[] 數組中存儲數據byte[] buffer = new byte[256];ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);//3. 向數組寫入 byte 類型數據byteBuffer.put((byte) 0x01);//4. 向數組中寫入 short 類型數據byteBuffer.putShort((short) 1);//5. 向數組中寫入 int 類型數據byteBuffer.putInt(1);//6. 向數組中寫入 char 類型數據byteBuffer.putChar('a');//7. 向數組中寫入 boolean 類型數據// 此處使用 byte 類型模擬 , true 為 1, false 為 0boolean bool = true;byteBuffer.put((byte) (bool ? 1 : 0));//8. 向數組中寫入 long 類型數據byteBuffer.putLong((long)1);//9. 向數組中寫入 float 類型數據byteBuffer.putFloat(3.14f);//10. 向數組中寫入 double 類型數據byteBuffer.putDouble(3.14);//11. 向數組中寫入 String 類型數據// 先把 String 字符串轉為 byte[] 數組, 在放入 byteBuffer 中byteBuffer.put("Hello World".getBytes());//12. 將 byte[] 數據發送到服務器端outputStream.write(buffer, 0, byteBuffer.position() + 1);System.out.println("客戶端 Socket 將各種類型數據發送到了服務器端");//13. 接收服務器端反饋的數據int readLen = inputStream.read(buffer);System.out.println("客戶端 Socket 接收到服務器端數據 " + readLen + " 字節");//V. 釋放資源//1. 關閉輸入輸出流outputStream.close();inputStream.close();} catch (IOException e) {e.printStackTrace();}}}
II 服務器端代碼示例
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Inet4Address; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.nio.ByteBuffer;/*** TCP 服務器端*/ public class Server {public static void main(String[] args) {try {//I. 設置服務器套接字//1. 創建服務器端 , 注意創建一個空的服務器套接字 , 一遍后面設置更詳細的參數ServerSocket serverSocket = new ServerSocket();System.out.println("服務器端端 ServerSocket 創建完畢");//2. 設置從 Socket 對象輸入流中讀取數據的阻塞等待超時時間// 當與 Socket 對象關聯的 InputStream 輸入流執行 read() 操作時 , 其阻塞時間為這個超時時間// 如果超過了該時間還沒有收到任何數據 , 就會拋出異常serverSocket.setSoTimeout(30000);//3. 設置是否可以復用 Socket 綁定的地址和端口號// Socket 連接在建立時 , 會使用之前綁定本地的 IP 地址和端口號// 這個端口號在使用之后 , 2 分鐘之內不允許再次使用// 進行了該設置之后 , 可以在連接關閉之后 , 馬上使用該本地 IP 地址和端口號serverSocket.setReuseAddress(true);//4. 設置發送接收緩沖區大小serverSocket.setReceiveBufferSize(64 * 1024 * 1024);//5. 設置性能參數 : ① 連接時長 , ② 最低延遲 , ③ 帶寬// 設置的值不是具體的參數 , 而是連接的性能權重 , 對哪個性能要求比較高 ;// 上面的延遲和帶寬的性能是互斥的 , 低延遲新能好 , 帶寬性能就差serverSocket.setPerformancePreferences(0, 2, 0);System.out.println("服務器端端 ServerSocket 設置完畢");//6. 綁定本地端口 , 只有綁定了本地端口 , 服務器端套接字才能正式工作// 服務器端才算是正式創建完畢// 上面的設置一定要在綁定接口之前設置完畢 , 之后在設置 serverSocket 是無效的serverSocket.bind(new InetSocketAddress(Inet4Address.getLocalHost(), 8888), 88);System.out.println("服務器端端 ServerSocket 綁定 8888 端口完畢");//II. 等待服務器端連接//1. 服務器端阻塞 , 等待客戶端連接服務器端的 8888 端口號Socket clientSocket = serverSocket.accept();//2. 創建客戶端異步處理線程 , 處理服務器端與該客戶端之間的交互 , 創建之后直接啟動線程即可ClientHandler clientHandler = new ClientHandler(clientSocket);clientHandler.start();} catch (IOException e) {e.printStackTrace();}}/*** 客戶端異步處理線程* 每當有客戶端連接服務器 , 就開啟一個線程處理與該客戶端之間的交互*/private static class ClientHandler extends Thread {/*** 客戶端線程*/private Socket clientSocket;public ClientHandler(Socket clientSocket) {this.clientSocket = clientSocket;}@Overridepublic void run() {super.run();System.out.println("客戶端 : " + clientSocket.getInetAddress() + " 連接到服務器端");try {//I. 獲取數據交互的輸入流 , 輸出流 , 及緩沖區//1. 從客戶端 Socket 中獲取與客戶端進行數據交互的輸入輸出流OutputStream outputStream = clientSocket.getOutputStream();InputStream inputStream = clientSocket.getInputStream();//2. 從客戶端讀取數據 , 并使用 ByteBuffer 讀取其中各種類型的數據byte[] buffer = new byte[256];int readCount = inputStream.read(buffer);ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, 0, readCount);//II. 按照順序讀取存放的數據//注意 : 要按照存放的順序讀取//1. 讀取 byte 類型數據byte var_byte = byteBuffer.get();System.out.println("① byte 類型數據 : " + var_byte);//2. 讀取 short 類型數據short var_short = byteBuffer.getShort();System.out.println("② short 類型數據 : " + var_short);//3. 讀取 int 類型數據int var_int = byteBuffer.getInt();System.out.println("③ int 類型數據 : " + var_int);//4. 讀取 char 類型數據char var_char = byteBuffer.getChar();System.out.println("④ char 類型數據 : " + var_char);//5. 讀取 short 類型數據boolean var_boolean = byteBuffer.get() == 1;System.out.println("⑤ boolean 類型數據 : " + var_boolean);//6. 讀取 long 類型數據long var_long = byteBuffer.getLong();System.out.println("⑥ long 類型數據 : " + var_long);//7. 讀取 float 類型數據float var_float = byteBuffer.getFloat();System.out.println("⑦ float 類型數據 : " + var_float);//8. 讀取 double 類型數據double var_double = byteBuffer.getDouble();System.out.println("⑧ double 類型數據 : " + var_double);//9. 讀取 short 類型數據int start = byteBuffer.position();String var_string = new String(buffer, start, readCount - start - 1);System.out.println("⑨ String 類型數據 : " + var_string);//III. 將接收的數據再發送回去, 并關閉連接outputStream.write(buffer, 0, readCount);outputStream.close();inputStream.close();} catch (IOException e) {e.printStackTrace();} finally {// 連接關閉try {clientSocket.close();} catch (IOException e) {e.printStackTrace();} finally {System.out.println("客戶端與服務器端交互完成");}}}}}
III 運行結果
1. 先運行服務器端 :
服務器端端 ServerSocket 創建完畢 服務器端端 ServerSocket 設置完畢 服務器端端 ServerSocket 綁定 8888 端口完畢2. 在運行客戶端 :
客戶端 Socket 創建完畢 客戶端 Socket 參數設置完畢 客戶端 Socket 連接服務器完畢 客戶端 Socket 將各種類型數據發送到了服務器端 客戶端 Socket 接收到服務器端數據 42 字節3. 最終查看服務器端打印 :
服務器端端 ServerSocket 創建完畢 服務器端端 ServerSocket 設置完畢 服務器端端 ServerSocket 綁定 8888 端口完畢 客戶端 : /192.168.87.2 連接到服務器端 ① byte 類型數據 : 1 ② short 類型數據 : 1 ③ int 類型數據 : 1 ④ char 類型數據 : a ⑤ boolean 類型數據 : true ⑥ long 類型數據 : 1 ⑦ float 類型數據 : 3.14 ⑧ double 類型數據 : 3.14 ⑨ String 類型數據 : Hello World 客戶端與服務器端交互完成總結
以上是生活随笔為你收集整理的【Java 网络编程】TCP 数据传输示例 ( 客户端参数设置 | 服务器端参数设置 | ByteBuffer 存放读取数据类型 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Java 网络编程】服务器端 Serv
- 下一篇: 【Java 集合】Java 集合主要脉络