(java)網絡編程(一)

【網絡通信協議】

          通過計算機網絡可以使多臺計算機實現連接,位於同一個網絡中的計算機在進行連接和通訊時,需要遵循一定的規則。在計算機網絡中,這些連接和通訊的規則被稱爲網絡通信協議。它對數據的傳輸格式、傳輸速率、傳輸步驟等做了統一規定,通信雙方必須同時遵守才能完成數據的交換。目前應用最廣泛的是TCP/IP、UDP協議。

【IP地址和端口號】

          IP地址:要想使網絡中的計算機能夠進行通信,必須爲每一個計算機指定一個標識號,通過這個標識號可以指定接受數據的計算機和發送數據的計算機。在TCP/IP協議中,這個標識號就是IP地址,它可以唯一標識一臺計算機。

          端口號:通過IP地址可以訪問到指定的計算機,如果要訪問目標計算機中的某個應用程序,還要指定端口號。在計算機中,不同的應用程序是通過端口號區分的。端口號是用2個字節表示的,它的取值範圍爲0~65535,用戶的普通程序應使用1024以上的端口號。

【InetAddress】用於封裝一個IP地址,並提供一系列與IP地址相關的方法,前兩個方法用於獲取該類的實例對象。

import java.net.InetAddress;
import java.net.UnknownHostException;
/*
 *  java.net.InetAddress  表示互聯網中的IP地址
 *  
 *      靜態方法:  static  InetAddress  getLocalHost()   
 *            返回本地主機, 返回InetAddress對象(此對象不能new,只能通過調用靜態方法得到)
 *            
 *             static  InetAddress  getByName(String hostName)  傳遞主機名,獲取IP地址
 *            
 *      非靜態方法:String  getHostAddress() 獲取主機IP地址
 *              String  getHostName() 獲取主機名
 */
public class InetAddressDemo {
	public static void main(String[] args) throws UnknownHostException {
		//fun_1();
		fun_2();
	}
	
	// 傳遞主機名,獲取IP地址
	public static void fun_2() throws UnknownHostException{
		InetAddress inet = InetAddress.getByName("peng");
		System.out.println(inet);                         // peng/192.168.3.32
	}
	// 獲得本機IP
	public static void fun_1() throws UnknownHostException{
		InetAddress inet = InetAddress.getLocalHost();    //  輸出的是主機名和IP地址
		System.out.println(inet);                         //  peng/192.168.3.32  
		
		// 對主機名和IP 進行分割
		String host = inet.toString();
		String[] strs = host.split("/");
		for(String str:strs){
			System.out.println(str);                     //   peng      192.168.3.32
		}
		
		// 非靜態方法分別獲取主機名和IP
		String ip = inet.getHostAddress();
		String name = inet.getHostName();
		System.out.println(ip+" "+name);                  //  192.168.3.32  peng
	}
	
}

 【UDP協議】

         UDP是無連接通信協議,即在數據傳輸時,數據的發送端和接收端不建立邏輯連接。

         特點:消耗資源少,通信效率高,通常用於視頻、音頻、普通數據的傳輸,例如視頻會議都使用UDP協議。由於UDP面向無連接性,因此傳輸重要數據時不建議使用UDP協議。

         DatagramPacket類,用於封裝UDP通信中發送或者接收的數據。要想創建DatagramPacket類對象,先要了解它的構造方法。         

                發送端:不但接收存放數據的字節數組,還需要指定發送端的IP和端口號

                接收端:接收端的構造方法只需接收一個字節數組,來存放接收到的數據。

        DatagramSocket類,這個類的實例化對象可以發送和接收DatagramPacket數據包。

               發送端:創建DatagramSocket對象,

               接收端:構造方法中指定一個端口號,這樣可以監聽到指定端口

【程序】

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/*
 *   實現UDP協議的發送端:
 *       實現封裝數據的類:java.net.DatagramPacket   將你的數據包裝
 *       實現數據傳輸的類:java.net.DatagramSocket   將數據包發出去
 *          
 *   實現步驟:
 *       1. 創建DatagramPacket對象,封裝數據,接受數據的地址和端口
 *       2. 創建DatagramSocket對象
 *       3. 調用DatagramSocket類方法send,發送數據包 
 *       4. 關閉資源
 *       
 *   DatagramPacket構造方法: Datagrampacket(byte[] buf, int length,InetAddress address, int port)
 *   DatagramSocket構造方法:DatagramSocket()空參               方法:send(DatagramPacket d)
 */      
   
