主要內容:
一,網絡體系結構:
二,網絡編程中的網絡要素
三,UDP與TCP的詳細講解
四,瀏覽器和服務端的原理(應用層http協議消息頭)
五,網絡結構
一,網絡體系結構:
① 計算機網絡的體系結構(architecture)是計算機網絡的各層及其協議的集合。
② 體系結構就是這個計算機網絡及其部件所應完成的功能的精確定義。
③ 實現(implementation)是遵循這種體系結構的前提下用何種硬件或軟件完成這些功能的問題。
④ 體系結構是抽象的,而實現則是具體的,是真正在運行的計算機硬件和軟件。
兩個模型:OSI模型,TCP/IP。
OSI模型的傳輸過程:
二,網絡編程中的網絡要素
三要素:ip地址,端口,協議。
IP地址
① 網絡中每臺主機都必須有一個唯一的IP地址。
② 因特網上的IP地址具有全球唯一性。
③ IP地址由32位二進制組成,佔4個字節,常用十進制的格式來表示,例如:192.168.0.5。
④ 對應的類-InetAddress。
獲取ip地址代碼實現:
//獲取本機的ip對象
InetAddress ip = InetAddress.getLocalHost();
//獲取ip地址
System.out.println("ip地址 "+ip.getHostAddress());
//獲取主機名稱
System.out.println("主機名稱 "+ip.getHostName());
//通過主機名稱,獲取ip對象,這裏用百度爲例
InetAddress ip_baidu = InetAddress.getByName("www.baidu.com");
//獲取ip地址
System.out.println("ip地址 "+ip_baidu.getHostAddress());
//獲取主機名稱
System.out.println("主機名稱 "+ip_baidu.getHostName());
端口號
① 端口號用來表示該計算機上的應用程序,代表此應用程序邏輯地址。
② 端口號使用一個16位的數字來表示,它的範圍是0~65535,1024以下的端口號保留給預定義的服務。例如:http使用80端口。
協議概念
爲計算機網絡中進行數據交換而建立的規則、標準或約定的集合。
常見的傳輸協議
UDP:
① 將數據源和目的地封裝在數據包中,不需要建立連接,每個數據包的大小限制在64k之內。
② 因爲不需要進行連接,所以速度快,但因此也不安全,所以是不可靠協議。
TCP:
① 建立連接,形成傳輸數據的通道,在連接中能進行大數據量傳輸。
② 通過三次握手完成連接,是可靠協議,但由於必須要建立連接,因此效率會降低。
三,UDP與TCP的詳細講解
UDP的傳輸
DatagramSocket(); 發送和接收數據包的套接字
DatagramPacket(); 數據報包,實現無連接包投遞服務
創建UDP傳輸的發送端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPSendDemo {
public static void main(String[] args) throws IOException {
/*
* 創建UDP傳輸的發送端
* 思路:
* 1,建立udp的socket服務。
* 2,將要發送的數據封裝到數據包中
* 3,通過udp的socket服務將數據包發送出去
* 4,關閉socket服務。
*/
//1,建立udp的socket服務
DatagramSocket socket = new DatagramSocket(8888);
//2,將要發送的數據封裝到數據包中
String str = "UDP傳輸演示:我來了小兄弟";
byte[] buf = str.getBytes();
DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getLocalHost(), 10000);
//3,通過udp的socket服務將數據包發送出去
socket.send(dp);
//4,關閉socket服務。
socket.close();
}
}
創建UDP傳輸的接收端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPReceDemo {
public static void main(String[] args) throws IOException {
/*
* 建立UDP接收端的思路。
* 1,建立udp socket服務,因爲是要接收數據,必須要明確一個端口號。
* 2,創建數據包,用於存儲接收到的數據。方便用數據包對象解析這些數據。
* 3,使用socket服務的receive方法將接受的數據存儲到數據包中
* 4,通過數據包的方法解析數據包中的數據
* 5,關閉資源
*/
//1,建立udp socket服務,因爲是要接收數據,必須要明確一個端口號。
DatagramSocket socket = new DatagramSocket(10000);
//2,創建數據包,用於存儲接收到的數據。方便用數據包對象解析這些數據。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
//3,使用socket服務的receive方法將接受的數據存儲到數據包中
socket.receive(dp); //阻塞方法
//4,通過數據包的方法解析數據包中的數據
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String content = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+":"+port+" "+content);
//5,關閉資源
socket.close();
}
}
這時我們,發送端和接受端都創建好了,那我們應該先運行,那段代碼呢?
這裏我想說的是,我們無論先運行那段代碼都可以,只不過如果我們先運行發送端,然後在運行接收端的話,我們會發現,接收端不會接收到任何結果。
這是因爲UDP是不需要先建立連接的,所以我們發送數據,不需要知道你接不接收,我們只管發數據就行了。
在這裏呢?因爲我們要想接受到數據,所以我們先啓動,接受端,然後在啓動發送端,就可以接手到數據了。
UDP實現聊天程序
思路架構:
聊天室其實就是在一個用戶上既可以發送數據,又可以接收數據,那麼,就需要兩個類分別代表發送和接收,並且這兩個要能夠同時進行,此時就需要多線程。
發送:用UDP發送數據,那麼就需要通過DatagramSocket類幫助發送,也就是任務對象一建立,就需要socket對象,那麼我們可以將DatagramSocket作爲成員變量並進行封裝。
然後線程中的run()如何實現呢?既然是發送端,那麼核心任務就是發送數據了:
1.發送的數據是通過鍵盤進行錄入的 2.將數據封裝到數據包中 3.將數據包發送出去
接收:和發送一樣,接收也同樣需要通過DatagramSocket類,創建socket對象
接收數據如何實現?其實原理就和上面的UDP接收端是一樣的:
1.接收具體的數據內容 2.創建數據包對象 3.將受到的數據存儲到數據包中 4.獲取數據
但是,這樣的話,接收和發送的數據就都只是一次性的,要讓它變爲不停的來回發送和接收,那麼只需要給條件加一個循環就可以了,以下是代碼實現:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//發送端
class Send implements Runnable{
private DatagramSocket socket = null;
public Send(DatagramSocket socket) {
super();
this.socket = socket;
}
@Override
public void run() {
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
try {
//鍵盤錄入
while((line = bufr.readLine()) != null) {
byte[] buf = line.getBytes();
// 192.168.2.104 是我的ip地址
DatagramPacket dp = new DatagramPacket(buf,buf.length, InetAddress.getByName("192.168.2.104"), 10000);
socket.send(dp);
if(line.equals("886"))
break;
}
bufr.close();
socket.close();
}catch(IOException e) {
}
}
}
//接收端
class Receive implements Runnable{
private DatagramSocket socket = null;
public Receive(DatagramSocket socket) {
super();
this.socket = socket;
}
@Override
public void run() {
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
try {
while(true) {
socket.receive(dp);
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String content = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+":"+port+" "+content);
if(content.equals("886"))
break;
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class UDPChatDemo {
public static void main(String[] args) throws IOException {
DatagramSocket send = new DatagramSocket(8888);
DatagramSocket receive = new DatagramSocket(10000);
new Thread(new Send(send)).start();
new Thread(new Receive(receive)).start();
}
}
運行結果如下:
在這裏我們發現在這個程序當中,我們是自己跟自己聊天,那要怎麼才能讓其他人跟我們聊天呢?
在Send類中 192.168.2.104 是我的ip地址,要想然別人也能一起聊天,需要把程序先發送給另一個,電腦,並且要保證,自己的電腦,和另外一臺電腦在同一個局域網中,然後獲取對方的ip,然後將其修改成對方的ip地址就可以了。
那我們怎樣才實現多人聊天呢?
原理跟上面一樣,但是我們這是填寫的ip地址會發生一點小小的變換。
通過cmd 我得到了我的IP地址如下:
在這裏有個子網掩碼,通過ip地址和子網掩碼我們可以得到,在這個局域網裏面的廣播地址是:
192.168.2.255,然後在改成廣播地址,就可以實現羣聊了。
(以下是我的猜測)
在這裏呢?我突然想到了QQ聊天是不是也是這樣的呢?兩個qq互加好友,然後相互聊天,構成一個局域網,然後利用UDP進行聊天,如果對方不在線,就先把聊天數據,保留在本地上,然後對方上線後,在聊天數據傳輸過去。
TCP的傳輸
TCP需要建立客戶端和服務端,但是TCP是面向連接的,只有連接成爲通路,纔會在客戶端和服務端之間進行數據的傳輸,而這個連接過程就被成爲三次握手,簡易圖解如下
創建TCP的客戶端
Socket(); 客戶端的套接字
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class TCPClientDemo {
public static void main(String[] args) throws IOException {
/*
* Tcp傳輸,客戶端建立的過程。
* 1,創建tcp客戶端socket服務。使用的是Socket對象。
* 建議該對象一創建就明確目的地。要連接的主機。
* 2,如果連接建立成功,說明數據傳輸通道已建立。
* 該通道就是socket流 ,是底層建立好的。 既然是流,說明這裏既有輸入,又有輸出。
* 想要輸入或者輸出流對象,可以找Socket來獲取。
* 可以通過getOutputStream(),和getInputStream()來獲取兩個字節流。
* 3,使用輸出流,將數據寫出。
* 4,關閉資源。
*/
//1,創建tcp客戶端socket服務。使用的是Socket對象。
Socket socket = new Socket(InetAddress.getLocalHost(),9090);
//2,使用輸出流,將數據寫出。
OutputStream out = socket.getOutputStream();
byte[] buf = "TCP傳輸:小老弟我來了".getBytes();
out.write(buf);
//3,關閉資源。
socket.close();
}
}
創建TCP的服務端
ServerSocket(); 服務端的套接字
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServerDemo {
public static void main(String[] args) throws IOException {
/*
* 建立tcp服務端的思路:
* 1,創建服務端socket服務。通過ServerSocket對象。
* 2,服務端必須對外提供一個端口,否則客戶端無法連接。
* 3,獲取連接過來的客戶端對象。
* 4,通過客戶端對象獲取socket流讀取客戶端發來的數據
* 並打印在控制檯上。
* 5,關閉資源。關客戶端,關服務端。
*/
//1,創建服務端socket服務。通過ServerSocket對象。
ServerSocket ss = new ServerSocket(9090);
//2,獲取連接過來的客戶端對象
Socket s = ss.accept();//阻塞式
//3,通過獲取得到的客戶端對象,使用getInputStream()方法,讀取
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = 0;
len = in.read(buf);
String ip = s.getInetAddress().getHostAddress();
int port = s.getPort();
String content = new String(buf,0,len);
System.out.println(ip+":"+port+" "+content);
//4,關閉資源
ss.close();
}
}
這時我們,客戶端和服務器都創建好了,那我們應該先運行,那段代碼呢?
如果,我們先啓動客戶端,那麼會報錯,因爲TCP是面向連接的,在傳輸前,需要建立連接,所以我們應該先啓動,服務端,然後在啓動客戶端。
TCP客戶端與服務端進行交互(分析)
客戶端輸入字母數據,發送給服務端,服務端收到後顯示在控制檯,並將該數據轉成大寫返回給客戶端
直到客戶端輸入over.轉換結束.
創建一個英文大寫轉換服務器.
分析:
有客戶端和服務端,使用tcp傳輸
客戶端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
public class TransClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket(InetAddress.getLocalHost(),9090);
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
//true的作用是,刷新緩衝區,將PrintWriter中的內容刷到socket.getOutputStream()裏面
//如果沒有true的話,改成 PrintWriter out = new PrintWriter(socket.getOutputStream());
//在下面代碼中out.println(line);執行時,我們需要在這句代碼下面,寫上out.flush();
//如果不寫的話,socket.getOutputStream()流裏面沒有內容,服務器那端將會一直等待
//從而使程序就會無法得到我們想要的結果。
PrintWriter out = new PrintWriter(socket.getOutputStream(),true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = null;
while((line = bufr.readLine()) != null) {
if(line.equals("over"))
break;
//如果我們將out.println(line); 改成out.print(line); 也會出現問題
//原因如下:在服務器那端使用的是in.readLine()來讀取數據,而readLine()讀取數據結束的標記是"\r\n"(windows下是這樣的)
//所以使用out.print(line)時,因爲沒有將"\r\n"寫進去,就會讓服務器那端使用的是in.readLine()一直等待,等到讀到結束標記符爲止。
//out.print(line)改成out.print(line+"\r\n");也可以使程序正常運行
out.println(line);
System.out.println(in.readLine());
}
socket.close();
}
}
服務器:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TransServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(9090);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"..............connected");
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
//跟客戶端那邊解釋一樣
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
String line = null;
while((line = in.readLine()) != null) {
System.out.println(ip+": "+line);
//跟客戶端那邊解釋一樣
out.println(line.toUpperCase());
}
ss.close();
}
}
運行結果圖如下:
TCP上傳文本客戶端
需求:客戶端向服務器上傳文件。
在這裏呢?需要用到IO流技術,可查看IO流一,IO流二。
客戶端:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
public class UploadFileClient {
public static void main(String[] args) throws IOException{
Socket socket = new Socket(InetAddress.getLocalHost(),9090);
BufferedReader bufr = new BufferedReader(new FileReader("file.txt"));
PrintWriter out = new PrintWriter(socket.getOutputStream(),true);
String line = null;
while((line = bufr.readLine()) != null) {
out.println(line);
}
//告訴服務端,客戶端寫完了
socket.shutdownOutput();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(in.readLine());
bufr.close();
socket.close();
}
}
服務端:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadFileServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(9090);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+".............connected");
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
BufferedWriter bufw = new BufferedWriter(new FileWriter("file_1.txt"));
String line = null;
while((line = in.readLine()) != null) {
bufw.write(line);
bufw.flush();
}
//true在這裏作用是,自動刷新緩衝區
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println("上傳成功");
bufw.close();
ss.close();
}
}
四,瀏覽器和服務端的原理(應用層http協議消息頭)
我們在將這個模塊時需要用到Tomcat,所以我們需要安裝Tomcat,如果你還沒有安裝好可以點點這個小東西
然後我們在apache-tomcat-9.0.22\webapps\ROOT這個目錄下面創建一個文件夾MYWORK,並在裏面創建一個1.html,1.html的內容如下:
<html>
<head>
<title>這是我的網頁</title>
</head>
<body>
<h1>歡迎光臨</h1>
<font size='5' color="red">這是一個tomcat服務器中的資源。是一個html網頁。</font>
</body>
</html>
自定義服務端,
使用已有的客戶端IE,瞭解一下客戶端給服務端發了什麼請求?
MyTomcat代碼如下:
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class MyTomcat {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
ServerSocket ss = new ServerSocket(9090);
Socket s = ss.accept();
InputStream in = s.getInputStream();
int len = 0;
byte[] buf = new byte[1024];
while((len = in.read(buf)) != -1) {
System.out.print(new String(buf,0,len));
}
ss.close();
}
}
然後我們啓動MyTomcat,再在遊覽器(ie遊覽器)裏面輸入http://192.168.5.103:9090/ (格式:http://ip地址:端口號/)
然後我們接收到的內容是:
GET / HTTP/1.1 //請求行 請求方式 /後面跟的是 請求的資源路徑 http的協議版本
//請求消息頭 . 屬性名:屬性值
Accept: text/html, application/xhtml+xml, image/jxr, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: 192.168.5.103:9090
Connection: Keep-Alive
//空行
//請求體。
自定義服務端
瞭解,服務端對於收到客戶端的請求,然後返回什麼數據。
MyIE代碼如下:
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
public class MyIE {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
Socket socket = new Socket(InetAddress.getLocalHost(),8080);
PrintWriter out = new PrintWriter(socket.getOutputStream(),true);
out.println("GET /MYWORK/1.html HTTP/1.1");
out.println("Accept: text/html, application/xhtml+xml, image/jxr, */*");
out.println("Accept-Language: zh-CN");
out.println("User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko");
out.println("Accept-Encoding: gzip, deflate");
out.println("Host: 192.168.5.103:8080");
out.println("Connection: Keep-Alive");
out.println();
out.println();
InputStream in = socket.getInputStream();
int len = 0;
byte[] buf = new byte[1024];
while((len = in.read(buf)) != -1) {
System.out.print(new String(buf,0,len));
}
socket.close();
}
}
輸出結果如下:
//服務端發回應答消息。
HTTP/1.1 200 //應答行,http的協議版本 應答狀態碼 應答狀態描述信息
應答消息屬性信息。 屬性名:屬性值
Accept-Ranges: bytes
ETag: W/"199-1564134739143"
Last-Modified: Fri, 26 Jul 2019 09:52:19 GMT
Content-Type: text/html
Content-Length: 199
Date: Sun, 28 Jul 2019 03:57:09 GMT
//空行
//應答體。
<html>
<head>
<title>這是我的網頁</title>
</head>
<body>
<h1>歡迎光臨</h1>
<font size='5' color="red">這是一個tomcat服務器中的資源。是一個html網頁。</font>
</body>
</html>
那我們要怎麼才能只收到 應答體 的下面部分,我們就需要薪的類 URL ,URLConnection
源碼如下:
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class URLdemo {
public static void main(String[] args) throws IOException {
String str_url = "http://192.168.5.103:8080/MYWORK/1.html";
URL url = new URL(str_url);
//相關屬性
// System.out.println("getProtocol:"+url.getProtocol());
// System.out.println("getHost:"+url.getHost());
// System.out.println("getPort:"+url.getPort());
// System.out.println("getFile:"+url.getFile());
// System.out.println("getPath:"+url.getPath());
// System.out.println("getQuery:"+url.getQuery());
//InputStream in = url.openStream();
//獲取url對象的Url連接器對象。將連接封裝成了對象:java中內置的可以解析的具體協議的對象+socket.
URLConnection conn = url.openConnection();
InputStream in = conn.getInputStream();
int len = 0;
byte[] buf = new byte[1024];
len = in.read(buf);
System.out.print( new String(buf,0,len));
}
}
輸出結果:(這樣遊覽器就可以解析產生界面)
<html>
<head>
<title>這是我的網頁</title>
</head>
<body>
<h1>歡迎光臨</h1>
<font size='5' color="red">這是一個tomcat服務器中的資源。是一個html網頁。</font>
</body>
</html>
五,網絡結構
1,C/S client/server
特點:
該結構的軟件,客戶端和服務端都需要編寫。
可發成本較高,維護較爲麻煩。
好處:
客戶端在本地可以分擔一部分運算。
2,B/S browser/server
特點:
該結構的軟件,只開發服務器端,不開發客戶端,因爲客戶端直接由瀏覽器取代。
開發成本相對低,維護更爲簡單。
缺點:所有運算都要在服務端完成。