一文帶你學(xué)會Java網(wǎng)絡(luò)編程
1.java網(wǎng)絡(luò)編程概述
網(wǎng)絡(luò)編程是指編寫運行在多個設(shè)備(計算機)的程序,這些設(shè)備都通過網(wǎng)絡(luò)連接起來。
java.net 包中 J2SE 的 API 包含有類和接口,它們提供低層次的通信細節(jié)。你可以直接使用這些類和接口,來專注于解決問題,而不用關(guān)注通信細節(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ù)包。
2.InetAddress類
這個類表示互聯(lián)網(wǎng)協(xié)議(IP)地址。下面演示了 Socket 編程時比較有用的方法:
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* InetAddress類演示
*/
public class InetAddressTest {
public static void main(String[] args) throws UnknownHostException {
// 獲取本機的InetAddress對象:主機名 + IP地址
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);
// 根據(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對象獲取主機名或者域名
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提供了兩臺計算機之間的通信機制。 客戶端程序創(chuàng)建一個套接字,并嘗試連接服務(wù)器的套接字。
當連接建立時,服務(wù)器會創(chuàng)建一個 Socket 對象??蛻舳撕头?wù)器現(xiàn)在可以通過對 Socket 對象的寫入和讀取來進行通信。
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ù)器進行通信。
- 在服務(wù)器端,accept() 方法返回服務(wù)器上一個新的 socket 引用,該 socket 連接到客戶端的 socket。
連接建立后,通過使用 I/O 流在進行通信,每一個socket都有一個輸出流和一個輸入流,客戶端的輸出流連接到服務(wù)器端的輸入流,而客戶端的輸入流連接到服務(wù)器端的輸出流。
TCP 是一個雙向的通信協(xié)議,因此數(shù)據(jù)可以通過兩個數(shù)據(jù)流在同一時間發(fā)送
4.TCP編程
TCP字節(jié)流編程
我們來模擬一個服務(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端口進行監(jiān)聽
// 細節(jié):需要確保9999端口處于空閑狀態(tài)
ServerSocket serverSocket = new ServerSocket(9999);
// 沒有客戶端鏈接時,會阻塞,等待鏈接
// 有客戶端鏈接,則會返回一個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ù)端,由于是測試程序,直接獲取本機的地址即可
// 鏈接本機的9999端口,鏈接成功會返回一個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("客戶端退出!");
}
}
同時運行服務(wù)端和客戶端,該示例代碼,客戶端會向服務(wù)端發(fā)送一個流信息:Hello Server
接下來,我們來看一個更為復(fù)雜的例子:實現(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端口進行監(jiān)聽
// 細節(jié):需要確保9999端口處于空閑狀態(tài)
ServerSocket serverSocket = new ServerSocket(9999);
// 沒有客戶端鏈接時,會阻塞,等待鏈接
// 有客戶端鏈接,則會返回一個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é)束標記
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ù)端,由于是測試程序,直接獲取本機的地址即可
// 鏈接本機的9999端口,鏈接成功會返回一個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é)束標記
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é)束標記,否則會相互等待,陷入僵持
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ù)端,由于是測試程序,直接獲取本機的地址即可
// 鏈接本機的9999端口,鏈接成功會返回一個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é)束標記
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端口進行監(jiān)聽
// 細節(jié):需要確保9999端口處于空閑狀態(tài)
ServerSocket serverSocket = new ServerSocket(9999);
// 沒有客戶端鏈接時,會阻塞,等待鏈接
// 有客戶端鏈接,則會返回一個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));
// 此時的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é)束標記
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é)束標志
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);
}
// 提供給客戶端下載的實際文件名
String resFileName = "";
if ("color".equals(downloadFileName)) {
resFileName = "networkprogramming\\tcp\\color.jpg";
} else {
resFileName = "networkprogramming\\tcp\\fish.jpg";
}
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(resFileName));
// 使用工具類將文件保存到一個字節(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ò)編程的詳細內(nèi)容,更多關(guān)于Java網(wǎng)絡(luò)編程的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于Java反射的map自動裝配JavaBean工具類設(shè)計示例代碼
controller函數(shù)中參數(shù)列表使用多個@RequestBody問題
java實現(xiàn)二分法查找出數(shù)組重復(fù)數(shù)字