public class UDPSend {
	public static void main(String[] args) throws IOException {
		// 創建數據包對象,封裝要發送的數據,接收端IP 和端口號
		byte[] data = "你好UDP".getBytes();
		// 創建InetAddress對象,封裝要發送的IP地址
		InetAddress inet = InetAddress.getByName("192.168.3.32");
		//System.out.println(inet);                //  /192.168.3.32
		DatagramPacket dp = new DatagramPacket(data,data.length,inet,6000);
		// 創建DatagramSocket對象,數據包的發送和接收對象
		DatagramSocket ds = new DatagramSocket();  
		// 調用ds方法 send,發送數據包
		ds.send(dp);
		// 關閉資源
		ds.close();
	}

}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/*
 *   實現UDP接收端:
 *      實現封裝數據包:java.net.DatagramPacket    將數據接收
 *      實現數據輸出傳輸:java.net.DatagramSocket   接收數據包
 *   實現步驟:
 *      1. 創建DatagramSocket對象,綁定端口號   要和發送端的端口號一致
 *      2. 創建字節數組,接收發來的數據
 *      3. 創建數據包對象DatagramPacket對象
 *      4. 調用DatagramSocket對象方法,  receive(DatagramPacket dp) 接收數據,數據放在數據包中
 *      5. 拆包    發送的IP地址          數據包對象DatagramPacket 方法getAddress() 獲取的是發送端的IP地址對象  返回值是InetAddress對象
 *             接收到的字節個數     數據包對象DatagramPacket 方法getLength()
 *             發送方的端口號        數據包對象DatagramPacket 方法getPort() 獲得發送端的端口
 *      6. 關閉資源
 */
public class UDPReceive  {
	public static void main(String[] args)throws IOException {
		// 創建數據包傳輸對象DatagramSocket  綁定端口號
		DatagramSocket ds = new DatagramSocket(6000);
		// 創建字節數組
		byte[] data = new byte[1024];                //  1024*64
		// 創建數據包對象,傳遞字節數組
		DatagramPacket dp = new DatagramPacket(data, data.length);
		// 調用ds對象方法receive傳遞數據包
		ds.receive(dp);
		// 獲取得到字節的個數
		int length = dp.getLength();                // 7
		// 獲取發送端的IP地址
		String ip = dp.getAddress().getHostAddress();
		// 獲得發送端的端口號
		int port = dp.getPort();
		System.out.println(new String(data,0,length)+"  "+ip+"  "+port); 
		
		ds.close();
	}
	
}

【鍵盤輸入聊天】

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
/*
 *   實現UDP發送,發送鍵盤輸入的數據 
 */
/*
 *   實現UDP協議的發送端:
 *       實現封裝數據的類:java.net.DatagramPacket   將你的數據包裝
 *       實現數據傳輸的類:java.net.DatagramSocket   將數據包發出去
 *          
 *   實現步驟:
 *       1. 創建DatagramPacket對象,封裝數據,接受數據的地址和端口
 *       2. 創建DatagramSocket對象
 *       3. 調用DatagramSocket類方法send,發送數據包 
 *       4. 關閉資源
 *       
 *   DatagramPacket構造方法: Datagrampacket(byte[] buf, int length,InetAddress address, int port)
 *   DatagramSocket構造方法:DatagramSocket()空參               方法:send(DatagramPacket d)
 */      
   
public class UDPSend {
	public static void main(String[] args) throws IOException {
		Scanner sc = new Scanner(System.in);
		// 創建InetAddress對象,封裝要發送的IP地址
		InetAddress inet = InetAddress.getByName("192.168.3.32");
		//System.out.println(inet);                //  /192.168.3.32
		// 創建DatagramSocket對象,數據包的發送和接收對象
		DatagramSocket ds = new DatagramSocket();  
		while(true){
			String message = sc.nextLine();
			// 創建數據包對象,封裝要發送的數據,接收端IP 和端口號
			byte[] data = message.getBytes();
			DatagramPacket dp = new DatagramPacket(data,data.length,inet,6000);
			// 調用ds方法 send,發送數據包
			ds.send(dp);
		}		
		
		// 關閉資源
		//ds.close();
	}

}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/*
 *   實現UDP接收端     永不停息的接收端
 */
