一文帶你學(xué)會Java網(wǎng)絡(luò)編程
1.java網(wǎng)絡(luò)編程概述
網(wǎng)絡(luò)編程是指編寫運(yùn)行在多個(gè)設(shè)備(計(jì)算機(jī))的程序,這些設(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保障了兩個(gè)應(yīng)用程序之間的可靠通信。通常用于互聯(lián)網(wǎng)協(xié)議,被稱 TCP / IP。
UDP:UDP (英語:User Datagram Protocol,用戶數(shù)據(jù)報(bào)協(xié)議),位于 OSI模型的傳輸層。一個(gè)無連接的協(xié)議。提供了應(yīng)用程序之間要發(fā)送數(shù)據(jù)的數(shù)據(jù)報(bào)。由于UDP缺乏可靠性且屬于無連接協(xié)議,所以應(yīng)用程序通常必須容許一些丟失、錯誤或重復(fù)的數(shù)據(jù)包。
2.InetAddress類
這個(gè)類表示互聯(lián)網(wǎng)協(xié)議(IP)地址。下面演示了 Socket 編程時(shí)比較有用的方法:
import java.net.InetAddress; import java.net.UnknownHostException; /** * InetAddress類演示 */ public class InetAddressTest { public static void main(String[] args) throws UnknownHostException { // 獲取本機(jī)的InetAddress對象:主機(jī)名 + IP地址 InetAddress localHost = InetAddress.getLocalHost(); System.out.println(localHost); // 根據(jù)主機(jī)名獲取InetAddress對象 InetAddress host1 = InetAddress.getByName("Dahe-Windows11"); System.out.println(host1); // 根據(jù)域名獲取InetAddress對象 InetAddress host2 = InetAddress.getByName("www.baidu.com"); System.out.println(host2); // 通過InetAddress對象獲取對應(yīng)的地址 String hostAddress = host2.getHostAddress(); System.out.println(hostAddress); // 通過InetAddress對象獲取主機(jī)名或者域名 String hostName = host2.getHostName(); System.out.println(hostName); } }
輸出:
XXX-WindowsXX/192.168.0.1
XXX-WindowsXX/192.168.0.1
www.baidu.com/39.156.66.18
39.156.66.18
www.baidu.com
3.Socket 編程
套接字使用TCP提供了兩臺計(jì)算機(jī)之間的通信機(jī)制。 客戶端程序創(chuàng)建一個(gè)套接字,并嘗試連接服務(wù)器的套接字。
當(dāng)連接建立時(shí),服務(wù)器會創(chuàng)建一個(gè) Socket 對象??蛻舳撕头?wù)器現(xiàn)在可以通過對 Socket 對象的寫入和讀取來進(jìn)行通信。
java.net.Socket 類代表一個(gè)套接字,并且 java.net.ServerSocket 類為服務(wù)器程序提供了一種來監(jiān)聽客戶端,并與他們建立連接的機(jī)制。
以下步驟在兩臺計(jì)算機(jī)之間使用套接字建立TCP連接時(shí)會出現(xiàn):
- 服務(wù)器實(shí)例化一個(gè) ServerSocket 對象,表示通過服務(wù)器上的端口通信。
- 服務(wù)器調(diào)用 ServerSocket 類的 accept() 方法,該方法將一直等待,直到客戶端連接到服務(wù)器上給定的端口。
- 服務(wù)器正在等待時(shí),一個(gè)客戶端實(shí)例化一個(gè) Socket 對象,指定服務(wù)器名稱和端口號來請求連接。
- Socket 類的構(gòu)造函數(shù)試圖將客戶端連接到指定的服務(wù)器和端口號。如果通信被建立,則在客戶端創(chuàng)建一個(gè) Socket對象能夠與服務(wù)器進(jìn)行通信。
- 在服務(wù)器端,accept() 方法返回服務(wù)器上一個(gè)新的 socket 引用,該 socket 連接到客戶端的 socket。
連接建立后,通過使用 I/O 流在進(jìn)行通信,每一個(gè)socket都有一個(gè)輸出流和一個(gè)輸入流,客戶端的輸出流連接到服務(wù)器端的輸入流,而客戶端的輸入流連接到服務(wù)器端的輸出流。
TCP 是一個(gè)雙向的通信協(xié)議,因此數(shù)據(jù)可以通過兩個(gè)數(shù)據(jù)流在同一時(shí)間發(fā)送
4.TCP編程
TCP字節(jié)流編程
我們來模擬一個(gè)服務(wù)端和客戶端通信的過程:
服務(wù)端:
import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; /** * 服務(wù)端 */ public class SocketServer { public static void main(String[] args) throws IOException { // 在本地的9999端口進(jìn)行監(jiān)聽 // 細(xì)節(jié):需要確保9999端口處于空閑狀態(tài) ServerSocket serverSocket = new ServerSocket(9999); // 沒有客戶端鏈接時(shí),會阻塞,等待鏈接 // 有客戶端鏈接,則會返回一個(gè)Socket對象 Socket socket = serverSocket.accept(); // 通過輸入流獲取客戶端發(fā)來的數(shù)據(jù) InputStream inputStream = socket.getInputStream(); // 讀取內(nèi)容 byte[] buf = new byte[1024]; int readLne = 0; while ((readLne = inputStream.read(buf)) != -1) { System.out.println(new String(buf, 0, readLne)); } // 關(guān)閉資源 inputStream.close(); socket.close(); serverSocket.close(); } }
客戶端:
import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; /** * 客戶端 */ public class SocketClient { public static void main(String[] args) throws IOException { // 鏈接服務(wù)端,由于是測試程序,直接獲取本機(jī)的地址即可 // 鏈接本機(jī)的9999端口,鏈接成功會返回一個(gè)Socket對象 Socket socket = new Socket(InetAddress.getLocalHost(), 9999); // 創(chuàng)建流向服務(wù)器端發(fā)送數(shù)據(jù) OutputStream outputStream = socket.getOutputStream(); outputStream.write("Hello Server".getBytes()); // 關(guān)閉輸出流對象和socket outputStream.close(); socket.close(); System.out.println("客戶端退出!"); } }
同時(shí)運(yùn)行服務(wù)端和客戶端,該示例代碼,客戶端會向服務(wù)端發(fā)送一個(gè)流信息:Hello Server
接下來,我們來看一個(gè)更為復(fù)雜的例子:實(shí)現(xiàn)客戶端和服務(wù)端的雙通信
服務(wù)端:
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; /** * 服務(wù)端 */ public class SocketServer { public static void main(String[] args) throws IOException { // 在本地的9999端口進(jìn)行監(jiān)聽 // 細(xì)節(jié):需要確保9999端口處于空閑狀態(tài) ServerSocket serverSocket = new ServerSocket(9999); // 沒有客戶端鏈接時(shí),會阻塞,等待鏈接 // 有客戶端鏈接,則會返回一個(gè)Socket對象 Socket socket = serverSocket.accept(); // 通過輸入流獲取客戶端發(fā)來的數(shù)據(jù) InputStream inputStream = socket.getInputStream(); // 讀取內(nèi)容 byte[] buf = new byte[1024]; int readLne = 0; while ((readLne = inputStream.read(buf)) != -1) { System.out.println(new String(buf, 0, readLne)); } // 向客戶端回送消息 OutputStream outputStream = socket.getOutputStream(); outputStream.write("hello,client".getBytes()); // 設(shè)置結(jié)束標(biāo)記 socket.shutdownOutput(); // 關(guān)閉資源 inputStream.close(); outputStream.close(); socket.close(); serverSocket.close(); } }
客戶端:
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; /** * 客戶端 */ public class SocketClient { public static void main(String[] args) throws IOException { // 鏈接服務(wù)端,由于是測試程序,直接獲取本機(jī)的地址即可 // 鏈接本機(jī)的9999端口,鏈接成功會返回一個(gè)Socket對象 Socket socket = new Socket(InetAddress.getLocalHost(), 9999); // 創(chuàng)建流向服務(wù)器端發(fā)送數(shù)據(jù) OutputStream outputStream = socket.getOutputStream(); outputStream.write("Hello Server".getBytes()); // 設(shè)置結(jié)束標(biāo)記 socket.shutdownOutput(); // 獲取服務(wù)端的回送數(shù)據(jù) InputStream inputStream = socket.getInputStream(); byte[] buf = new byte[1024]; int readLen = 0; while ((readLen = inputStream.read(buf)) != -1) { System.out.println(new String(buf, 0, readLen)); } // 關(guān)閉輸出流對象和socket outputStream.close(); socket.close(); System.out.println("客戶端退出!"); } }
需要注意:雙端通信需要設(shè)置結(jié)束標(biāo)記,否則會相互等待,陷入僵持
TCP字符流編程
字符流編程,需要使用轉(zhuǎn)換流的技術(shù)
直接上代碼:
客戶端:
import java.io.*; import java.net.InetAddress; import java.net.Socket; /** * 基于字符流的TCP編程 * 客戶端 */ public class CharacterSocketClient { public static void main(String[] args) throws IOException { // 鏈接服務(wù)端,由于是測試程序,直接獲取本機(jī)的地址即可 // 鏈接本機(jī)的9999端口,鏈接成功會返回一個(gè)Socket對象 Socket socket = new Socket(InetAddress.getLocalHost(), 9999); // 創(chuàng)建流向服務(wù)器端發(fā)送數(shù)據(jù) OutputStream outputStream = socket.getOutputStream(); // 使用IO轉(zhuǎn)換流 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream)); bw.write("hello,server 字符流"); // 插入換行符,表示寫入的內(nèi)容結(jié)束 bw.newLine(); // 使用字符流,必須手動刷新,否則數(shù)據(jù)將不會寫入通道 bw.flush(); // 設(shè)置結(jié)束標(biāo)記 socket.shutdownOutput(); // 獲取服務(wù)端的回送數(shù)據(jù) InputStream inputStream = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(inputStream)); String s = br.readLine(); System.out.println(s); // 關(guān)閉輸出流對象和socket br.close(); bw.close(); outputStream.close(); socket.close(); System.out.println("客戶端退出!"); } }
服務(wù)端:
import java.io.*; import java.net.ServerSocket; import java.net.Socket; /** * 基于字符流的TCP編程 * 服務(wù)端 */ public class CharacterSocketServer { public static void main(String[] args) throws IOException { // 在本地的9999端口進(jìn)行監(jiān)聽 // 細(xì)節(jié):需要確保9999端口處于空閑狀態(tài) ServerSocket serverSocket = new ServerSocket(9999); // 沒有客戶端鏈接時(shí),會阻塞,等待鏈接 // 有客戶端鏈接,則會返回一個(gè)Socket對象 Socket socket = serverSocket.accept(); // 通過輸入流獲取客戶端發(fā)來的數(shù)據(jù) InputStream inputStream = socket.getInputStream(); // IO轉(zhuǎn)換流 BufferedReader br = new BufferedReader(new InputStreamReader(inputStream)); // 必須使用readLine方式來讀 String s = br.readLine(); System.out.println(s); // 向客戶端回送消息 OutputStream outputStream = socket.getOutputStream(); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream)); bw.write("Hello,Client 字符流"); bw.newLine(); bw.flush(); // 關(guān)閉資源 br.close(); bw.close(); inputStream.close(); outputStream.close(); socket.close(); serverSocket.close(); } }
5.網(wǎng)絡(luò)上傳文件
服務(wù)端代碼:
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.net.ServerSocket; import java.net.Socket; /** * 文件上傳,服務(wù)端 */ public class TCPFileUploadServer { public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(8888); Socket socket = serverSocket.accept(); BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()); byte[] bytes = StreamUtils.streamToByteArray(bis); String destFilePath = "networkprogramming\\tcp\\color.jpg"; BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath)); bos.write(bytes); bos.close(); // 關(guān)閉資源 bis.close(); socket.close(); serverSocket.close(); } }
客戶端代碼:
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.net.InetAddress; import java.net.Socket; /** * 文件上傳,客戶端 */ public class TCPFileUploadClient { public static void main(String[] args) throws Exception { Socket socket = new Socket(InetAddress.getLocalHost(), 8888); // 創(chuàng)建讀取磁盤文件的輸入流 String filePath = "D:\\color.jpg"; BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath)); // 此時(shí)的bytes就是文件的字節(jié)內(nèi)容 byte[] bytes = StreamUtils.streamToByteArray(bis); // 通過socket獲取到輸出流,將bytes數(shù)據(jù)發(fā)送給服務(wù)端 BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); bos.write(bytes); bis.close(); // 寫入數(shù)據(jù)結(jié)束標(biāo)記 socket.shutdownOutput(); // 關(guān)閉資源 bos.close(); socket.close(); } }
6.TCP文件下載
客戶端代碼:
import java.io.*; import java.net.InetAddress; import java.net.Socket; import java.util.Scanner; /** * TCP文件下載客戶端 */ public class TCPFileDownloadClient { public static void main(String[] args) throws Exception { Scanner scanner = new Scanner(System.in); System.out.println("請輸入下載文件名:"); String downloadFileName = scanner.next(); Socket socket = new Socket(InetAddress.getLocalHost(), 9999); OutputStream outputStream = socket.getOutputStream(); outputStream.write(downloadFileName.getBytes()); // 設(shè)置寫入結(jié)束標(biāo)志 socket.shutdownOutput(); // 接受服務(wù)器返回的文件字節(jié)數(shù)組 BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()); byte[] bytes = StreamUtils.streamToByteArray(bis); // 將文件寫入磁盤 String filePath = "D:\\" + downloadFileName + ".jpg"; BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath)); bos.write(bytes); bos.close(); socket.close(); outputStream.close(); bis.close(); } }
服務(wù)端代碼:
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; /** * TCP文件下載服務(wù)端 */ public class TCPFileDownloadServer { public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(9999); Socket socket = serverSocket.accept(); // 讀取客戶端發(fā)送的要下載的文件名稱 InputStream inputStream = socket.getInputStream(); byte[] b = new byte[1024]; int len = 0; // 客戶端要下載的文件名 String downloadFileName = ""; while ((len = inputStream.read(b)) != -1) { downloadFileName += new String(b, 0, len); } // 提供給客戶端下載的實(shí)際文件名 String resFileName = ""; if ("color".equals(downloadFileName)) { resFileName = "networkprogramming\\tcp\\color.jpg"; } else { resFileName = "networkprogramming\\tcp\\fish.jpg"; } BufferedInputStream bis = new BufferedInputStream(new FileInputStream(resFileName)); // 使用工具類將文件保存到一個(gè)字節(jié)數(shù)組中 byte[] bytes = StreamUtils.streamToByteArray(bis); BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); // 寫入到數(shù)據(jù)通道,返回給客戶端 bos.write(bytes); socket.shutdownOutput(); inputStream.close(); socket.close(); serverSocket.close(); } }
以上就是一文帶你學(xué)會Java網(wǎng)絡(luò)編程的詳細(xì)內(nèi)容,更多關(guān)于Java網(wǎng)絡(luò)編程的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
spring boot 默認(rèn)異常處理的實(shí)現(xiàn)
這篇文章主要介紹了spring boot 默認(rèn)異常處理的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04

基于Java反射的map自動裝配JavaBean工具類設(shè)計(jì)示例代碼

controller函數(shù)中參數(shù)列表使用多個(gè)@RequestBody問題

java實(shí)現(xiàn)二分法查找出數(shù)組重復(fù)數(shù)字