1. 程式人生 > 實用技巧 >java基礎(8)

java基礎(8)

1.網路程式設計

概述:就是用來實現網路互連的不同計算機上執行的程式間可以進行資料交換

網路模型

概述:計算機網路之間以何種規則進行通訊,就是網路模型研究問題

網路模型一般是指:

1.OSI(Open System Interconnection開放系統互聯)參考模型

  • 應用層
  • 表示層
  • 會話層
  • 傳輸層
  • 網路層
  • 資料鏈路層
  • 物理層

2.TCP/IP模型

  • 應用層
  • 傳輸層
  • 網路層
  • 網路介面層

網路程式設計三要素

  • IP地址:網路中計算機的唯一標識
  • 埠: 正在執行的程式的標識,有效埠為0-65535,其中0-1024被系統使用或保留
  • 協議:通訊的規則,分為UDP協議(不安全,不可靠,速度快,不建立連線,把資料打包,資料有限制)和TCP協議(安全,可靠,速度相對較慢,建立通道傳輸資料,通過三次握手完成連線)

socket(網路套接字)

概述:網路上具有唯一標識的ip地址和埠號組合在一起才能構成唯一能識別的識別符號套接字

socket原理:

  • 通訊的兩端都有socket
  • 網路通訊其實就是socket間的通訊
  • 資料在兩個socket間通過IO傳輸

2.網路程式設計在java中的使用

InetAddress(代表網際網路協議(IP)協議)

如果一個類沒有構造方法,有三種情況:

  • 成員全部是靜態的
  • 單例模式
  • 類中有靜態方法返回該類的物件

而我們的InetAddress類就是屬於第三種情況,他通過靜態方法返回本類的物件

InetAddress的成員方法:

  • public static InetAddress getName(String host):根據主機名或者ip地址的字串得到IP地址物件
  • public String getHostName():獲得主機物件的主機名
  • public String getHostAddress():獲得主機物件的IP地址

程式碼示例:

import java.net.InetAddress;
import java.net.UnknownHostException;

public class Demo1 {
	public static void main(String[] args) throws UnknownHostException {
		//通過靜態方法獲得InetAddress物件
		InetAddress address = InetAddress.getByName("DESKTOP-JOEJMG5");
		//獲取主機名和主機ip地址
		String name = address.getHostName();
		String ip = address.getHostAddress();
		
		System.out.println(name + "---" + ip);
	}
}

UDP協議傳送接受資料

UDP傳送資料的步驟:

  • 建立傳送端Socket物件
  • 建立資料,並把資料打包
  • 呼叫socket物件的傳送方法傳送資料
  • 釋放資源

程式碼實現:

//傳送資料
//SendDemo.java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class SendDemo {
	public static void main(String[] args) throws IOException {
		//建立UPD的socket物件
		DatagramSocket ds = new DatagramSocket();
		//建立資料
		byte[] bys = "你好,約麼?".getBytes();
		//資料的長度
		int length = bys.length;
		//IP地址物件
		InetAddress address = InetAddress.getByName("127.0.0.1");
		//埠號
		int port = 10086;
		//把資料打包
		DatagramPacket dp = new DatagramPacket(bys, length, address, port);
		//傳送資料包
		ds.send(dp);
		//釋放資源
		ds.close();
	}
}

UDP協議接受資料的步驟:

  • 建立接收端Socket物件
  • 建立一個數據包(接受容器)
  • 呼叫Socket物件的接受方法接受資料
  • 解析資料,並顯示在控制檯
  • 釋放資源

程式碼示例:

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

public class ReceiveDemo {
	public static void main(String[] args) throws IOException {
		//建立一個接收端socket物件,開的是10086埠
		DatagramSocket ds = new DatagramSocket(10086);
		
		//建立一個數據包(接受容器)
		byte[] bys = new byte[1024];
		int length = bys.length;
		DatagramPacket dp = new DatagramPacket(bys, length);
		
		//呼叫socket物件的接受方法接受資料
		ds.receive(dp);//阻塞式
		
		byte[] bys2 = dp.getData();//獲取資料緩衝區
		int len = bys2.length;//獲取資料的實際長度
		InetAddress address = dp.getAddress();//獲得InetAddress物件
		String ip = address.getHostAddress();//獲取ip地址
		String result = new String(bys2, 0, len);
		System.out.println(result);
		System.out.println("ip:" + ip);
		
		//釋放資源
		ds.close();
	}
}

補充:傳送端和接收端要想通訊,就得先開啟接收端,再開啟發送端,但是沒有接收端,傳送端開啟也不會報錯,因為UDP不保證資料可以被接收到

多執行緒實現UDP聊天室程式碼實現:

//ChatRoom.java

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

public class ChatRoom {
	public static void main(String[] args) throws IOException {
		DatagramSocket dsSend = new DatagramSocket();
		DatagramSocket dsReceive = new DatagramSocket(12306);
		
		SendThread  st = new SendThread(dsSend);
		ReceiveThread  rt = new ReceiveThread(dsReceive);
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(rt);
		
		t1.start();
		t2.start();
	}
}

//SendThread.java

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

public class SendThread implements Runnable {