/*
 *   實現UDP接收端:
 *      實現封裝數據包:java.net.DatagramPacket    將數據接收
 *      實現數據輸出傳輸:java.net.DatagramSocket   接收數據包
 *   實現步驟:
 *      1. 創建DatagramSocket對象,綁定端口號   要和發送端的端口號一致
 *      2. 創建字節數組,接收發來的數據
 *      3. 創建數據包對象DatagramPacket對象
 *      4. 調用DatagramSocket對象方法,  receive(DatagramPacket dp) 接收數據,數據放在數據包中
 *      5. 拆包    發送的IP地址          數據包對象DatagramPacket 方法getAddress() 獲取的是發送端的IP地址對象  返回值是InetAddress對象
 *             接收到的字節個數     數據包對象DatagramPacket 方法getLength()
 *             發送方的端口號        數據包對象DatagramPacket 方法getPort() 獲得發送端的端口
 *      6. 關閉資源
 */
public class UDPReceive  {
	public static void main(String[] args)throws IOException {
		// 創建數據包傳輸對象DatagramSocket  綁定端口號
		DatagramSocket ds = new DatagramSocket(6000);
		// 創建字節數組
		byte[] data = new byte[1024];                //  1024*64
		// 創建數據包對象,傳遞字節數組
		while(true){
			DatagramPacket dp = new DatagramPacket(data, data.length);
			// 調用ds對象方法receive傳遞數據包
			ds.receive(dp);
			// 獲取得到字節的個數
			int length = dp.getLength();                // 7
			// 獲取發送端的IP地址
			String ip = dp.getAddress().getHostAddress();
			// 獲得發送端的端口號
			int port = dp.getPort();
			System.out.println(new String(data,0,length)+"  "+ip+"  "+port);
		}
		 
		//ds.close();
	}
	
}

【TCP通信】

         TCP通信是嚴格區分客戶端和服務器端的,必須先有客戶端先去連接服務器端,才能實現通信,服務器端不可以主動連接客戶端,且服務器端的程序需要事先啓動,等待客戶端的連接。

Java中兩個用於實現TCP通信的類,ServerSocket類,用於表示服務器;socket類,用於表示客戶端。

(1)ServerSocket    實現服務端程序

                   

        使用該構造方法,在創建ServerSocket對象時,將其綁定到一個指定的端口上 。

                  

        ServerSocket對象用於監聽某臺計算機的某個端口號,調用該對象的方法accept(),接收來自客戶端的請求。執行accept()方法後,服務器端程序會發生阻塞,直到客戶端發出連接請求。accept()方法纔會返回一個Socket對象,用於和客戶端實現通信,程序纔會繼續向下執行。 

(2)Socket    實現客戶端程序

                   

       使用該構造方法創建Socket對象時,會根據參數去連接在指定主機上的指定端口上運行的服務器程序,host接收的是一個字符串型的IP地址。(常用)

                   

       參數address用於接收一個InetAddress類型的對象,該對象用於封裝一個IP地址。

InputStream  getInputStream()

該方法返回一個InputStream類型的輸入流對象。

若該對象由服務器端的Socket返回,用於讀取客戶端發送的數據,反之用於讀取服務器端發送的數據。

OutputStream getOutputStream()

該方法返回一個OutputStream類型的輸出流對象。

若該對象由服務器端的Socket返回,用於向客戶端發送數據,反之用於向服務器端發送數據。

void  close() 用於關閉Socket連接,結束本次通信。
int  getPort() 該對象是Socket對象與服務器連接的端口號
InetAddress  getLocalAddress() 用於獲取Socket對象綁定的本地IP,並將IP地址封裝成InetAddress類型對象返回

【程序】

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/*
 *  實現TCP服務程序
 *      java.net.ServerSocket
 *  構造方法:
 *      ServerSocket(int port)  傳遞端口號
 *  
 *  很重要的事情:必須要獲得 客戶端的套接字對象Socket
 *     Socket accept()    偵聽並接受此套接字的連接
 *     服務器可以獲得 客戶端套接字的對象,
 */
