1. 網(wǎng)絡(luò)通信概述
可以參考link中的第二點(diǎn)網(wǎng)絡(luò)基礎(chǔ)
1.1 軟件的結(jié)構(gòu)
C/S 結(jié)構(gòu):客戶端、服務(wù)器
B/S 結(jié)構(gòu): 瀏覽器、服務(wù)器
1.2 網(wǎng)絡(luò)通信協(xié)議
網(wǎng)絡(luò)通信協(xié)議
一個網(wǎng)絡(luò)中的計(jì)算機(jī)需要通信,他們需要遵循一些規(guī)則
在計(jì)算機(jī)中這些鏈接和通信規(guī)則就稱之為網(wǎng)絡(luò)通信協(xié)議
他們對數(shù)據(jù)的傳輸格式,傳輸速率,傳輸步驟等統(tǒng)一規(guī)定,通信雙方必須同時遵循這些規(guī)則才能通信-
TCP/IP協(xié)議
又稱為 傳輸控制協(xié)議,因特網(wǎng)互聯(lián)協(xié)議
定義了計(jì)算機(jī)如何介入因特網(wǎng),數(shù)據(jù)在節(jié)點(diǎn)之間傳輸?shù)臉?biāo)準(zhǔn)
內(nèi)部包含一系列用于處理數(shù)據(jù)通信的協(xié)議,采用了4層的分層模型,每層都呼叫自己的下一層為自己提供協(xié)議完成自己的需求
鏈路層:鏈路層是用于定義物理傳輸通道,通常是對某些網(wǎng)絡(luò)連接設(shè)備的驅(qū)動協(xié)議,例如針對光纖、網(wǎng)線提供的驅(qū)動
網(wǎng)絡(luò)層:網(wǎng)絡(luò)層是整個TCP/IP協(xié)議的核心,它主要用于將傳輸?shù)臄?shù)據(jù)進(jìn)行分組,將分組數(shù)據(jù)發(fā)送到目標(biāo)計(jì)算機(jī)或者網(wǎng)絡(luò)。
運(yùn)輸層:主要使網(wǎng)絡(luò)程序進(jìn)行通信,在進(jìn)行網(wǎng)絡(luò)通信時,可以采用TCP協(xié)議,也可以采用UDP協(xié)議。
應(yīng)用層:主要負(fù)責(zé)應(yīng)用程序的協(xié)議,例如HTTP協(xié)議、FTP協(xié)議等。
1.3 協(xié)議分類
- UDP——無連接通信協(xié)議
- 發(fā)送端給接收段發(fā)送數(shù)據(jù)不需要接收端同意,也不會判斷是否存在
- 耗資少,通信效率高 通常用于音視頻和普通數(shù)據(jù)的傳輸 視頻會議、視頻聊天一般都采用的UDP協(xié)議
偶爾會丟一兩個數(shù)據(jù)包,但對結(jié)果不會與太大影響。 - 因?yàn)閁DP的無連接性,所有我們傳輸重要數(shù)據(jù)時最好不要用UDP協(xié)議
- TCP——面向連接通信協(xié)議
在傳輸數(shù)據(jù)之前會先建立連接,然后才會開始傳輸數(shù)據(jù),可以提供兩個節(jié)點(diǎn)之間無差錯的數(shù)據(jù)傳輸。
三次握手:- 客戶端向服務(wù)端發(fā)送連接請求,等待服務(wù)器確認(rèn)
- 服務(wù)端向客戶端發(fā)送一個響應(yīng),回應(yīng)收到了請求
- 客戶端再次向服務(wù)端發(fā)送已經(jīng)收到確認(rèn)信息的響應(yīng),確認(rèn)建立連接
通過三次握手建立連接后,客戶端與服務(wù)端就可以進(jìn)行數(shù)據(jù)傳輸了。因?yàn)榇颂匦裕琓CP協(xié)議可以保證傳輸數(shù)據(jù)的安全
1.4網(wǎng)絡(luò)編程的3要素
協(xié)議
計(jì)算機(jī)網(wǎng)絡(luò)通信必須遵循的規(guī)則,詳情見上述IP地址
互聯(lián)網(wǎng)協(xié)議地址,設(shè)備的唯一編號
ipV4:32位
ipV6:128位端口號
- 指定了ip只能準(zhǔn)確的找到計(jì)算機(jī),但是計(jì)算機(jī)上有很多的軟件。
- 如何準(zhǔn)確的與計(jì)算機(jī)上的軟件進(jìn)行通信呢?
- ip:端口 就可以準(zhǔn)確的找到軟件
- 1024之前的端口基本上被已知軟件占用
- 端口不能多個軟件共同使用
- 端口的范圍為 0——65535
2. TCP協(xié)議
TCP通信能實(shí)現(xiàn)兩臺計(jì)算機(jī)之間進(jìn)行數(shù)據(jù)交換,通信的兩端,嚴(yán)格的區(qū)分客戶端和服務(wù)端
TCP通信: 面向連接的通信,客戶端和服務(wù)端必須進(jìn)行三次握手才能建立邏輯連接,才能安全通信
2.1 通信的步驟:
- 服務(wù)器先啟動,服務(wù)器不會主動發(fā)起對客戶段的連接。
- 客戶端發(fā)起請求,客戶端與服務(wù)端就會建立一個邏輯連接
連接中包括一個對象——IO對象 - 客戶端與服務(wù)端就可以使用
傳輸?shù)臄?shù)據(jù)不限于字符,所以IO對象是字節(jié)流對象
2.2 服務(wù)端必須明確的事情
- 多個客戶端與服務(wù)端進(jìn)行交互時,服務(wù)端必須明確是哪個客戶端與其交互
在服務(wù)端有一個方法叫,accept可以獲取到請求客戶端對象 - 多個客戶端與服務(wù)器進(jìn)行交互時,就需要使用多個IO流對象
服務(wù)端是沒有IO流的,服務(wù)端可以獲取到發(fā)起請求的客戶端的Socket對象,從而使用每個客戶端中的Socket中提供的IO流與客戶端進(jìn)行交互- 服務(wù)器使用客戶端的字
節(jié)輸入流讀取客戶端發(fā)送的數(shù)據(jù) - 服務(wù)器使用客戶端的字
節(jié)輸出流給客戶端會寫數(shù)據(jù)
簡單說就是服務(wù)器使用客戶端的流進(jìn)行交互
- 服務(wù)器使用客戶端的字

