day18
復(fù)習
1.緩沖流【重點】
a时迫。作用:對普通類的增強(性能的增強)
b婚脱。BufferedWriter 特有方法: public void newLine();
c。BufferedReader 特有方法: public String readLine();
2.轉(zhuǎn)換流【重點】
轉(zhuǎn)換流可以在讀寫文件時指定編碼
OutputStreamWriter 轉(zhuǎn)換輸出流 寫數(shù)據(jù)時指定編碼
InputStreamReader 轉(zhuǎn)換輸入流 讀數(shù)據(jù)時指定編碼
3.序列化流
操作對象的流
ObjectOutputStream 對象的輸出流(序列化流),寫對象
ObjectInputStream 對象的輸入流(反序列化流),讀對象
4.commons-io工具包
IOUtis 復(fù)制大的或者小的文件
FileUtils 復(fù)制文件也能復(fù)制文件夾
5.裝飾者設(shè)計模式
作用: 在不該變原來類的代碼,也不使用繼承的基礎(chǔ)上對某個類的功能進行增強
步驟:
a.同一個接口(被裝飾類和裝飾類實現(xiàn)同一個接口)
b.含有引用(裝飾類內(nèi)部必須具有被裝飾類的引用)
c.對需要增強的方法進行增強
d.對不需要增強的方法調(diào)用被裝飾類的同名方法
6.打印流
能方便的打印各種數(shù)據(jù)類型
PrintStream
print(各種類型)
println(各種類型)
今日內(nèi)容
- 網(wǎng)絡(luò)編程概念(網(wǎng)絡(luò)通信協(xié)議, IP地址, 端口號)
- TCP 網(wǎng)絡(luò)編程
- 綜合案例(文件上傳)
網(wǎng)絡(luò)編程入門
軟件架構(gòu)介紹
- C/S 架構(gòu): 客戶端/服務(wù)器
- B/S 架構(gòu): 瀏覽器/服務(wù)器
網(wǎng)絡(luò)通信協(xié)議
-
網(wǎng)絡(luò)通信協(xié)議概念
網(wǎng)絡(luò): 互聯(lián)網(wǎng)/局域網(wǎng)
通信: 數(shù)據(jù)傳輸
協(xié)議: 規(guī)則規(guī)范
-
TCP/IP 協(xié)議
TCP/IP協(xié)議:傳輸控制協(xié)議/因特網(wǎng)互聯(lián)協(xié)議, Internet最基本世囊、最廣泛的協(xié)議。它定義了計算機如何連入因特網(wǎng),以及數(shù)據(jù)如何在它們之間傳輸?shù)臉藴势钾ぁK膬?nèi)部包含一系列的用于處理數(shù)據(jù)通信的協(xié)議,并采用了4層的分層模型放典,每一層都呼叫它的下一層所提供的協(xié)議來完成自己的需求碉纺。
Java中支持的常見協(xié)議
java.net
包中包含的類和接口,它們提供低層次的通信細節(jié)刻撒。我們可以直接使用這些類和接口骨田,來專注于網(wǎng)絡(luò)程序開發(fā),而不用考慮通信的細節(jié)声怔。
java.net
包中提供了兩種常見的網(wǎng)絡(luò)協(xié)議的支持:
-
TCP 協(xié)議: 傳輸控制協(xié)議
傳輸控制協(xié)議, TCP協(xié)議是面向連接的通信協(xié)議态贤,即傳輸數(shù)據(jù)之前,在發(fā)送端和接收端建立邏輯連接醋火,然后再傳輸數(shù)據(jù)悠汽,它提供了兩臺計算機之間可靠無差錯的數(shù)據(jù)傳輸箱吕。
優(yōu)點: 保證數(shù)據(jù)是完整的, 安全的
缺點: 性能較低
- 三次握手:TCP協(xié)議中,在發(fā)送數(shù)據(jù)的準備階段柿冲,客戶端與服務(wù)器之間的三次交互茬高,以保證連接的可
靠。- 第一次握手假抄,客戶端向服務(wù)器端發(fā)出連接請求怎栽,等待服務(wù)器確認。服務(wù)器你死了嗎宿饱?
- 第二次握手熏瞄,服務(wù)器端向客戶端回送一個響應(yīng),通知客戶端收到了連接請求谬以。我活著扒恳!为黎!
- 第三次握手邮丰,客戶端再次向服務(wù)器端發(fā)送確認信息,確認連接铭乾。整個交互過程如下圖所示柠座。我知道
了!片橡!
- 三次握手:TCP協(xié)議中,在發(fā)送數(shù)據(jù)的準備階段柿冲,客戶端與服務(wù)器之間的三次交互茬高,以保證連接的可
-
UDP 協(xié)議: 用戶數(shù)據(jù)報協(xié)議
UDP協(xié)議是一個面向無連接的協(xié)議妈经。傳輸數(shù)據(jù)時,不需
要建立連接捧书,不管對方端服務(wù)是否啟動吹泡,直接將數(shù)據(jù)、數(shù)據(jù)源和目的地都封裝在數(shù)據(jù)包中经瓷,直接發(fā)送爆哑。每個數(shù)據(jù)包的大小限制在64k以內(nèi)。它是不可靠協(xié)議舆吮,因為無連接,所以傳輸速度快色冀,但是容易丟失數(shù)據(jù)潭袱。日常應(yīng)
用中,例如視頻會議、QQ聊天等锋恬。優(yōu)點: 性能較高
缺點: 不能保證數(shù)據(jù)完整和不能保證安全性.
網(wǎng)絡(luò)編程的三要素[重點]
協(xié)議
協(xié)議:計算機網(wǎng)絡(luò)通信必須遵守的規(guī)則屯换,已經(jīng)介紹過了,不再贅述。
IP地址
IP地址:指互聯(lián)網(wǎng)協(xié)議地址(Internet Protocol Address)彤悔,俗稱IP嘉抓。IP地址用來給一個網(wǎng)絡(luò)中的計算機設(shè)備做唯一的編號。假如我們把“個人電腦”比作“一臺電話”的話晕窑,那么“IP地址”就相當于“電話號碼”抑片。
127.0.0.1 本機地址(回環(huán)地址), Localhost 只是它另外一個名稱(訪問localhost,內(nèi)部又把 localhost 翻譯為了 127.0.0.1)
-
IP地址分類:
- IPv4:是一個32位的二進制數(shù)杨赤,通常被分為4個字節(jié)敞斋,表示成 a.b.c.d 的形式,例如 192.168.65.100 望拖。
其中a、b挫鸽、c说敏、d都是0~255之間的十進制整數(shù),那么最多可以表示42億個丢郊。由于互聯(lián)網(wǎng)的蓬勃發(fā)展盔沫,IP地址的需求量愈來愈大,但是網(wǎng)絡(luò)地址資源有限枫匾,使得IP的分配越發(fā)緊張架诞。
有資料顯示,全球IPv4地址在2011年2月分配完畢干茉。 - IPv6:為了擴大地址空間谴忧,擬通過IPv6重新定義地址空間,采用128位地址長度角虫,每16個字節(jié)一組沾谓,分成8組十六進制數(shù),表示成 ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 戳鹅,號稱可以為全世界的每一粒沙子編上一個網(wǎng)址均驶,這樣就解決了網(wǎng)絡(luò)地址資源數(shù)量不夠的問題。
- IPv4:是一個32位的二進制數(shù)杨赤,通常被分為4個字節(jié)敞斋,表示成 a.b.c.d 的形式,例如 192.168.65.100 望拖。
特殊的IP地址
本機IP地址: 127.0.0.1 枫虏、 localhost 妇穴。-
常用的IP地址相關(guān)命令
ping 空格 IP地址 ping 220.181.57.216 ping www.baidu.com
端口號
如果說IP地址可以唯一標識網(wǎng)絡(luò)中的設(shè)備,那么端口號就可以唯一標識設(shè)備中的進程(應(yīng)用程序)了隶债。
端口號:用兩個字節(jié)表示的整數(shù)腾它,它的取值范圍是0-65535。其中死讹,0-1023之間的端口號用于一些知名的網(wǎng)絡(luò)服務(wù)和應(yīng)用携狭,普通的應(yīng)用程序需要使用1024以上的端口號。如果端口號被另外一個服務(wù)或應(yīng)用所占用回俐,會導(dǎo)致當前程序啟動失敗逛腿。
利用 協(xié)議 + IP地址 + 端口號 三元組合稀并,就可以標識網(wǎng)絡(luò)中的進程了,那么進程間的通信就可以利用這個標識與其它進程進行交互单默。
InetAddress類的使用
在Java中碘举,可以使用java.net.InetAddress類來表示一個IP地址:
/**
InetAddress類概述
* 一個該類的對象就代表一個IP地址對象。
InetAddress類成員方法
* static InetAddress getLocalHost()
* 獲得本地主機IP地址對象
* static InetAddress getByName(String host)
* 根據(jù)IP地址字符串或主機名獲得對應(yīng)的IP地址對象
* String getHostName();獲得主機名
* String getHostAddress();獲得IP地址字符串
*/
public class InetAddressDemo01 {
public static void main(String[] args) throws Exception {
// 獲得本地主機IP地址對象
InetAddress inet01 = InetAddress.getLocalHost();
// pkxingdeMacBook-Pro.local/10.211.55.2
// 主機名/ip地址字符串
System.out.println(inet01);
// 根據(jù)IP地址字符串或主機名獲得對應(yīng)的IP地址對象
// InetAddress inet02 = InetAddress.getByName("192.168.73.97");
InetAddress inet02 = InetAddress.getByName("baidu.com");
System.out.println(inet02);
// 獲得主機名
String hostName = inet01.getHostName();
System.out.println(hostName);
// 獲得IP地址字符串
String hostAddress = inet01.getHostAddress();
System.out.println(hostName);
System.out.println(hostAddress);
}
}
TCP通信[重點]
TCP通信分為客戶端和服務(wù)器
- 客戶端
- 服務(wù)器端
TCP協(xié)議是面向連接的通信協(xié)議搁廓,即在傳輸數(shù)據(jù)前先在客戶端和服務(wù)器端建立邏輯連接引颈,然后再傳輸數(shù)據(jù)。它
提供了兩臺計算機之間可靠無差錯的數(shù)據(jù)傳輸境蜕。TCP通信過程如下圖所示:
TCP中的兩個重要的類[重點]
- Socket類, 代表客戶端類(套接字)
- ServerSocket類, 代表服務(wù)器端類(服務(wù)器套接字)
- 客戶端和服務(wù)器端對象都具有
getOutputStream()
,getInputStream()
方法,具體的輸入輸出流是根據(jù)具體在客戶端還是服務(wù)器端寫的獲取輸入輸出流. 對于服務(wù)器來說, 在服務(wù)器端寫的 通過獲取的 客戶端對象, 去調(diào)用的輸入輸出流也是相對于服務(wù)器而言的. - 服務(wù)器客戶端之間只有兩條流, 輸入 輸出是相對而言的 客戶端寫給服務(wù)器端的東西實在客戶端的輸出流中, 這條流需要服務(wù)器端是通過客戶端對象調(diào)用獲取輸入流來獲取.
Socket類的介紹和使用
-
構(gòu)造方法
public Socket(String ip, int port)
: 參數(shù):ip為服務(wù)器IP地址, port 為服務(wù)器端口號此構(gòu)造, 會根據(jù)傳入的參數(shù), 自動去連接服務(wù)器,
如果連接成功, 對象正常創(chuàng)建
如果連接失敗, 直接拋出異常
-
常用方法
public OutputStream getOutputStream()
;獲得連接通道字節(jié)輸出流對象public InputStream
:獲取連接通道中字節(jié)輸入流對象public void shutDownoutput()
: 關(guān)閉連接通道中的輸出流,相當于是告知服務(wù)器關(guān)閉了輸出流public void shutDownInput()
: 關(guān)閉連接通道中的輸入流public void close()
關(guān)閉客戶端對象
ServerSocket類的介紹和使用
-
構(gòu)造方法
public ServerSocket(int port)
: 指定服務(wù)器端使用的端口號 -
常用方法
public Socket accept()
: 接收連接到服務(wù)器的那個Socket對象,如果暫時沒有客戶端, 該方法會阻塞.publci void close()
; 關(guān)閉服務(wù)器端 -
注意
在服務(wù)器中, 使用accept()方法返回的客戶端對象, 再通過客戶端對象調(diào)用 getOutputStream()方法, 此時獲取的字節(jié)輸出流 是相對于服務(wù)器端來說的
TCP單向和雙向通信
簡單的TCP 通信實現(xiàn)(單向通信)
客戶端給服務(wù)器發(fā)送信息,服務(wù)器不回!
public class SocketDemo {
public static void main(String[] args) throws IOException {
//1.創(chuàng)建Socket對象
Socket socket = new Socket("127.0.0.1",8888);
System.out.println("連接服務(wù)器成功...");
//2.獲取輸出流
OutputStream out = socket.getOutputStream();
//3.調(diào)用輸出流的write方法
out.write("你好我是客戶端!".getBytes());
System.out.println("數(shù)據(jù)發(fā)送成功...");
//4.釋放資源
out.close();
socket.close();
System.out.println("客戶端關(guān)閉了...");
}
}
public class ServerSocketDemo {
public static void main(String[] args) throws IOException {
//1.創(chuàng)建ServerSocket對象
ServerSocket server = new ServerSocket(8888);
System.out.println("服務(wù)器啟動了...");
//2.接收連接到的客戶端對象
Socket socket = server.accept(); //阻塞
System.out.println("有客戶端來了...");
//3.獲取輸入流
InputStream in = socket.getInputStream();
//4.調(diào)用輸入流的read方法
byte[] bs = new byte[1024];
int len = in.read(bs);
System.out.println("客戶端說:"+new String(bs,0,len));
//5.釋放資源
in.close();
socket.close();
server.close();
}
}
簡單的TCP 通信實現(xiàn)(雙向通信)
客戶端給服務(wù)器發(fā)信息,服務(wù)器接收到之后,給客戶端回信息
public class SocketDemo {
public static void main(String[] args) throws IOException {
//客戶端步驟:
//1.創(chuàng)建Socket對象
Socket socket = new Socket("127.0.0.1",9999);
System.out.println("連接成功...");
//2.獲取輸出流
OutputStream out = socket.getOutputStream();
//3.調(diào)用輸出流的write方法
out.write("你好,我又來了...".getBytes());
System.out.println("發(fā)送成功...");
//==讀取服務(wù)器回的信息==
//4.獲取輸入流
InputStream in = socket.getInputStream();
//5.調(diào)用輸入流的read方法
byte[] bs = new byte[1024];
int len = in.read(bs);
System.out.println("服務(wù)器回復(fù):" + new String(bs, 0, len));
//================
//6.釋放資源
in.close();
out.close();
socket.close();
System.out.println("客戶端關(guān)閉...");
}
}
public class ServerSocketDemo {
public static void main(String[] args) throws IOException {
//服務(wù)器的步驟
//1.創(chuàng)建ServerSocket對象
ServerSocket server = new ServerSocket(9999);
System.out.println("服務(wù)器啟動...");
//2.接收連接到的客戶端對象
Socket socket = server.accept();//阻塞
System.out.println("客戶端來了...");
//3.獲取輸入流
InputStream in = socket.getInputStream();
//4.調(diào)用輸入流的read方法
byte[] bs = new byte[1024];
int len = in.read(bs);
System.out.println("客戶端說:" + new String(bs, 0, len));
//=======回信息========
//5.獲取輸出流
OutputStream out = socket.getOutputStream();
//6.調(diào)用輸出流的write方法
out.write("您的信息我收到了,安息吧!".getBytes());
System.out.println("成功回復(fù)...");
//====================
//7.釋放資源
out.close();
in.close();
socket.close();
server.close();
System.out.println("服務(wù)器關(guān)閉...");
}
}
綜合案例: 文件上傳
文件上傳案例分析
- 【客戶端】輸入流蝙场,從硬盤讀取文件數(shù)據(jù)到程序中。
- 【客戶端】輸出流粱年,寫出文件數(shù)據(jù)到服務(wù)端售滤。
- 【服務(wù)端】輸入流,讀取文件數(shù)據(jù)到服務(wù)端程序台诗。
- 【服務(wù)端】輸出流完箩,寫出文件數(shù)據(jù)到服務(wù)器硬盤中。
- 【服務(wù)端】獲取輸出流拉队,回寫數(shù)據(jù)弊知。
- 【客戶端】獲取輸入流,解析回寫數(shù)據(jù)粱快。
文件上傳案例實現(xiàn)
//客戶端代碼實現(xiàn)
public class SocketDemo {
public static void main(String[] args) throws IOException {
//文件上傳客戶端步驟:
//1.創(chuàng)建Socket
Socket socket = new Socket("127.0.0.1",9999);
System.out.println("連接成功...");
//2.獲取輸出流
OutputStream out = socket.getOutputStream();
//3.創(chuàng)建文件的輸入流
FileInputStream fis = new FileInputStream("555.png");
//循環(huán):一邊讀文件,一邊發(fā)送
byte[] bs = new byte[1024];
int len = 0;
while ((len = fis.read(bs)) != -1) {
out.write(bs,0,len);
}
//添加一句代碼,告知服務(wù)器文件發(fā)送完畢
socket.shutdownOutput();
System.out.println("文件上傳完畢...");
//4.獲取輸入流
InputStream in = socket.getInputStream();
//5.讀取服務(wù)器回復(fù)的信息
len = in.read(bs);
System.out.println("服務(wù)器回復(fù):" + new String(bs, 0, len));
//6.釋放資源
in.close();
fis.close();
out.close();
socket.close();
System.out.println("客戶端關(guān)閉...");
}
}
//服務(wù)器代碼實現(xiàn)
public class ServerSocketDemo {
public static void main(String[] args) throws IOException {
//文件上傳服務(wù)器步驟:
//1.創(chuàng)建ServerSocket
ServerSocket server = new ServerSocket(9999);
System.out.println("服務(wù)器啟動...");
//2.獲取客戶端
Socket socket = server.accept();
System.out.println("有客戶端來了...");
//3.獲取輸入流
InputStream in = socket.getInputStream();
//4.創(chuàng)建文件的輸出流
FileOutputStream fos = new FileOutputStream(System.currentTimeMillis()+".png");
//循環(huán):一邊讀數(shù)據(jù),一邊寫文件
byte[] bs = new byte[1024];
int len = 0;
while ((len = in.read(bs)) != -1) {
fos.write(bs, 0, len);
}
System.out.println("文件保存成功...");
//5.獲取輸出流
OutputStream out = socket.getOutputStream();
//6.給客戶端回信息
out.write("您的圖片我收到了,安息吧~~".getBytes());
System.out.println("消息回復(fù)成功...");
//7.釋放資源
out.close();
fos.close();
in.close();
socket.close();
server.close();
System.out.println("服務(wù)器關(guān)閉...");
}
}
服務(wù)器多線程文件上傳
public class ServerSocketMultiDemo {
public static void main(String[] args) throws IOException {
//文件上傳服務(wù)器步驟:
//1.創(chuàng)建ServerSocket
ServerSocket server = new ServerSocket(9999);
System.out.println("服務(wù)器啟動...");
//2.獲取客戶端
while (true) {
Socket socket = server.accept();
System.out.println("有客戶端來了...");
//創(chuàng)建線程
new Thread(()->{
try {
//3.獲取輸入流
InputStream in = socket.getInputStream();
//4.創(chuàng)建文件的輸出流
FileOutputStream fos = new FileOutputStream(System.currentTimeMillis() + ".png");
//循環(huán):一邊讀數(shù)據(jù),一邊寫文件
byte[] bs = new byte[1024];
int len = 0;
while ((len = in.read(bs)) != -1) {
fos.write(bs, 0, len);
}
System.out.println("文件保存成功...");
//5.獲取輸出流
OutputStream out = socket.getOutputStream();
//6.給客戶端回信息
out.write("您的圖片我收到了,安息吧~~".getBytes());
System.out.println("消息回復(fù)成功...");
//7.釋放資源
out.close();
fos.close();
in.close();
socket.close();
} catch (IOException ie) {
ie.printStackTrace();
}
}).start();
}
//server.close();
//System.out.println("服務(wù)器關(guān)閉...");
}
}
模擬B/S架構(gòu)服務(wù)器
public class ServerSocketDemo {
public static void main(String[] args) throws IOException {
//1.創(chuàng)建服務(wù)器
ServerSocket server = new ServerSocket(12345);
System.out.println("服務(wù)器啟動...");
//2.接收客戶端
while (true) {
Socket socket = server.accept();
System.out.println("客戶端來了..");
//3.獲取輸入流
InputStream in = socket.getInputStream();
//4.使用輸入流的read方法,把瀏覽器發(fā)送過的數(shù)據(jù)讀取出來
//我們現(xiàn)在只想讀取,客戶端要的那個文件
//a.只讀第一行
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String line = br.readLine();
//b.從中獲取瀏覽器想要的那個文件名字
String[] splits = line.split(" ");
String fileName = splits[1].substring(1);
System.out.println("瀏覽器想要:" + fileName);
//5.把瀏覽器想要的文件,讀取出來,發(fā)送給瀏覽器
FileInputStream fis = new FileInputStream(fileName);
OutputStream out = socket.getOutputStream();
byte[] bs = new byte[1024];
int len = 0;
out.write("HTTP/1.1 200 OK\r\n".getBytes());
out.write("Content-Type:text/html\r\n".getBytes());
out.write("\r\n".getBytes());
while ((len = fis.read(bs)) != -1) {
out.write(bs, 0, len);
}
//5.釋放資源
in.close();
socket.close();
}
// server.close();
// System.out.println("服務(wù)器關(guān)閉..");
}
}
模擬HTTP服務(wù)器
// 基于 http 協(xié)議的服務(wù)器
public class HttpServer {
// 獲取 1.jpg 協(xié)議版本
// 請求規(guī)則 GET 資源名稱 HTTP/1.1
// 狀態(tài)碼 200 表示成功 HTTP/1.1
// 響應(yīng)規(guī)則 200 OK 協(xié)議版本
// Content-Type: text/html(網(wǎng)頁) image/jpeg(圖片) ....
// 空行
// 內(nèi)容
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
// 不斷處理連接信息
while(true) {
// 建立一個新的連接
Socket socket = serverSocket.accept();
// 使用一個新的線程處理連接
new Thread(() -> {
// 處理連接秩彤, GET /1.jpg HTTP/1.1
try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
// 讀取第一行
String line = reader.readLine();
// 找到資源部分
String resource = line.split(" ")[1];
resource = resource.substring(1);
System.out.println(resource);
// 返回響應(yīng), 向輸出流寫入資源
// 響應(yīng)規(guī)則 協(xié)議版本 200 OK
// Content-Type: text/html(網(wǎng)頁) image/jpeg(圖片) ....
// 空行
// 內(nèi)容
try (OutputStream out = socket.getOutputStream()) {
out.write("HTTP/1.1 200 OK\n".getBytes());
if(resource.endsWith(".html")) {
out.write("Content-Type: text/html\n".getBytes());
} else if(resource.endsWith(".jpg")) {
out.write("Content-Type: image/jpeg\n".getBytes());
}
out.write("\n".getBytes());
Files.copy(Paths.get(resource), socket.getOutputStream());
}
} catch (IOException e) {
} // finally close
}).start();
}
}
}
今日小結(jié)
能夠說出TCP協(xié)議特點
TCP的特點: 面向有鏈接(先建立連接后才能數(shù)據(jù)傳輸)
UDP的特點: 面向無連接(只需要發(fā)送數(shù)據(jù),不需要關(guān)心對象是否開機,是否存在)
能夠說出TCP協(xié)議下兩個常用類名稱
Socket:客戶端類(構(gòu)造方法,成員方法)
ServerSocket:服務(wù)器類(構(gòu)造方法和成員方法)
"能夠編寫TCP協(xié)議下字符串數(shù)據(jù)傳輸程序"【TCP的單、雙向通信J驴蕖呐舔!3遍!慷蠕!】
能夠理解TCP協(xié)議下文件上傳案例
什么叫上傳: 客戶端把其硬盤中文件,通過網(wǎng)絡(luò)發(fā)送到服務(wù)器端,并保存到服務(wù)器的硬盤中
上傳過程: a.客戶端讀取本地文件 b.通過輸出流發(fā)送給服務(wù)器
c.服務(wù)器讀取輸入流數(shù)據(jù) d.保存到服務(wù)器的本地
能夠理解TCP協(xié)議下BS案例
我們不需要自己編寫客戶端,使用瀏覽器當過客戶端,去訪問服務(wù)器
訪問時: 127.0.0.1:8888