public class TCPServer {
	public static void main(String[] args) throws IOException {
		// 創建綁定到特定端口的服務器套接字
		ServerSocket server = new ServerSocket(5001);
		// 調用服務器套接字對象中方法accept() 獲得客戶端套接字對象
		Socket socket = server.accept();
		// 通過客戶端套接字對象方法,獲取字節輸入流, 讀取到的是客戶端發送來的數據
		InputStream in = socket.getInputStream();
		byte[] data = new byte[1024];
		int len = in.read(data);
		System.out.println(new String(data, 0, len));
		
		// 服務器端向客戶端回數據,字節輸出流,通過客戶端套接字對象 獲得字節輸出流
		OutputStream out = socket.getOutputStream();
		out.write("謝謝,收到".getBytes());
		
		socket.close();
		server.close();
	}

}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/*
 *   實現TCP客戶端,連接到服務器 和服務器實現數據交換
 *        java.net.Socket  實現TCP客戶端程序的類
 *   構造方法:
 *        Socket(String host, int port) 傳遞服務器IP和端口號
 *        注意:構造方法只要運行,就會和服務器進行連接,若連接失敗,拋出異常
 *    
 *        OutputStream getOutputStream()  返回套接字的輸出流    作用:將數據輸出,輸出到服務器
 *        InputStream  getInputStream()   返回套接字的輸入流    作用: 從服務器端讀取數據
 *        
 *   客戶端、服務器端數據交換,必須使用套接字對象Socket中的方法獲取IO流,自己new的流不行
 */
public class TCPClient {
	public static void main(String[] args) throws  IOException {
		// 創建Socket對象,連接服務器
		Socket socket = new Socket("192.168.3.32",5001);
		// 通過客戶端的套接字對象Socket方法,獲得字節輸出流,將數據寫入服務器端
		OutputStream out = socket.getOutputStream();
		out.write("服務器ok".getBytes());
		
		// 讀取服務端發回的數據,使用Socket套接字對象中的方法,獲得字節輸入流
		InputStream in = socket.getInputStream();
		byte[] data  = new byte[1024];
		int len = in.read(data);
		System.out.println(new String(data,0,len));
		socket.close();
	}

}

 【TCP 實現 上傳圖片到服務器端】

/*
 *  TCP 圖片上傳服務器
 *  實現步驟:
 *      1. 創建ServerSocket套接字對象,綁定監聽端口號8000
 *      2. 方法accept()獲得客戶端的連接對象
 *      3. 客戶端連接對象獲取字節輸入流,讀取客戶端發送的圖片
 *      4. 創建File對象,綁定上傳文件夾    (判斷文件件是否存在,若不存在,創建文件夾)
 *      5. 創建字節輸出流,數據目的是File對象所在的文件夾
 *      6. 字節流讀取圖片,字節流將圖片寫到指定文件夾中
 *      7. 將“上傳成功”返回客戶端
 *      8. 關閉資源
 * 
 */
public class TCPServer {
	public static void main(String[] args) throws IOException{
		ServerSocket server = new ServerSocket(8000);
		// 調用對象方法accept()獲取客戶端對象
		Socket socket = server.accept();
		//通過客戶端連接對象,獲取字節輸入流,讀取客戶端發來的圖片
		InputStream in = socket.getInputStream();
		// 將目的文件夾上傳到File封裝的文件夾
		File upload = new File("E:\\workspace\\day31\\src\\usst\\javacode\\upload");
		// 若不存在,則創建該文件夾
		if(!upload.exists())
			upload.mkdirs();
		
		// 防止文件同名被覆蓋,重新定義文件夾名字      規則:usst+毫秒值+6位隨機數
		String fileName = "usst" + System.currentTimeMillis() + new Random().nextInt(999999) + ".jpg";
		
		// 創建字節輸出流,將圖片寫入到指定的文件夾中
		FileOutputStream fos = new FileOutputStream(upload + File.separator + fileName);
		
		// 讀  寫字節數組
		int len = 0;
		byte[] bytes = new byte[1024];
		while((len = in.read(bytes)) != -1){
			fos.write(bytes,0,len);
		}
		
		
		// 通過客戶端連接對象獲取字節輸出流,將“上傳成功”發送至客戶端
		OutputStream out = socket.getOutputStream();
		out.write("上傳成功".getBytes());
		
		fos.close();
		socket.close();
		server.close();
	}

}
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/*
 *  TCP實現上傳圖片到服務器端:
 *  實現步驟:
 *       1. Socket套接字連接服務器端
 *       2. 通過Socket對象方法,獲得字節輸出流,服務器端寫圖片
 *       3. 使用自己的流對象,讀取圖片數據源    FileInputStream
 *       4. 讀取圖片,使用字節輸出流,將圖片寫到服務器    採用字節數組進行緩衝
 *       5. 通過Socket套接字獲取字節輸入流     讀取服務器端發回來的“上傳成功”
 *       6. 關閉資源
 * 
 */