code:
- TcpClient服務(wù)類
public class TcpClient {
public static void main(String[] args) throws IOException {
//1. 創(chuàng)建一個客戶端對象Socket,構(gòu)造方法寫入IP地址和端口號
Socket socket = new Socket("127.0.0.1",8888);
//2. 使用Socket對象中的 方法getOutputStream();獲取到網(wǎng)絡(luò)字節(jié)輸出流
OutputStream outputStream = socket.getOutputStream();
//3. 使用網(wǎng)絡(luò)字節(jié)輸出流中的write方法來給服務(wù)器發(fā)送數(shù)據(jù)
outputStream.write("你好服務(wù)器??!".getBytes());
//拿到回復(fù)
InputStream inputStream = socket.getInputStream();
//讀取類容
ReaderMsg.readers(inputStream);
// 釋放資源
socket.close();
}
}
- TcpServer 服務(wù)類
public class TcpServer {
public static void main(String[] args) throws IOException {
//創(chuàng)建服務(wù)器對象ServerSocket,并通過構(gòu)造方法指定端口號
ServerSocket serverSocket = new ServerSocket(8888);
//使用ServerSocket中的accpet方法來得到客戶端的Socket對象
Socket socket = serverSocket.accept();
//使用Socket中的getInputStream();方法來獲取到網(wǎng)絡(luò)輸入流
InputStream inputStream = socket.getInputStream();
//讀取類容
ReaderMsg.readers(inputStream);
//使用Socket中的getOutputStream();方法來獲取到網(wǎng)絡(luò)輸出流
OutputStream outputStream = socket.getOutputStream();
outputStream.write("我收到你的問候!".getBytes());
//關(guān)閉流
socket.close();
serverSocket.close();
}
}
- 字符輸出類
public class ReaderMsg {
public static void readers(InputStream inputStream) throws IOException {
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
System.out.println(new String(bytes,0,len));
}
}
3. 綜合案例_文件上傳
客戶端:
public class TcpFileClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",8888);
OutputStream outputStream = socket.getOutputStream();
BufferedInputStream biStream = new BufferedInputStream(
new FileInputStream("I:\\download\\myeclise-2018.8.0破解文件-小笨\\readme.txt"));
System.out.println("客戶端:我開始向服務(wù)器上傳數(shù)據(jù)");
byte[] bytes = new byte[512];
int lenth = 0;
while ((lenth = biStream.read(bytes))!=-1){
outputStream.write(bytes,0,lenth);
}
//不寫的話服務(wù)器將不知道什么時候才將文件上傳結(jié)束
socket.shutdownOutput();
InputStream inputStream = socket.getInputStream();
lenth = 0;
while ((lenth = inputStream.read(bytes))!=-1){
System.out.println(new String(bytes,0,lenth));
}
System.out.println("客戶端:數(shù)據(jù)上傳完畢");
biStream.close();
socket.close();
}
}
服務(wù)端:
public class TcpFileServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
File file = new File("I:\\chenpengFiles\\test");
if (!file.exists()){
file.mkdirs();
}
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
new FileOutputStream(file+"http://test.txt"));
System.out.println("服務(wù)器:開始上傳文件");
byte[] bytes = new byte[512];
int lenth = 0;
while ((lenth = inputStream.read(bytes))!=-1){
bufferedOutputStream.write(bytes,0,lenth);
}
socket.getOutputStream().write("文件上傳成功!".getBytes());
System.out.println("服務(wù)器:文件上傳成功");
bufferedOutputStream.close();
socket.close();
serverSocket.close();
}
}
優(yōu)化版:
public class TcpFileServerThread {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8888);
while (true){
Socket socket = serverSocket.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
InputStream inputStream = socket.getInputStream();
File file = new File("I:\\chenpengFiles\\test");
if (!file.exists()){
file.mkdirs();
}
String fileName = "\\"+new Random().nextInt(99999)+".txt";
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
new FileOutputStream(file+fileName));
System.out.println("服務(wù)器:開始上傳文件");
byte[] bytes = new byte[512];
int lenth = 0;
while ((lenth = inputStream.read(bytes))!=-1){
bufferedOutputStream.write(bytes,0,lenth);
}
socket.getOutputStream().write("文件上傳成功!".getBytes());
System.out.println("服務(wù)器:文件上傳成功");
bufferedOutputStream.close();
}catch (IOException e){
System.out.println(e.getMessage());
throw new RuntimeException();
}finally {
try {
socket.close();
}catch (Exception e){
throw new RuntimeException();
}
}
}
}).start();
}
}
}
4. 模擬BS服務(wù)器案例
code:
public class WebServer {
private String paths = "H:\\javaCode\\ideaCode\\java2019\\demo\\src\\com\\looc\\demo12網(wǎng)絡(luò)編程\\demo2\\";
public void webServer() throws IOException {
ServerSocket serverSocket = new ServerSocket(80);
//監(jiān)聽事件
while (true){
//拿到socket對象
Socket socket = serverSocket.accept();
//開啟多線程提高效率
new Thread(new Runnable() {
@Override
public void run() {
try {
InputStream inputStream = socket.getInputStream();
//讀響應(yīng)體
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream,"US-ASCII"));
//讀取到文件
String str = br.readLine().split(" ")[1].substring(1);
BufferedInputStream bfs = new BufferedInputStream(
new FileInputStream(paths+str));
//回顯
OutputStream oStream = socket.getOutputStream();
oStream.write("HTTP/1.1 200 OK\r\n".getBytes());
oStream.write("Content-Type:text/html\r\n".getBytes());
oStream.write("\r\n".getBytes());
byte[] bytes = new byte[1024];
int len = 0;
while ((len = bfs.read(bytes))!=-1){
oStream.write(bytes,0,len);
}
//關(guān)閉流
bfs.close();
br.close();
}catch (Exception e){
System.out.println(e.getMessage());
}finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
public static void main(String[] args) throws IOException {
WebServer webServers = new WebServer();
webServers.webServer();
}
}