1、網絡協議的概述編程
多臺計算機的鏈接和「交流」離不開網絡的支持,而就像道路同樣,爲了保證傳輸的效率和安全,網絡的傳輸也需
要必定得規則。只有雙方都知足這個規則才能創建聯繫。如今應用最普遍的就是目前應用最普遍的是TCP/IP協議
(Transmission Control Protocal/Internet Protoal),中文全名爲:傳輸控制協議/英特網互聯協議。它包含了
TCP協議和IP協議,UDP協議等。而爲了保證傳輸先後數據的一致性,在傳輸的過程當中須要在原數據的頭尾部添加一
些數據。TCP/IP協議一共分爲4層:數組
鏈路層:鏈路層主要規定了物理傳輸通道的規則、一般是某些網絡連接設備的驅動協議,如:光纖、網線。
網絡層:網絡層是TCP/IP協議的核心,它主要用於將傳輸的數據進行分組並將分組數據發送到目標計算機或者網絡
(進行數據的分包)。
傳輸層:主要用於程序之間得的網絡通信,能夠採用TCP協議或者是UDP協議(創建連接)。
應用層:主要用於應用程序的協議,如http協議。安全
2、IP地址和端口號 服務器
想要進行應用程序之間通訊,和咱們平時串門的思想是同樣的,你首先要知道你的朋友住在哪一個小區(IP地址)
,知道了他家的小區地址以後你還須要知道你朋友家的具體門牌號(端口號)才能進入家門進行深刻的交流。目前當
前IP地址普遍使用的版本是IPv4,它是由4個字節大小的二進制數來表示。可是隨着互聯網普及的愈來愈廣IPv4的方式
已經不能知足咱們的需求了,所以IPv6就應運而生了,IPv6使用16個字節表示IP地址,它所擁有的地址容量約是IPv4
的8×1028倍,這樣解決了IP地址不夠用的問題。經過IP地址能夠鏈接到指定計算機,若是想訪問目標計算機中的某個應
用程序,則須要指定對應端口號。在計算機中,不一樣的應用程序是經過端口號區分的。端口號是用兩個字節(16位的二
進制數)表示的,它的取值範圍是0~65535,其中,0~1023之間的端口號用於一些知名的網絡服務和應用,用戶的普通
應用程序須要使用1024以上的端口號,從而避免端口號被另一個應用或服務所佔用。網絡
3、InetAddress類 socket
Java爲了咱們編程更加的方便,爲咱們的網絡編程提供了一些技術支持。InetAddress類用於封裝一個IP地址並提供了
一些關於IP地址經常使用的方法:
一、getByName:在給定主機名的狀況下肯定主機的 IP 地址 - 返回值爲InetAddress對象。
二、getHostName():獲取此 IP 地址的主機名 - 返回值爲字符串。
三、getHostAddress():返回 IP 地址字符串。
四、getLocalHost(): 返回本地主機 - 返回值爲InetAddress對象。
舉例:ide
1 //給定主機名返回主機IP 2 InetAddress i=InetAddress.getByName("DESKTOP-4DDBUKG"); 3 System.out.println(i); 4 //獲取此IP地址的主機名 5 String hostName = i.getHostName(); 6 System.out.println(hostName); 7 //返回IP地址的字符串 8 String address = i.getHostAddress(); 9 System.out.println(address); 10 //返回本地主機 11 InetAddress localHost = InetAddress.getLocalHost(); 12 System.out.println(localHost);
4、UDP協議post
UDP是無鏈接通訊協議,即在發送數據時無論接收端接不接收他都發送數據,而接收端在接收數據時也不會給發送端一
個反饋。由於UDP協議消耗資源小,通訊效率高,因此一般都會用於音頻、視頻和普通數據的傳輸例如視頻會議都使用
UDP協議,由於這種狀況即便偶爾丟失一兩個數據包,也不會對接收結果產生太大影響。可是在傳輸重要數據時不建議使用
UDP協議,由於UDP是無鏈接通訊協議不能保證數據的完整性。網站
(一)、DatagramSocket類this
DatagramSocket類表示用來發送和接收數據報包的套接字。數據報套接字是包投遞服務的發送或接收點。因此
DatagramSocket類能夠理解爲發送快遞的收件人和寄件人,在建立發送端和接收端的DatagramSocket對象時使用的
是不一樣的構造方法:
一、DatagramSocket():該構造方法用於建立發送端的DatagramSocket對象,系統會給他分配一個沒有被其它網絡程
序所使用的端口號。
二、DatagramSocket(int port) :該構造方法既可用於建立接收端的DatagramSocket對象,又能夠建立發送端的
DatagramSocket對象,在建立接收端的DatagramSocket對象時,必需要指定一個端口號,這樣就能夠監聽指定的端口。
DatagramSocket類的經常使用方法:
一、今後套接字接收數據報包:receive(DatagramPacket p)。
二、今後套接字發送數據報包:send(DatagramPacket p) 。
(二)DatagramPacket類
UDP通訊的過程其實和發快遞很像,知道了收件人和寄件人後還須要對「貨物」進行打包,而DatagramPacket類就是
至關於「快遞員」,用於封裝UDP通訊中發送或者接收的數據。DatagramPacket類的構造方法與DatagramSocket類的相似
接收端的構造方法只須要接收收到的數據,而發送端的構造方法不只要存放要發送的數據,還要指定接收端的IP和端口
號。
一、接收端 - 構造 DatagramPacket,用來接收長度爲 length 的數據包:DatagramPacket(byte[] buf, int length)
二、發送端 - 構造數據報包,用來將長度爲 length 的包發送到指定主機上的指定端口號:
DatagramPacket(byte[] buf, int length, SocketAddress address)
其經常使用的方法:
一、返回某臺機器的 IP 地址,此IP將要發往該機器或者是從該機器接收到的:getAddress()
二、返回某臺遠程主機的端口號,此數據報將要發往該主機或者是從該主機接收到的:getPort()
三、返回數據緩衝區:getData()
四、返回將要發送或接收到的數據的長度:getLength()
舉例:
*發送端
1 public static void main(String[] args) throws IOException { 2 // 獲取DatagramSocket類的對象ds 3 DatagramSocket ds = new DatagramSocket(); 4 // 死循環發送信息 5 while (true) { 6 // 準備要發送的數據 7 String s = "薩拉黑有"; 8 // 將其轉換成byte數組 9 byte[] bs = s.getBytes(); 10 // 獲取數組的長度 11 int length = bs.length; 12 // 根據IP地址獲取InetAddress對象it 13 InetAddress it = InetAddress.getByName("192.168.1.146"); 14 // 準備端口號 15 int post = 8888; 16 // 建立DatagramPacket對象,傳入準備好的數據、數據長度、IP地址、端口號進行打包 17 DatagramPacket datagramPacket = new DatagramPacket(bs, length, it, post); 18 // 調用send方法將準備好的數據數據發送出去,由於在一直髮送數據因此沒有關閉資源 19 ds.send(datagramPacket); 20 } 21 }
*接收端
1 public static void main(String[] args) throws IOException { 2 // 建立DatagramSocket對象,設置端口號進行監聽 3 DatagramSocket ds = new DatagramSocket(8888); 4 // 循環接收數據 5 while (true) { 6 // 準備接受數據的容器 7 byte[] buf = new byte[1024]; 8 // 建立DatagramPacket對象用來接收數據、傳入容器和容器的長度 9 DatagramPacket packet = new DatagramPacket(buf, buf.length); 10 // 調用receive方法接收數據 11 ds.receive(packet); 12 // 獲取發送端的InetAddress對象 13 InetAddress address = packet.getAddress(); 14 // 獲取傳入數據的長度 15 int length = packet.getLength(); 16 // 輸出發送端IP地址和發送的內容 17 System.out.println("---->" + address.getHostAddress() + " 內容:" + new String(buf, 0, length)); 18 } 19 }
5、TCP協議
TCP做爲另外一種經常使用的通訊協議,與UDP協議的相同點是都能實現兩臺計算機之間的通訊,通訊的兩端都須要建立socket對象。
而二者不一樣的是UDP中只有發送端和接收端,不區分客戶端與服務器端,計算機之間能夠任意地發送數據。而TCP通訊是嚴格區分
客戶端與服務器端的,在通訊時,必須先由客戶端去鏈接服務器端才能實現通訊,服務器端不能夠主動鏈接客戶端,而且服務器
端程序須要事先啓動,等待客戶端的鏈接。而JAVA爲了方便咱們的開發提供給咱們2個用於實現TCP程序的類,一個是ServerSocket類
,用於表示服務器端,一個是Socket類,用於表示客戶端。
(一)、ServerSocket類
TCP協議進行通信時首先要建立表明服務器端的ServerSocket對象,等待客戶端的鏈接。而後建立表明客戶端的Socket對象向服務
器端發出鏈接請求,服務器端響應請求,二者創建鏈接開始通訊。
ServerSocket類的經常使用構造方有 - 建立綁定到特定端口的服務器套接字:ServerSocket(int port)
其經常使用方法:
一、返回值爲Socket對象,偵聽並接受到此套接字的鏈接:accept()
二、關閉此套接字:close()
三、返回此服務器套接字的本地地址:getInetAddress()
ServerSocket對象負責監聽某臺計算機的某個端口號,在建立ServerSocket對象後,須要繼續調用該對象的accept()方法,接收
來自客戶端的請求。當執行了accept()方法以後,服務器端程序會發生阻塞,直到客戶端發出鏈接請求,accept()方法纔會返回一個
Scoket對象用於和客戶端實現通訊,程序才能繼續向下執行。
(二)、Socket類
Socket類實現客戶端套接字(TCP客戶端程序),其經常使用的構造方法有:
一、建立一個流套接字並將其鏈接到指定 IP 地址的指定端口號 - 根據InetAddress對象和端口號port建立:
Socket(InetAddress address, int port)
二、建立一個流套接字並將其鏈接到指定主機上的指定端口號 - 根據IP的字符串和端口號port建立:
Socket(String host, int port)
Socket類的經常使用方法有:
一、返回值爲int,返回的是Socket對象與服務器端鏈接的端口號:getPort()
二、返回值爲InetAddress類型的對象,獲取Socket對象綁定的本地IP地址:getLocalAddress()
五、關閉此套接字:close()
六、返回值爲InputStream類型的輸入流對象,返回值爲此套接字的輸入流,用於讀取數據:getInputStream()
七、返回值爲OutputStream類型的輸出流對象,返回值爲此套接字的輸出流,用於數據的寫入:getInputStream()
當客戶端和服務端創建鏈接後,數據是以IO流的形式進行交互的,從而實現通訊。
舉例:
*客戶端
1 public static void main(String[] args) throws IOException, Exception { 2 // 建立Socket客戶端對象 3 Socket socket = new Socket("192.168.14.81", 9999); 4 // 獲取OutputStream對象,準備發送數據 5 OutputStream stream = socket.getOutputStream(); 6 // 準備發送的數據 7 String s = "我啦啦啦啦啦"; 8 // 將字符轉換成byte發送 9 stream.write(s.getBytes()); 10 // 關閉資源 11 socket.close(); 12 stream.close(); 13 }
*服務器端
1 public static void main(String[] args) throws IOException { 2 // 建立ServerSocket服務器端對象,設置端口號進行監聽 3 ServerSocket ss = new ServerSocket(9999); 4 // 調用accept方法進行監聽,這裏之後的代碼先不會執行,和客戶端創建鏈接以後在繼續執行 5 Socket accept = ss.accept(); 6 // 根據accept對象建立InputStream對象用來接收數據 7 InputStream stream = accept.getInputStream(); 8 // 準備接收數據的容器 9 byte[] b = new byte[1024]; 10 // 接收數據據的長度 11 int len; 12 // 往b裏讀取數據 13 len = stream.read(b); 14 // 獲取客戶端的InetAddress對象 15 InetAddress inetAddress = accept.getInetAddress(); 16 // 輸出客戶端的IP 17 System.out.println("-------->" + inetAddress.getHostAddress()); 18 // 輸出接收的數據從0到len 19 System.out.println(new String(b, 0, len)); 20 // 關閉資源,由於服務器在正常狀況下不會關閉,因此這裏指關閉流資源 21 stream.close(); 22 }
模擬網站登陸舉例:
*用戶類
1 public class User { 2 private String username; 3 private String pwd; 4 5 public User() { 6 super(); 7 // TODO Auto-generated constructor stub 8 } 9 10 public User(String username, String pwd) { 11 super(); 12 this.username = username; 13 this.pwd = pwd; 14 } 15 16 public String getUsername() { 17 return username; 18 } 19 20 public void setUsername(String username) { 21 this.username = username; 22 } 23 24 public String getPwd() { 25 return pwd; 26 } 27 28 public void setPwd(String pwd) { 29 this.pwd = pwd; 30 } 31 @Override 32 public boolean equals(Object obj) { 33 if (this == obj) 34 return true; 35 if (obj == null) 36 return false; 37 if (getClass() != obj.getClass()) 38 return false; 39 User other = (User) obj; 40 if (pwd == null) { 41 if (other.pwd != null) 42 return false; 43 } else if (!pwd.equals(other.pwd)) 44 return false; 45 if (username == null) { 46 if (other.username != null) 47 return false; 48 } else if (!username.equals(other.username)) 49 return false; 50 return true; 51 } 52 }
*模擬的用戶數據
1 public class Obj { 2 private static ArrayList<User> arr = new ArrayList<>(); 3 static { 4 arr.add(new User("admin", "123456")); 5 arr.add(new User("lisi", "1111")); 6 arr.add(new User("wang5", "3333")); 7 arr.add(new User("lisi", "000000")); 8 //System.out.println(arr); 9 10 } 11 public static ArrayList<User> get() { 12 return arr; 13 } 14 }
*客戶端
1 public static void main(String[] args) throws IOException, IOException { 2 // 根據IP地址和端口號建立客戶端對象 3 Socket socket = new Socket("192.168.14.27", 8888); 4 // 鍵盤錄入用戶名和密碼 5 BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 6 System.out.println("請輸入用戶名"); 7 String username = reader.readLine(); 8 System.out.println("請輸入密碼"); 9 String pwd = reader.readLine(); 10 // 根據socket對象建立字符輸出流,爲了方便用PrintWriter類傳輸數據 11 PrintWriter writer = new PrintWriter(socket.getOutputStream(), true); 12 // 將用戶名和密碼寫到服務器 13 writer.println(username); 14 writer.println(pwd); 15 // 根據socket建立BufferedReader用來接收返回的數據 16 BufferedReader reader2 = new BufferedReader(new InputStreamReader(socket.getInputStream())); 17 // 讀取返回的數據 18 String line = reader2.readLine(); 19 // 顯示登陸提示 20 System.out.println(line); 21 // 關閉資源 22 reader2.close(); 23 reader.close(); 24 writer.close(); 25 socket.close(); 26 }
*服務器
public static void main(String[] args) throws IOException { // 建立服務端ServerSocket對象,設置端口號 ServerSocket socket = new ServerSocket(8888); // 調用accept方法,返回Socket對象 Socket accept = socket.accept(); // 根據Socket對象獲取InputStream對象對其轉型,建立BufferedReader對象 BufferedReader reader = new BufferedReader(new InputStreamReader(accept.getInputStream())); // 讀取客戶端傳來的用戶名和密碼 String username = reader.readLine(); String pwd = reader.readLine(); // 根據傳過來的用戶名和密碼建立User對象 User user = new User(username, pwd); // 獲取OutputStream()對象將其轉換,準備把處理的結果返回給客戶端 PrintWriter writer = new PrintWriter(accept.getOutputStream(), true); // 獲取正確的用戶集合 ArrayList<User> list = Obj.get(); // 判斷根據傳過來的用戶名和密碼建立User對象是否在集合裏存在 if (list.contains(user)) { // 存在,返回登錄成功提示 writer.println("登錄成功"); } else { // 不存在,返回登陸失敗提示 writer.println("登陸失敗"); } // 關閉資源 writer.close(); accept.close(); }