1.網絡編程入門
計算機網絡
- 是指將地理位置不同的具有獨立功能的多台計算機及其外部設備,通過通信線路連接起來,在網絡操作系統,網絡管理軟件及網絡通信協議的管理和協調下,實現資源共享和信息傳遞的計算機系統
網絡編程
- 在網絡通信協議下,實現網絡互連的不同計算機運行的程序間可以進行數據交換
網絡編程三要素
(1)IP地址
IP地址:是網絡中設備的唯一標識
IP地址分為兩大類:
- IPv4:是給每個連接在網絡上的主機分配一個32bit地址。按照TCP/IP規定,IP地址用二進制來表示,每個IP地址長32bit,也就是4個字節。例如一個采用二進制形式的IP地址是“11000000 10101000 00000001 01000010”,這么長的地址,處理起來太費勁了。為了方便數組,IP地址經常被寫成十進制形式,中間使用符號 “.” 分隔不同的字節。於是,上面的IP地址可以表示為“192.168.1.66”。IP地址的這種表示法叫做“點分十進制表示法”,這顯然比1和0容易記憶的多
- IPv6:由於互聯網的蓬勃發展,IP地址的需求量愈來愈大,但是網絡地址資源有限,使得IP的分配越發緊張。為了擴大地址空間,通過IPv6重新定義地址空間,采用128位地址長度,每16個字節一組,分成8組十六進制數,這樣就解決了網絡地址資源數量不夠的問題
常用命名:
- ipconfig:查看本機IP地址
- ping IP地址:檢查網絡是否連通
- 特殊IP地址
- 127.0.0.1:是回送地址,可以代表本機地址,一般用來測試使用
InetAddress 的使用
為了方便我們對IP地址的獲取和操作,Java提供了一個類InetAddress 供我們使用
InetAddress :此類表示Internet協議(IP)地址
public static void main(String[] args) throws UnknownHostException {
InetAddress address = InetAddress.getByName("192.168.1.66");
String hostName = address.getHostName();
String ip = address.getHostAddress();
System.out.println("主機名:"+hostName);
System.out.println("IP地址:"+ip);
}
(2)端口
端口:設備上應用程序的唯一標識
端口號:用兩個字節表示的整數,它的取值范圍是065535。其中,01023 之間的端口號用於一些知名的網絡服務和應用,普通的應用程序需要使用1024以上的端口號。如果端口號被另外一個服務或應用所占用,會導致當前程序啟動失敗
(3)協議
協議:計算機網絡中,連接和通信的規則被稱為網絡通信協議
UDP協議
-
用戶數據報協議(User Datagram Protocol)
-
UDP是無連接通信協議,即在數據傳輸時,數據的發送端和接收端不建立邏輯連接。簡單來說,當一台計算機向另一台計算機發送數據時,發送端不會確認接收端是否存在,就會發出數據,同樣接收端在收到數據時,也不會向發送端反饋是否收到數據。
由於使用UDP協議消耗資源小,通信效率高,所以通常都會用於音頻、視頻和普通數據的傳輸
-
例如視頻會議通常采用UDP協議,因為這種情況即使偶爾丟失一兩個數據包,也不會對接收結果產生太大影響。但是在使用UDP協議傳送數據時,由於UDP的面向無連接性,不能保證數據的完整性,因此在傳輸重要數據時不建議使用UDP協議
TCP協議
- 傳輸控制協議(Transmission Control Protocol)
- TCP協議是面向連接的通信協議,即傳輸數據之前,在發送端和接收端建立邏輯連接,然后再傳輸數據,它提供了兩台計算機之間可靠無差錯的數據傳輸。在TCP連接中必須要明確客戶端與服務器端,有客戶端想服務器端發出連接請求,每次連接的創建都需要經過“三次握手”
- 三次握手:TCP協議中,在發送數據的准備階段,客戶端與服務器端之間的三次交互,以保證連接的可靠
- 第一次握手,客戶端向服務器端發出連接請求,等待服務器端確認
- 第二次握手,服務器端向客戶端回送一個響應,通知客戶端收到了連接請求
- 第三次握手,客戶端再次向服務器端發送確認信息,確認連接
- 完成三次握手后,連接建立,客戶端和服務器端就可以開始進行數據傳輸了。由於這種面向連接的特性,TCP協議可以保證傳輸數據的安全,所以應用十分廣泛。例如上傳文件、下載文件和瀏覽網頁等等
2.UDP通信程序
(1)UDP通信原理
UDP協議是一種不可靠的網絡協議,它在通信的兩端各建立一個Socket對象,但是這兩個Socket只是發送、接收數據的對象,因此對於基於UDP協議的通信雙方而言,沒有所謂的客戶端和服務器端概念。Java提供了DatagramSocket類作為基於UDP協議的Socket
(2)UDP發送數據
發送數據的步驟:
- 創建發送端的Socket對象(DatagramSocket)
- 創建數據,並把數據打包
- 調用DatagramSocket對象的方法發送數據
- 關閉發送端
// 一:
public static void main(String[] args) throws IOException{
// 創建發送端的Socket對象(DatagramSocket)
DatagramSocket ds = new DatagramSocket();
// 創建數據,並把數據打包
byte[] bys = "hello,UDP 我來了".getBytes();
int length = bys.length;
InetAddress address = InetAddress.getByName("192.168.1.66");
int port = 10086;
DatagramPacket dp = new DatagramPacket(bys,length,address,port);
// 調用DatagramSocket對象的方法發送數據
ds.send(dp);
// 關閉發送端
ds.close();
}
// 二:發送的數據來自於鍵盤錄入,直到輸入的數據是886,發送數據結束
public static void main(String[] args) throws IOException{
// 創建發送端的Socket對象(DatagramSocket)
DatagramSocket ds = new DatagramSocket();
// 錄入數據,並把數據打包
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line;
while((line=br.readLine()) != null){
// 當輸入的是886時,發送數據結束
if("886".equal(line)){
break;
}
// 獲取錄入的數據,並打包
byte[] bys = line.getBytes();
DatagramPacket dp = new DatagramPacket(bys,bys.length,InetAddress.getByName("192.168.1.66"),10086);
// 調用DatagramSocket對象的方法發送數據
ds.send(dp);
}
// 關閉發送端
ds.close();
}
(3)UDP接收數據
接收數據的步驟:
- 創建接收端的Socket對象(DatagramSocket)
- 創建一個數據包,用於接收數據
- 調用DatagramSocket對象的方法接收數據
- 解析數據包,並把數據在控制台顯示
- 關閉接收端
// 一:
public static void main(String[] args) throws IOException{
// 創建接收端的Socket對象(DatagramSocket)
DatagramSocket ds = new DatagramSocket(10086);
// 創建一個數據包,用於接收數據
byte[] bys = new byte[1024];
int length = bys.length;
DatagramPacket dp = new DatagramPacket(bys,length);
// 調用DatagramSocket對象的方法接收數據
ds.receive(dp);
// 解析數據包,並把數據在控制台顯示
byte[] datas = ds.getData();
int len = dp.getLength(); // 實際接收到的數據長度
String datasString = new String(datas,0,len);
System.out.println("接收到的數據是:"+datasString);
// 關閉接收端
ds.close();
}
// 二:因為接收端不清楚發送端什么時候停止發送,故采用死循環接收
public static void main(String[] args) throws IOException{
// 創建接收端的Socket對象(DatagramSocket)
DatagramSocket ds = new DatagramSocket(10086);
while(true){
// 創建一個數據包,用於接收數據
byte[] bys = new byte[1024];
int length = bys.length;
DatagramPacket dp = new DatagramPacket(bys,length);
// 調用DatagramSocket對象的方法接收數據
ds.receive(dp);
// 解析數據包,並把數據在控制台顯示
System.out.println("接收到的數據是:"+new String(ds.getData(),0,dp.getLength()));
}
// 關閉接收端
ds.close();
}
3.TCP通信程序
(1)TCP通信原理
TCP通信協議是一種可靠的網絡協議,它在通信的兩端各建立一個Socket對象,從而在通信的兩端形成網絡虛擬鏈路,一旦建立了虛擬的網絡鏈路,兩端的程序就可以通過虛擬鏈路進行通信。
Java對基於TCP協議的網絡提供了良好的封裝,使用Socket對象來代表兩端的通信端口,並通過Socket產生IO流來進行網絡通信
Java為客戶端提供了Socket類,為服務器端提供了ServerSocket類
(2)TCP發送數據
發送數據的步驟:
- 創建客戶端的Socket對象(Socket)
- 獲取輸出流,寫數據
- 釋放資源
// 一:
public static void main(String[] args) throws IOException{
// 創建客戶端的Socket對象(Socket)
Socket s = new Socket("192.168.1.66",10000);
// 獲取輸出流,寫數據
OutputStream os = new s.getOutputStream();
os.write("hello,TCP,我來了".getBytes());
// 釋放資源
s.close();
}
// 二:發送的數據來自於鍵盤錄入,直到輸入的數據是886,發送數據結束
public static void main(String[] args) throws IOException{
// 創建客戶端的Socket對象(Socket)
Socket s = new Socket("192.168.1.66",10000);
// 錄入數據,並把數據打包
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 封裝輸出流對象
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line;
while((line=br.readLine()) != null){
// 當輸入的是886時,發送數據結束
if("886".equal(line)){
break;
}
bw.write(line);
bw.newLine();
bw.flush();
}
// 關閉發送端
s.close();
}
(3)TCP接收數據
接收數據的步驟:
- 創建服務器端的Socket對象(ServerSocket)
- 獲取輸入流,讀數據,並把數據顯示在控制台
- 釋放資源
// 一:
public static void main(String[] args) throws IOException{
// 創建服務器端的Socket對象(ServerSocket)
ServerSocket ss = new ServerSocket(10000);
Socket s = ss.accept();
// 獲取輸入流,讀數據,並把數據顯示在控制台
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len= is.read(bys);
System.out.println("接收到的數據是:"+new String(bys,0,len));
// 釋放資源
ss.close();
}
// 二:因為服務器端不清楚客戶端什么時候停止發送,故采用死循環接收
public static void main(String[] args) throws IOException{
// 創建服務器端的Socket對象(ServerSocket)
ServerSocket ss = new ServerSocket(10000);
Socket s = ss.accept();
// 解析數據包,並把數據在控制台顯示
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line;
while((line=br.readLine()) != null){
System.out.println("接收到的數據是:"+line);
}
// 釋放資源
ss.close();
}
練習案例:
- 客戶端:數據來自於文本文件,接收服務器反饋
- 服務器:接收到的數據寫入文本文件,給出反饋,代碼用線程進行封裝,為每一個客戶端開啟一個線程
// 客戶端
public static void main(String[] args) throws IOException{
// 創建客戶端的Socket對象(Socket)
Socket s = new Socket("192.168.1.66",10000);
// 封裝文本文件的數據
BufferedReader br = new BufferedReader(new FileReader("InetAddressDemo.java"));
// 封裝輸出流對象
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line;
while((line=br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
}
// 防止讀數據是阻塞式的
s.shutdownOutput();
// 接收反饋
BufferedReader brClient = new BufferedReader(InputStreamReader(s.getInputStream()));
String data = brClient.readLine();
System.out.println("服務器端的反饋:"+data);
// 釋放資源
br.close();
s.close();
}
// 服務器端
public static void main(String[] args) throws IOException{
// 創建服務器端的Socket對象(ServerSocket)
ServerSocket ss = new ServerSocket(10000);
while(true){
Socket s = ss.accept();
// 為每一個客戶端開啟一個線程
new Thread(new ServerThread(s)).start();
}
// 一般來說服務器是不關閉的,再次就不釋放資源
}
// 線程類
public class ServerThread implements Runnable {
private Socket s;
public ServerThread(Socket s){
this.s = s;
}
@Override
public void run(){
try{
// 接收數據寫到文本文件中
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
// 解決客戶端多次請求,文件名稱重復沖突的問題
int count = 0;
File file = new File("Copy["+count+"].java");
while(file.exists()){
count++;
file = new File("Copy["+count+"].java");
}
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
String line;
while((line=br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
}
// 給出客戶端反饋
BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
bwServer.write("文件上傳成功");
bwServer.newLine();
bwServer.flush();
// 釋放資源
s.close();
}catch(IOException e){
e.printStackTrace();
}
}
}