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