public class TCPClient {
	public static void main(String[] args) throws IOException {
		Socket socket = new Socket("192.168.3.32", 8000);
		// 獲取字節輸出流,將圖片傳送到服務器
		OutputStream out = socket.getOutputStream();
		// 創建字節輸入流,讀取本機上的數據源圖片
		FileInputStream fis = new FileInputStream("E:\\workspace\\day31\\src\\usst\\javacode\\java學習路程.png");
		// 開始讀 寫字節數組
		int len = 0;
		byte[] bytes = new byte[1024];
		while((len = fis.read(bytes)) != -1){
			out.write(bytes, 0, len);
		}
		// 給服務器寫終止序列
		socket.shutdownOutput();
		
		// 獲取字節輸入流,接受服務器發回來的“上傳成功”
		InputStream in = socket.getInputStream();
		len = in.read(bytes);
		System.out.println(new String(bytes, 0, len));
		
		fis.close();
		socket.close();
	}

}

【多線程上傳案例】

           實現服務器端可以同時接收多個客戶端上傳的文件。修改服務器端的代碼。

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Random;

public class Upload implements Runnable{
	private Socket socket;
	// 構造方法傳參
	public Upload(Socket socket){
		this.socket = socket;
	}
	// 重寫run方法
	public void run(){
		try{
			//通過客戶端連接對象,獲取字節輸入流,讀取客戶端發來的圖片
			InputStream in = socket.getInputStream();
			// 將目的文件夾上傳到File封裝的文件夾
			File upload = new File("E:\\workspace\\day31\\src\\usst\\javacode\\upload");
			// 若不存在,則創建該文件夾
			if(!upload.exists())
				upload.mkdirs();
			
			// 防止文件同名被覆蓋,重新定義文件夾名字      規則:usst+毫秒值+6位隨機數
			String fileName = "usst" + System.currentTimeMillis() + new Random().nextInt(999999)+socket.getPort()+ ".jpg";
			// 創建字節輸出流,將圖片寫入到指定的文件夾中
			FileOutputStream fos = new FileOutputStream(upload + File.separator + fileName);
			
			// 讀  寫字節數組
			int len = 0;
			byte[] bytes = new byte[1024];
			while((len = in.read(bytes)) != -1){
				fos.write(bytes,0,len);
			}
			
			// 通過客戶端連接對象獲取字節輸出流,將“上傳成功”發送至客戶端
			OutputStream out = socket.getOutputStream();
			out.write("上傳成功".getBytes());
			
			fos.close();
			socket.close();
		}catch(Exception ex){
			ex.printStackTrace();
		}
       
	}

}

 

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/*
 * 
 */
public class TCPThreadServer {
	public static void main(String[] args) throws IOException {
		ServerSocket server = new ServerSocket(8004);
		while(true){
			// 獲取一個客戶端就必須開啓一個新線程
			Socket socket = server.accept();
			new Thread(new Upload(socket)).start();
		}
		
	}

}

【UDP與TCP的區別】

         都能實現兩臺計算機的通信,通信的兩端都要創建socket對象。

         UDP只有發送端和接收端,不區分客戶端和服務器端,計算機之間可以任意發送數據;TCP是嚴格區分客戶端和服務器端的,服務器端要事先開啓,必須先由客戶端去連接服務器端,才能實現通信。

         通信時,首先創建代表服務器端的ServerSocket對象,該對象相當於開啓一個服務,並等待客戶的連接,然後創建代表客戶端的Socket對象,向服務器端發出連接請求,服務器端響應請求,兩者建立連接開始通信。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章