Java網(wǎng)絡(luò)編程基礎(chǔ)用法詳解
網(wǎng)絡(luò)編程是指編寫運行在多個設(shè)備(計算機)的程序,這些設(shè)備都通過網(wǎng)絡(luò)連接起來。java.net 包中 J2SE 的 API 包含有類和接口,它們提供低層次的通信細(xì)節(jié)。你可以直接使用這些類和接口,來專注于解決問題,而不用關(guān)注通信細(xì)節(jié)。
java.net 包中提供了兩種常見的網(wǎng)絡(luò)協(xié)議的支持:
- TCP:TCP(英語:Transmission Control Protocol,傳輸控制協(xié)議) 是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議,TCP 層是位于 IP 層之上,應(yīng)用層之下的中間層。TCP 保障了兩個應(yīng)用程序之間的可靠通信。通常用于互聯(lián)網(wǎng)協(xié)議,被稱 TCP / IP。
- UDP:UDP (英語:User Datagram Protocol,用戶數(shù)據(jù)報協(xié)議),位于 OSI 模型的傳輸層。一個無連接的協(xié)議。提供了應(yīng)用程序之間要發(fā)送數(shù)據(jù)的數(shù)據(jù)報。由于UDP缺乏可靠性且屬于無連接協(xié)議,所以應(yīng)用程序通常必須容許一些丟失、錯誤或重復(fù)的數(shù)據(jù)包。
Socket 編程
套接字使用TCP提供了兩臺計算機之間的通信機制。 客戶端程序創(chuàng)建一個套接字,并嘗試連接服務(wù)器的套接字。當(dāng)連接建立時,服務(wù)器會創(chuàng)建一個 Socket 對象??蛻舳撕头?wù)器現(xiàn)在可以通過對 Socket 對象的寫入和讀取來進(jìn)行通信。
java.net.Socket 類代表一個套接字,并且 java.net.ServerSocket 類為服務(wù)器程序提供了一種來監(jiān)聽客戶端,并與他們建立連接的機制。以下步驟在兩臺計算機之間使用套接字建立TCP連接時會出現(xiàn):
- 服務(wù)器實例化一個 ServerSocket 對象,表示通過服務(wù)器上的端口通信。
- 服務(wù)器調(diào)用 ServerSocket 類的 accept() 方法,該方法將一直等待,直到客戶端連接到服務(wù)器上給定的端口。
- 服務(wù)器正在等待時,一個客戶端實例化一個 Socket 對象,指定服務(wù)器名稱和端口號來請求連接。
- Socket 類的構(gòu)造函數(shù)試圖將客戶端連接到指定的服務(wù)器和端口號。如果通信被建立,則在客戶端創(chuàng)建一個 Socket 對象能夠與服務(wù)器進(jìn)行通信。
- 在服務(wù)器端,accept() 方法返回服務(wù)器上一個新的 socket 引用,該 socket 連接到客戶端的 socket。
連接建立后,通過使用 I/O 流在進(jìn)行通信,每一個socket都有一個輸出流和一個輸入流,客戶端的輸出流連接到服務(wù)器端的輸入流,而客戶端的輸入流連接到服務(wù)器端的輸出流。TCP 是一個雙向的通信協(xié)議,因此數(shù)據(jù)可以通過兩個數(shù)據(jù)流在同一時間發(fā)送。
ServerSocket 類的方法
服務(wù)器應(yīng)用程序通過使用 java.net.ServerSocket 類以獲取一個端口,并且偵聽客戶端請求。ServerSocket 類有四個構(gòu)造方法:
序號 | 方法描述 |
---|---|
1 | public ServerSocket(int port) throws IOException,創(chuàng)建綁定到特定端口的服務(wù)器套接字 |
2 | public ServerSocket(int port, int backlog) throws IOException,利用指定的 backlog 創(chuàng)建服務(wù)器套接字并將其綁定到指定的本地端口號 |
3 | public ServerSocket(int port, int backlog, InetAddress address) throws IOException,使用指定的端口、偵聽 backlog 和要綁定到的本地 IP 地址創(chuàng)建服務(wù)器 |
4 | public ServerSocket() throws IOException,創(chuàng)建非綁定服務(wù)器套接字 |
創(chuàng)建非綁定服務(wù)器套接字。 如果 ServerSocket 構(gòu)造方法沒有拋出異常,就意味著你的應(yīng)用程序已經(jīng)成功綁定到指定的端口,并且偵聽客戶端請求。
ServerSocket 類的常用方法:
序號 | 方法描述 |
---|---|
1 | public int getLocalPort(),返回此套接字在其上偵聽的端口 |
2 | public Socket accept() throws IOException,偵聽并接受到此套接字的連接,該方法將阻塞,直到建立連接為止 |
3 | public void setSoTimeout(int timeout),通過指定超時值啟用/禁用 SO_TIMEOUT,以毫秒為單位 |
4 | public void bind(SocketAddress host, int backlog),將 ServerSocket 綁定到特定地址(IP 地址和端口號) |
Socket 類的方法
java.net.Socket 類代表客戶端和服務(wù)器都用來互相溝通的套接字??蛻舳艘@取一個 Socket 對象通過實例化 ,而服務(wù)器獲得一個 Socket 對象則通過 accept() 方法的返回值。Socket 類有五個構(gòu)造方法:
序號 | 方法描述 |
---|---|
1 | public Socket(String host, int port) throws UnknownHostException, IOException,創(chuàng)建一個流套接字并將其連接到指定主機上的指定端口號 |
2 | public Socket(InetAddress host, int port) throws IOException,創(chuàng)建一個流套接字并將其連接到指定 IP 地址的指定端口號 |
3 | public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException,創(chuàng)建一個套接字并將其連接到指定遠(yuǎn)程主機上的指定遠(yuǎn)程端口 |
4 | public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException,創(chuàng)建一個套接字并將其連接到指定遠(yuǎn)程地址上的指定遠(yuǎn)程端口 |
5 | public Socket(),通過系統(tǒng)默認(rèn)類型的 SocketImpl 創(chuàng)建未連接套接字 |
當(dāng) Socket 構(gòu)造方法返回,它實際上會嘗試連接到指定的服務(wù)器和端口,而并不是簡單的實例化了一個 Socket 對象。
下面列出了一些感興趣的方法,注意客戶端和服務(wù)器端都有一個 Socket 對象,所以無論客戶端還是服務(wù)端都能夠調(diào)用這些方法:
序號 | 方法描述 |
---|---|
1 | public void connect(SocketAddress host, int timeout) throws IOException,將此套接字連接到服務(wù)器,并指定一個超時值 |
2 | public InetAddress getInetAddress(),返回套接字連接的地址 |
3 | public int getPort(),返回此套接字連接到的遠(yuǎn)程端口 |
4 | public int getLocalPort(),返回此套接字綁定到的本地端口 |
5 | public SocketAddress getRemoteSocketAddress(),返回此套接字連接的端點的地址,如果未連接則返回 null |
6 | public InputStream getInputStream() throws IOException,返回此套接字的輸入流 |
7 | public OutputStream getOutputStream() throws IOException,返回此套接字的輸出流 |
8 | public void close() throws IOException,關(guān)閉此套接字 |
InetAddress 類的方法
這個類表示互聯(lián)網(wǎng)協(xié)議(IP)地址。下面列出了 Socket 編程時比較有用的方法:
序號 | 方法描述 |
---|---|
1 | static InetAddress getByAddress(byte[] addr),在給定原始 IP 地址的情況下,返回 InetAddress 對象 |
2 | static InetAddress getByAddress(String host, byte[] addr),根據(jù)提供的主機名和 IP 地址創(chuàng)建 InetAddress |
3 | static InetAddress getByName(String host),在給定主機名的情況下確定主機的 IP 地址 |
4 | String getHostAddress() ,返回 IP 地址字符串(以文本表現(xiàn)形式) |
5 | String getHostName() ,獲取此 IP 地址的主機名 |
6 | static InetAddress getLocalHost(),返回本地主機 |
7 | String toString(),將此 IP 地址轉(zhuǎn)換為 String |
Socket 通訊實例
客戶端實例:GreetingClient 是一個客戶端程序,該程序通過 socket 連接到服務(wù)器并發(fā)送一個請求,然后等待一個響應(yīng)。
import java.io.*; import java.net.Socket; import java.util.ArrayList; public class GreetingClient { public static void main(String [] args) { //服務(wù)端主機名 String serverName = "localhost"; //服務(wù)端口號 int port = Integer.parseInt("6066"); try { // 輸出服務(wù)端主機名與端口號 System.out.println("連接到主機:" + serverName + " ,端口號:" + port); // 創(chuàng)建 Socket 對象,并嘗試連接到指定的服務(wù)器和端口 Socket client = new Socket(serverName, port); // 輸出此套接字連接的端點的地址 System.out.println("遠(yuǎn)程主機地址:" + client.getRemoteSocketAddress()); // 獲取此套接字的輸出流 OutputStream outToServer = client.getOutputStream(); // 使用此套接字的輸出流創(chuàng)建 DataOutputStream 輸出流 DataOutputStream out = new DataOutputStream(outToServer); // 以UTF-8編碼將字符串寫入基礎(chǔ)輸出流 out.writeUTF("客戶端說:Hello from " + client.getLocalSocketAddress() + "\n"); // 獲取此套接字的輸入流 InputStream inFromServer = client.getInputStream(); // 創(chuàng)建讀取鍵盤輸入內(nèi)容的 InputStreamReader InputStreamReader fingerboardReader = new InputStreamReader(System.in,"UTF-8"); // 創(chuàng)建讀取服務(wù)端輸入內(nèi)容的 InputStreamReader InputStreamReader serverDataReader = new InputStreamReader(inFromServer,"UTF-8"); // 創(chuàng)建存放在鍵盤讀取字符的數(shù)組集合 ArrayList<Character> fingerboardList = new ArrayList<Character>(); // 創(chuàng)建存放在服務(wù)端端讀取字符的數(shù)組集合 ArrayList<Character> serverDataList = new ArrayList<Character>(); do{ // 判斷讀取鍵盤輸入內(nèi)容的 InputStreamReader 是否可以讀取 if(fingerboardReader.ready()){ // 讀取鍵盤輸入內(nèi)容的 InputStreamReader 可以讀取 // 清空 存放鍵盤讀取字符的數(shù)組集合 fingerboardList.clear(); // 聲明 鍵盤輸入char變量 char fingerboardInputChar; // 讀取鍵盤輸入內(nèi)容的 InputStreamReader 可以讀取,循環(huán)讀取,將讀取的內(nèi)容存入數(shù)組集合,一次輸入讀取完成后ready()返回NO while (fingerboardReader.ready()) { fingerboardInputChar = (char)fingerboardReader.read();; fingerboardList.add(fingerboardInputChar); } // 創(chuàng)建可變字符串 StringBuilder 對象 StringBuilder fingerboardStringBuilder = new StringBuilder(); // 拼接 存放鍵盤讀取字符的數(shù)組集合 中的字符組成字符串 for (Character cha :fingerboardList){ fingerboardStringBuilder.append(cha); } // 將可變字符串 StringBuilder 對象 轉(zhuǎn)為字符串 String 對象 String fingerboardString = fingerboardStringBuilder.toString(); // 如果 轉(zhuǎn)換的字符串不是空的 并且不是一個換行符 if(!fingerboardString.isEmpty() && !fingerboardString.equals("\n")) { // 使用此套接字的輸出流寫出數(shù)據(jù) out.writeUTF("客戶端說:" + fingerboardString); // 如果輸出Goodbye!,跳出循環(huán) if (fingerboardString.equals("Goodbye!\n")) { break; } } } // 判斷讀取客戶端輸入內(nèi)容的 InputStreamReader 是否可以讀取 if(serverDataReader.ready()) { // 讀取客戶端輸入內(nèi)容的 InputStreamReader 可以讀取 // 清空客戶端讀取字符的數(shù)組集合 serverDataList.clear(); // 聲明客戶端讀取char變量 char serverDataReaderChar; // 讀取客戶端輸入內(nèi)容的 InputStreamReader 可以讀取,循環(huán)讀取,將讀取的內(nèi)容存入數(shù)組集合,一次輸入讀取完成后ready()返回NO while (serverDataReader.ready()) { serverDataReaderChar = (char) serverDataReader.read(); serverDataList.add(serverDataReaderChar); } // 創(chuàng)建可變字符串 StringBuilder 對象 StringBuilder dataReaderStringBuilder = new StringBuilder(); for (Character cha :serverDataList){ dataReaderStringBuilder.append(cha); } // 將可變字符串 StringBuilder 對象 轉(zhuǎn)為字符串 String 對象 String dataReaderString = dataReaderStringBuilder.toString(); // 打印服務(wù)端輸入內(nèi)容 System.out.print(dataReaderString); // 服務(wù)端輸出Goodbye,跳出循環(huán) if (dataReaderString.endsWith("Goodbye!\n")) { break; } } }while (true); // 關(guān)閉此套接字 client.close(); }catch(IOException e) { e.printStackTrace(); } } }
服務(wù)端實例:GreetingServer 程序是一個服務(wù)器端應(yīng)用程序,使用 Socket 來監(jiān)聽一個指定的端口。
import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.util.ArrayList; public class GreetingServer extends Thread { private ServerSocket serverSocket; public GreetingServer(int port) throws IOException { // 創(chuàng)建服務(wù)端Socket對象 this.serverSocket = new ServerSocket(port); // Socket超時時間,設(shè)置為0時無限制,超時引發(fā)SocketTimeoutException this.serverSocket.setSoTimeout(0); } public void run() { try { // 輸出此套接字在其上偵聽的端口 System.out.println("等待遠(yuǎn)程連接,端口號為:" + serverSocket.getLocalPort() + "..."); // 偵聽并接受到此套接字的連接,該方法將阻塞,直到建立連接為止 Socket server = serverSocket.accept(); // 輸出此套接字連接的端點的地址 System.out.println("遠(yuǎn)程主機地址:" + server.getRemoteSocketAddress()); // 使用此套接字的輸出流創(chuàng)建 DataOutputStream 輸出流 DataOutputStream out = new DataOutputStream(server.getOutputStream()); // 以UTF-8編碼將字符串寫入基礎(chǔ)輸出流 out.writeUTF("服務(wù)端說:謝謝連接我:" + server.getLocalSocketAddress() + "\n"); // 創(chuàng)建讀取鍵盤輸入內(nèi)容的 InputStreamReader InputStreamReader fingerboardReader = new InputStreamReader(System.in,"UTF-8"); // 創(chuàng)建讀取客戶端輸入內(nèi)容的 InputStreamReader InputStreamReader clientDataReader = new InputStreamReader(server.getInputStream(),"UTF-8"); // 創(chuàng)建存放在鍵盤讀取字符的數(shù)組集合 ArrayList<Character> fingerboardList = new ArrayList<Character>(); // 創(chuàng)建存放在客戶端讀取字符的數(shù)組集合 ArrayList<Character> clientDataList = new ArrayList<Character>(); do{ // 判斷讀取鍵盤輸入內(nèi)容的 InputStreamReader 是否可以讀取 if(fingerboardReader.ready()){ // 讀取鍵盤輸入內(nèi)容的 InputStreamReader 可以讀取 // 清空 存放鍵盤讀取字符的數(shù)組集合 fingerboardList.clear(); // 聲明 鍵盤輸入char變量 char fingerboardInputChar; // 讀取鍵盤輸入內(nèi)容的 InputStreamReader 可以讀取,循環(huán)讀取,將讀取的內(nèi)容存入數(shù)組集合,一次輸入讀取完成后ready()返回NO while (fingerboardReader.ready()) { fingerboardInputChar = (char)fingerboardReader.read();; fingerboardList.add(fingerboardInputChar); } // 創(chuàng)建可變字符串 StringBuilder 對象 StringBuilder fingerboardStringBuilder = new StringBuilder(); // 拼接 存放鍵盤讀取字符的數(shù)組集合 中的字符組成字符串 for (Character cha :fingerboardList){ fingerboardStringBuilder.append(cha); } // 將可變字符串 StringBuilder 對象 轉(zhuǎn)為字符串 String 對象 String fingerboardString = fingerboardStringBuilder.toString(); // 如果 轉(zhuǎn)換的字符串不是空的 并且不是一個換行符 if(!fingerboardString.isEmpty() && !fingerboardString.equals("\n")) { // 使用此套接字的輸出流寫出數(shù)據(jù) out.writeUTF("服務(wù)端說:" + fingerboardString); // 如果輸出Goodbye!,跳出循環(huán) if (fingerboardString.equals("Goodbye!\n")) { break; } } } // 判斷讀取客戶端輸入內(nèi)容的 InputStreamReader 是否可以讀取 if(clientDataReader.ready()) { // 讀取客戶端輸入內(nèi)容的 InputStreamReader 可以讀取 // 清空客戶端讀取字符的數(shù)組集合 clientDataList.clear(); // 聲明客戶端讀取char變量 char clientDataReaderChar; // 讀取客戶端輸入內(nèi)容的 InputStreamReader 可以讀取,循環(huán)讀取,將讀取的內(nèi)容存入數(shù)組集合,一次輸入讀取完成后ready()返回NO while (clientDataReader.ready()) { clientDataReaderChar = (char) clientDataReader.read(); clientDataList.add(clientDataReaderChar); } // 創(chuàng)建可變字符串 StringBuilder 對象 StringBuilder dataReaderStringBuilder = new StringBuilder(); for (Character cha :clientDataList){ dataReaderStringBuilder.append(cha); } // 將可變字符串 StringBuilder 對象 轉(zhuǎn)為字符串 String 對象 String dataReaderString = dataReaderStringBuilder.toString(); // 打印客戶端輸入內(nèi)容 System.out.print(dataReaderString); // 客戶端輸出Goodbye,跳出循環(huán) if (dataReaderString.endsWith("Goodbye!\n")) { break; } } }while (true); // 關(guān)閉此套接字 server.close(); System.out.println("套接字已關(guān)閉"); }catch(SocketTimeoutException s) { // Socket超時 System.out.println("Socket timed out!"); }catch(IOException e) { e.printStackTrace(); } } public static void main(String [] args) { int port = Integer.parseInt("6066"); try { Thread t = new GreetingServer(port); t.run(); }catch(IOException e) { e.printStackTrace(); } } }
先運行服務(wù)端程序,再運行客戶端程序,打印如下:
方法實例:
獲取指定主機的IP地址:
使用 InetAddress 類的 InetAddress.getByName() 方法來獲取指定主機(網(wǎng)址)的IP地址:
public class Test { public static void main(String[] args) { InetAddress address = null; try { // 在給定主機名稱的情況下,確定主機的IP地址 address = InetAddress.getByName("www.juejin.cn"); } catch (UnknownHostException e) { System.exit(2); } // 輸出取此IP地址的主機名與IP地址字符串 System.out.println(address.getHostName() + " = " + address.getHostAddress()); System.exit(0); } } // 以上程序執(zhí)行結(jié)果為: // www.juejin.cn = 111.132.34.239
查看端口是否已使用:
public class Test { public static void main(String[] args) { System.out.println("端口是否真正在使用:" + isSocketAliveUitlitybyCrunchify("www.juejin.cn",80)); } /** * 判斷主機端口是否已使用 * * @param hostName 主機名 * @param port 主機端口 * @return boolean - true/false */ public static boolean isSocketAliveUitlitybyCrunchify(String hostName, int port) { // 主機端口是否已使用,默認(rèn)否 boolean isAlive = false; // 超時設(shè)置,單位毫秒 int timeout = 2000; // 創(chuàng)建一個套接字 SocketAddress socketAddress = new InetSocketAddress(hostName, port); Socket socket = new Socket(); System.out.println("主機名: " + hostName + ", 主機端口: " + port); try { // 將此套接字連接到服務(wù)器 socket.connect(socketAddress, timeout); // 關(guān)閉此套接字 socket.close(); // 連接未發(fā)生異常,說明主機端口是使用的 isAlive = true; } catch (SocketTimeoutException exception) { System.out.println("套接字超時異常 " + hostName + ":" + port + ". " + exception.getMessage()); } catch (IOException exception) { System.out.println( "IO異常 - 無法連接到 " + hostName + ":" + port + ". " + exception.getMessage()); } // 返回主機端口是否已使用的結(jié)果 return isAlive; } } // 以上程序執(zhí)行結(jié)果為: // 主機名: www.juejin.cn, 主機端口: 80 // 端口是否真正在使用:true
獲取本機ip地址及主機名:
public class Test { public static void main(String[] args) throws UnknownHostException { InetAddress addr = InetAddress.getLocalHost(); System.out.println("本地主機地址: " + addr.getHostAddress()); String hostname = addr.getHostName(); System.out.println("本地主機名: "+hostname); } }
使用 Socket 連接到指定主機:
public class Test { public static void main(String[] args) { try { InetAddress addr; Socket sock = new Socket("www.juejin.cn", 80); addr = sock.getInetAddress(); System.out.println("連接到 " + addr); sock.close(); } catch (java.io.IOException e) { System.out.println("無法連接 " + args[0]); System.out.println(e); } } } // 以上程序執(zhí)行結(jié)果為: // 連接到 www.juejin.cn/111.132.34.240
以上就是Java網(wǎng)絡(luò)編程基礎(chǔ)用法詳解的詳細(xì)內(nèi)容,更多關(guān)于Java 網(wǎng)絡(luò)編程的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring Boot項目添加外部Jar包以及配置多數(shù)據(jù)源的完整步驟
這篇文章主要給大家介紹了關(guān)于Spring Boot項目添加外部Jar包以及配置多數(shù)據(jù)源的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06springMVC使用ajaxFailUpload上傳圖片的方法
這篇文章主要介紹了springMVC使用ajaxFailUpload上傳圖片的相關(guān)知識,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-06-06springMVC返回復(fù)雜的json格式數(shù)據(jù)方法
下面小編就為大家分享一篇springMVC返回復(fù)雜的json格式數(shù)據(jù)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-03-03java僅用30行代碼就實現(xiàn)了視頻轉(zhuǎn)音頻的批量轉(zhuǎn)換
這篇文章主要介紹了java僅用30行代碼就實現(xiàn)了視頻轉(zhuǎn)音頻的批量轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04Thread類interrupt interrupted及isInterrupted區(qū)別
這篇文章主要為大家介紹了Thread類interrupt interrupted及isInterrupted區(qū)別,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10Java數(shù)據(jù)庫連接池的幾種配置方法(以MySQL數(shù)據(jù)庫為例)
這篇文章主要介紹了Java數(shù)據(jù)庫連接池的幾種配置方法(以MySQL數(shù)據(jù)庫為例) 的相關(guān)資料,需要的朋友可以參考下2016-07-07Java?如何通過注解實現(xiàn)接口輸出時數(shù)據(jù)脫敏
這篇文章主要介紹了Java?如何通過注解實現(xiàn)接口輸出時數(shù)據(jù)脫敏,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12