網絡編程
1.1概述
打電話-----------連接------接了------通話 TCP
發短信---------發送了就完事了-----接收 UDP
計算機網絡:
計算機網絡是指將地理位置不同的具有獨立功能的多台計算機及其外部設備,通過通信線路連接起來,在網絡操作系統,網絡管理軟件及網絡通信協議的管理和協調下,實現資源共享和信息傳遞的計算機系統。
網絡編程的目的:
傳播交流信息,數據交換。通信
想要達到這個效果需要什么:
- 如何准確地定位網絡上的一台主機 192.168.16.124:端口,定位到這個計算機的某個資源
- 找到了這個主機,傳輸數據
JavaWeb:網頁編程 B/S架構
網絡編程:TCP/IP C/S架構
1.2、網絡通信的要素
如何實現網絡的通信?
通信雙方地址:
- ip 唯一的
- 端口
- 192.168.16.124:5900 IP加端口就能定位到某一個具體計算機上具體的應用。
規則:網絡通信協議
TCP/IP參考模型:

小結:
- 網絡編程中有兩個主要的問題
- 如何准確地定位到網絡上的一台或者多台主機
- 找到主機之后如何進行通信
- 網絡編程中的要素
- IP和端口號
- 網絡通信協議
- Java萬物皆對象
1.3、IP
ip地址:InetAddress
-
唯一定位一台網絡上的計算機
-
127.0.0.1:本機 localhost
-
ip地址的分類
-
IP地址分類 IPV4/IPV6
-
IPV4 127.0.0.1 4個字節組成,每個字節的長度為0-255。 42億~; 2011年用盡;
-
IPV6 fe80::a8dd:4442:e28a:bc7f%13 128位。8個無符號整數!但這個不全,完整的類似於下面這樣。
2001:0bb2:aaaa:0015::0000:0000:1aaa:1312例如這樣一個ipv6地址
2001:0db8:85a3:0000:1319:8a2e:0370:7344
一共8組,每組4個數字,一共32個數,每個數字都是十六進制數,一個十六進制數可以寫成4個二進制數(十六進制最大的數是f,二進制表示為1111,所以一個十六進制數可以寫成4個二進制數)。
可以理解為有多少位就是多少個二進制數
所以,32×4=128位
-
-
公網(互聯網) - 私網(局域網)
-
ABCD類地址
-
192.168.xx.xx 專門給組織內部使用的
-
-
-
域名:記憶IP問題
- IP:

InetAddress這個類沒有字段沒有構造器,所以是不能new出來的,只能通過靜態方法返回本身才能拿到這個類
所以沒有構造器就不能new出來
import java.net.InetAddress;
import java.net.UnknownHostException;
//測試IP
public class TestInetAddress {
public static void main(String[] args) {
try {
//查詢本機地址
InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1");
System.out.println(inetAddress1);
InetAddress inetAddress3 = InetAddress.getByName("localhost");
System.out.println(inetAddress3);
InetAddress inetAddress4 = InetAddress.getLocalHost();
System.out.println(inetAddress4);
//查詢網站ip地址
InetAddress inetAddress2 = InetAddress.getByName("www.baidu.com");
System.out.println(inetAddress2);
//常用方法
// System.out.println(inetAddress2.getAddress()); //這個不重要
//System.out.println(inetAddress2.getCanonicalHostName()); //規范的名字 這個也不重要
System.out.println(inetAddress2.getHostAddress()); //ip
System.out.println(inetAddress2.getHostName()); //域名 或者自己電腦的名字
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}

1.4、端口
端口表示計算機上的一個程序的進程(任務管理器中的pid是進程id,不是端口號);
-
不同的進程有不同的端口號!用來區分軟件!
-
被規定 0~65535
-
TCP端口/UDP端口,每一個都有0~65535 所以一共65535 * 2 單個協議下,端口號不能沖突。但是假設 tcp用80端口,udp也可以用80端口,不沖突。
-
端口分類
-
公有端口 0~1023
- HTTP:80
- HTTPS:443
- FTP:21
- TELENT:23
-
程序注冊端口:1024~49151,分配給用戶或者程序
- Tomcat:8080
- MySQL:3306
- Oracle:1521
-
動態、私有端口:49152~65535
netstat -ano #查看所有的端口 netstat -ano|findstr "5900" # 查看指定的端口 #這個|豎線在Linux里是管道過濾的意思,先查找|后面的話,再代入到前面的 tasklist|findstr"8696" #查看指定端口的進程用netstat -ano看到的本地地址冒號后面的才是端口號

-
import java.net.InetSocketAddress; //socket:套接字 public class TestInetSocketAddress { public static void main(String[] args) { InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8080); InetSocketAddress socketAddress2 = new InetSocketAddress("localhost", 8080); System.out.println(socketAddress); System.out.println(socketAddress2); System.out.println(socketAddress.getAddress()); System.out.println(socketAddress.getHostName()); //地址 在此電腦系統盤windows->system32->drivers->etc->hosts文件里,如果修改過127.0.0.1,則為修改的值,沒改過,輸出的就是127.0.0.1 System.out.println(socketAddress.getPort()); } }
-

端口映射:

1.5、通信協議
協議:就是一種約定。
網絡通信協議:速率,傳輸碼率,代碼傳輸結構,傳輸控制......
問題:非常的復雜
大事化小:分層!
TCP/IP協議簇:實際上是一組協議
里面兩個重要的協議:
- TCP: 用戶傳輸協議
- UDP:用戶數據報協議
出名的協議:
- TCP:用戶傳輸協議
- IP: 網絡互連協議
TCP UDP對比
TCP:打電話
-
連接,穩定
-
三次握手 四次揮手
最少需要三次,才能保證穩定鏈接! A:你瞅啥 B:瞅你咋地 A:干一場! A:我要走了 B:你真的要走了嗎? B:你真的真的要走了嗎? A:我真的要走了三次握手:
1、發送端首先發送一個帶SYN(synchronize)標志的數據包給接收方【第一次的seq序列號是隨機產生的,這樣是為了網絡安全,如果不是隨機產生初始序列號,黑客將會以很容易的方式獲取到你與其他主機之間的初始化序列號,並且偽造序列號進行攻擊】
2、接收端收到后,回傳一個帶有SYN/ACK(acknowledgement)標志的數據包以示傳達確認信息【SYN 是為了告訴發送端,發送方到接收方的通道沒問題;ACK 用來驗證接收方到發送方的通道沒問題】
3、最后,發送端再回傳一個帶ACK標志的數據包,代表握手結束
若在握手某個過程中某個階段莫名中斷,TCP協議會再次以相同的順序發送相同的數據包。四次揮手:
1、主動斷開方(客戶端/服務端)-發送一個 FIN,用來關閉主動斷開方(客戶端/服務端)到被動斷開方(客戶端/服務端)的數據傳送
2、被動斷開方(客戶端/服務端)-收到這個 FIN,它發回一 個 ACK,確認序號為收到的序號加1 。和 SYN 一樣,一個 FIN 將占用一個序號
3、被動點開方(客戶端/服務端)-關閉與主動斷開方(客戶端/服務端)的連接,發送一個FIN給主動斷開方(客戶端/服務端)
4、主動斷開方(客戶端/服務端)-發回 ACK 報文確認,並將確認序號設置為收到序號加1
-
客戶端、服務端
-
傳輸完成,釋放連接,效率低
UDP:發短信
- 不連接,不穩定
- 客戶端、服務端:沒有明確的界限
- 不管有沒有准備好,都可以發給你
- 導彈
- DDOS:洪水攻擊!(飽和攻擊)
1.6、Tcp
客戶端
-
連接服務器 Socket
-
發送消息
-
import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; //客戶端 public class TCPClientDemo01 { public static void main(String[] args) { Socket socket = null; OutputStream os = null; try { //1、要知道服務器的地址、端口號 InetAddress serverIP = InetAddress.getByName("127.0.0.1"); int port = 9999; //2、創建一個socket連接 socket = new Socket(serverIP, port); //3、發送消息 IO流 os = socket.getOutputStream(); os.write("你好".getBytes()); } catch (Exception e) { e.printStackTrace(); } finally { //關閉的時候記得先開后關原則 if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
服務器
-
建立服務的端口 ServerSocket
-
等待用戶連接 accept
-
接收用戶的消息
-
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; //服務端 public class TCPServerDemo01 { public static void main(String[] args) { ServerSocket serverSocket = null; Socket socket = null; InputStream is = null; ByteArrayOutputStream baos = null; try { //1、服務端得有一個地址 serverSocket = new ServerSocket(9999); //2、等待客戶端連接 socket = serverSocket.accept(); //3、讀取客戶端的消息 is = socket.getInputStream(); /* byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { String msg = new String(buffer, 0, len); System.out.println(msg); } 這是一種暴力的寫法,會有問題,發送的是漢字的話可能會超過1024,輸出會亂碼。 */ //管道流 baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { baos.write(buffer, 0, len); } System.out.println(baos.toString()); } catch (IOException e) { e.printStackTrace(); } finally { //關閉資源 if (baos != null){ try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if (serverSocket != null) { try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }


文件上傳
TCPServerDemo02(服務器端):
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServerDemo02 {
public static void main(String[] args) throws IOException {
//1.創建服務
ServerSocket serverSocket = new ServerSocket(9000);
//2.監聽客戶端的連接
Socket socket = serverSocket.accept();//阻塞式監聽,會一直等待客戶端連接
//3.獲取輸入流
InputStream is = socket.getInputStream();
//4.文件輸出
FileOutputStream fos = new FileOutputStream(new File("receive.jpg")); //文件輸出流
byte[] buffer = new byte[1024];
int len;
while ((len=is.read(buffer))!=-1) {
fos.write(buffer,0,len);
}
//通知客戶端接收完畢了
OutputStream os = socket.getOutputStream();
os.write("我接收完畢了,你可以斷開了".getBytes());
//5.關閉資源
fos.close();
is.close();
socket.close();
serverSocket.close();
}
}
TCPClientDemo02(客戶端):
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class TCPClientDemo02 {
public static void main(String[] args) throws Exception{
//1.創建一個socket連接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9000);
//2.創建一個輸出流
OutputStream os = socket.getOutputStream();
//3.文件流->讀取文件
FileInputStream fis = new FileInputStream(new File("tupian.jpg"));
//4.文件流->寫出文件
byte[] buffer = new byte[1024];
int len;
while ((len=fis.read(buffer)) !=-1) {
os.write(buffer,0,len);
}
//通知服務器,我已經結束了
socket.shutdownOutput(); //我已經傳輸完了,一定要有這一行,否則會一直等待
//確定服務器接收完畢,才能夠斷開連接
InputStream inputStream = socket.getInputStream();
//String byte[] 服務器端的getBytes()返回的是一個字符串,所以用字符串的管道流
ByteArrayOutputStream baos = new ByteArrayOutputStream(); //因為接收過來的不知道是什么,所以要用一個管道流來接收
byte[] buffer2 = new byte[1024];
int len2;
while ((len2=inputStream.read(buffer2))!=-1) {
baos.write(buffer2,0,len2);
}
System.out.println(baos.toString());
//5.關閉資源
baos.close();
inputStream.close();
fis.close();
os.close();
socket.close();
}
}
客戶端讀取文件,把文件寫出去,寫道管道流中。
服務器端連接客戶端,獲取輸入流,再輸出文件。
Tomcat
服務端
- 自定義 S
- Tomcat服務器 S :Java后台開發!
客戶端
- 自定義 C
- 瀏覽器 B
1.7 UDP
發短信:不用連接,需要知道對方的地址
UDPServerDemo01(接收端):
import java.net.DatagramPacket;
import java.net.DatagramSocket;
//還是要等待客戶端的連接!
public class UDPServerDemo01 {
public static void main(String[] args) throws Exception{
//開放端口
DatagramSocket socket = new DatagramSocket(9090);
//接收數據包
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);//接收
socket.receive(packet); //阻塞接收
System.out.println(packet.getAddress().getHostAddress());
System.out.println(new String(packet.getData(),0,packet.getLength())); //這行代碼是輸出包中的內容,傳過來的是byte,轉化為String
//數據包是字節流,轉化為字符流
//關閉連接
socket.close();
}
UDPClientDemo01(發送端):
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//不需要連接服務器
public class UDPClientDemo01 {
public static void main(String[] args) throws Exception {
//1.建立一個Socket
DatagramSocket socket = new DatagramSocket();
//2.建個包
String msg = "你好啊,服務器!";
//發送給誰
InetAddress localhost = InetAddress.getByName("localhost");
int port = 9090;
//數據,數據的長度,要發送給誰
DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, port);
//3.發送包
socket.send(packet);
//4.關閉流
socket.close();
}
}
循環發送消息
接收端:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPReceiveDemo01 {
public static void main(String[] args) throws Exception{
DatagramSocket socket = new DatagramSocket(6666);
//准備接收包裹
byte[] container = new byte[1024];
while (true) {
DatagramPacket packet = new DatagramPacket(container, 0, container.length);
//斷開連接
byte[] data = packet.getData();
String receiveData = new String(data, 0, data.length);
System.out.println(receiveData);
if (receiveData.equals("bye")) {
break;
}
socket.receive(packet);//阻塞式接收包裹
}
socket.close();
}
}
發送端:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class UDPSenderDemo01 {
public static void main(String[] args) throws Exception{
DatagramSocket socket = new DatagramSocket(8888);
//准備數據:控制台讀取 System.in
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String data = reader.readLine();
byte[] datas = data.getBytes();
DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("localhost", 6666)); //有send就需要一個DatagramPacket
socket.send(packet);
if (data.equals("bye")) {
break;
}
}
socket.close();
}
}
在線咨詢:兩個人都可以是發送方,也都可以是接收方。
TalkSend:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class TalkSend implements Runnable{
DatagramSocket socket = null;
BufferedReader reader = null;
private int fromPort;
private String toIP;
private int toPort;
public TalkSend(int fromPort, String toIP, int toPort) {
this.fromPort = fromPort;
this.toIP = toIP;
this.toPort = toPort;
try {
socket = new DatagramSocket(fromPort);
reader = new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true) {
try {
String data = reader.readLine();
byte[] datas = data.getBytes();
DatagramPacket packet = new DatagramPacket(datas,0,datas.length,new InetSocketAddress(this.toIP,this.toPort));
socket.send(packet);
if (data.equals("bye")) {
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
socket.close();
}
}
TalkReceive:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class TalkReceive implements Runnable{
DatagramSocket socket = null;
private int port;
private String msgFrom;
public TalkReceive(int port,String msgFrom) {
this.port = port;
this.msgFrom = msgFrom;
try {
socket = new DatagramSocket(port);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true) {
try {
//准備接收包裹
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container,0,container.length);
socket.receive(packet); //阻塞式接收包裹
//斷開連接 bye
byte[] data = packet.getData();
String receiveData = new String(data, 0, data.length);
System.out.println(msgFrom + ":" + receiveData);
if (receiveData.equals("bye")) {
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
TalkStudent:
public class TalkStudent {
public static void main(String[] args) {
//開啟兩個線程
new Thread(new TalkSend(7777,"localhost",9999)).start();
new Thread(new TalkReceive(8888,"老師")).start();
}
}
TalkTeacher:
public class TalkTeacher {
public static void main(String[] args) {
new Thread(new TalkSend(5555,"localhost",8888)).start();
new Thread(new TalkReceive(9999,"學生")).start();
}
}
1.8URL
URL:統一資源定位符:定位資源的,定位互聯網上的某一個資源
DNS域名解析:把一個域名變成一個IP
協議://IP地址:端口/項目名/資源
url下載文件:
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class URLDown {
public static void main(String[] args) throws Exception{
//1.下載地址
URL url = new URL("http://localhost:8080/naraku/SecurityFile.txt");
//2.連接到這個資源
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
FileOutputStream fos = new FileOutputStream("SecurityFile.txt");
byte[] buffer = new byte[1024];
int len;
while ((len=inputStream.read(buffer))!=-1){
fos.write(buffer,0,len); //寫出這個數據
}
fos.close();
inputStream.close();
urlConnection.disconnect();
}
}