	private DatagramSocket ds;
	public SendThread(DatagramSocket ds){
		this.ds = ds;
	}
	@Override
	public void run() {
		try{
			//封裝鍵盤錄入物件
			BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
			String line = null;
				while((line = br.readLine()) != null){
					if("886".equals(line)){
						break;
					}
			
			byte[] bys = line.getBytes();
			//封裝資料包
			DatagramPacket dp = new DatagramPacket(bys, bys.length,
						InetAddress.getByName("127.0.0.1"), 12306);
			//傳送資料
			ds.send(dp);
			
		
		}
	}catch(IOException e){
		e.printStackTrace();
	}	
}
}

//ReceiveThread.java

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

public class ReceiveThread implements Runnable {
	private DatagramSocket ds;
	public ReceiveThread(DatagramSocket ds){
		this.ds = ds;
	}
	@Override
	public void run() {
		try{
			while(true){
				//建立一個包(接受容器)
				byte[] bys = new byte[1024];
				DatagramPacket dp = new DatagramPacket(bys, bys.length);
				//接受資料
				ds.receive(dp);
				//獲取ip值
				String ip = dp.getAddress().getHostAddress();
				//獲取傳送過來的資料
				String s = new String(dp.getData(), 0, dp.getLength());
				System.out.println("from " + ip + " data is:" + s);
			}
		}catch(IOException e){
			e.printStackTrace();
		}
	}

}

TCP協議傳送接受資料

TCP傳送資料的步驟:

  • 建立傳送端的Socket物件:如果這步成功了,說明連線已經建立了,因為如果沒有連線成功,是會報錯的
  • 獲取輸出流,寫資料
  • 釋放物件

程式碼示例:

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

public class SendDemo2 {
	public static void main(String[] args) throws IOException {
		//建立socket物件
		Socket s = new Socket("127.0.0.1", 8006);
		//獲取輸出流,寫資料
		OutputStream os = s.getOutputStream();
		os.write("你好!約嗎".getBytes());
		//釋放資源
		s.close();
	}
}

TCP接收資料的步驟:

  • 建立接收資料的Socket物件
  • 監聽客戶端連線,返回一個對應的Socket物件
  • 獲取輸入流,讀取資料顯示在控制檯
  • 釋放資源

程式碼示例:

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

public class ReceiveDemo2 {
	public static void main(String[] args) throws IOException {
		//建立接收端socket物件
		ServerSocket ss = new ServerSocket(8006);
		
		//監聽客戶端連線,返回一個對應的socket物件
		Socket s = ss.accept();//阻塞式
		
		//獲取輸入流,讀取資料到控制檯
		InputStream is = s.getInputStream();
		byte[] bys = new byte[1024];
		int len = is.read(bys);
		String str = new String(bys, 0, len);
		System.out.println(str);
		
		//釋放資源
		s.close();
	}
}

補充:TCP傳送端啟動前,必須要有接受端,不然就會報錯,因為TCP是可靠的,保證資訊被接收到的

TCP上傳文字檔案並給出反饋程式碼案例:

//SendDemo3.java

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

public class SendDemo3 {
	public static void main(String[] args) throws IOException {
		//建立socket物件
		Socket s = new Socket("127.0.0.1", 8007);
		//封裝文字檔案
		BufferedReader br = new BufferedReader(new FileReader("text1.txt"));
		//封裝通道內流
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
		String line = null;
		while((line = br.readLine()) != null){
			bw.write(line);
			bw.newLine();
			bw.flush();
		}
		
		//Socket提供了一個終止功能,通知伺服器別等了,我沒有資料過去了
		s.shutdownOutput();
		//接收反饋
		BufferedReader brReceive = new BufferedReader(new InputStreamReader(s.getInputStream()));
		String receive = brReceive.readLine();//阻塞
		System.out.println(receive);
		//釋放資源
		bw.close();
		br.close();
		s.close();
		
	}
}

//ReceiveDemo3.java

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ReceiveDemo3 {
	public static void main(String[] args) throws IOException {
		ServerSocket ss = new ServerSocket(8007);
		
		Socket s = ss.accept();//阻塞式
		//封裝通道內的流
		BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
		//封裝文字檔案
		BufferedWriter bw = new BufferedWriter(new FileWriter("text2.txt"));
		String line = null;
		/*
		 * 讀取文字檔案是可以以null作為結束資訊的,但是呢,通道內不能這樣結束資訊的,
		 * 所以傳送端需要使用shutdownOutput()方法通知我們說已經終止傳送資料了,不然就會一直阻塞
		 */
		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();
		
		//釋放資源
		bw.close();
		s.close();
	}
}

補充:通過while迴圈可以使得以上的服務端(接受端)接收多個客戶端(傳送端),但是這個是有問題的,因為當有多個客戶端連線時,單執行緒的服務端就需要一個一個處理,等待,不符合實際情況,所以,我們想要讓伺服器接受多個客戶端,就必須得使用多執行緒

更多瞭解請參考:https://www.cnblogs.com/linhaifeng/articles/5937962.html

https://www.cnblogs.com/linhaifeng/articles/5951486.html