網絡模型
一般是指1 . 物理層
:主要定義物理設備標准,如網線的接口類型、光纖的接口類型、各種傳輸介質的傳輸速率等。它的主要作用是傳輸比特流。這一層的數據叫做比特。
2 . 數據鏈路層
:主要將從物理層接收的數據進行MAC地址(網卡的地址)的封裝
與解封裝
。常把這一層的數據叫做幀
。在這一層工作的設備是交換機,數據通過交換機來傳輸。
3 . 網絡層
:主要將從下層接收到的數據進行IP地址的封裝
與解封裝
。在這一層工作的設備是路由器,常把這一層的數據叫做數據包
。
4 . 傳輸層
:定義了一些傳輸數據的協議和端口號。 主要是將從下層接收的數據進行分段和傳輸,到達目的地址後再進行重組。常常把這一層數據叫做段
。
TCP
:傳輸控制協議,傳輸效率低,可靠性強,用於傳輸可靠性要求高,數據量大的數據。 UDP
:用戶數據包協議,與TCP特性恰恰相反,用於傳輸可靠性要求不高,數據量小的數據。5 . 會話層
:通過傳輸層建立數據傳輸的通路。主要在你的系統之間發起會話或者接受會話請求(設備之間需要互相認識可以是IP也可以是MAC或者是主機名)。
6 . 表示層
:主要是進行對接收的數據進行解釋、加密與解密、壓縮與解壓縮等(把計算機能夠識別的東西轉換成人能夠能識別的東西(如圖片、聲音等)。
7 . 應用層
: 主要是一些終端的應用,比如說FTP(各種文件下載),WEB(IE浏覽),QQ之類的(可以把它理解成我們在電腦屏幕上可以看到的東西.就是終端應用)。
要想讓網絡中的計算機能夠互相通信,必須為每台計算機指定一個標識號,通過這個標識號來指定要接受數據的計算機和識別發送的計算機,在TCP/IP協議中,這個標識號就是IP地址。
如何獲取和操作IP地址呢?
為了方便我們對IP地址的獲取和操作,java提供了一個類InetAddress
。
所謂IP地址就是給每個連接在Internet上的主機分配的一個32bit地址。按照TCP/IP規定,IP地址用二進制來表示,每個IP地址長32bit,比特換算成字節,就是4個字節。IP地址的這種表示法叫做“點分十進制表示法
”,這顯然比1和0容易記憶得多。
IP地址 = 網絡號碼
+主機地址
A類IP地址
: 第一段號碼為網絡號碼
,剩下的三段號碼為本地計算機的號碼
B類IP地址
:前二段號碼為網絡號碼
,剩下的二段號碼為本地計算機的號碼
C類IP地址
:前三段號碼為網絡號碼
,剩下的一段號碼為本地計算機的號碼
特殊地址:
127.0.0.1回環地址
,可用於測試本機的網絡是否有問題。
網絡編程
,套接字編程
Socket套接字:IP地址
和端口號
組合在一起才能構成唯一能識別的標識符套接字。 Socket原理機制:
DatagramSocket
與DatagramPacket
建立發送端,接收端。 建立數據包。 調用Socket的發送接收方法。 關閉Socket。 發送端與接收端是兩個獨立的運行程序。
4:關閉資源
UDP傳輸-發送端代碼
/*
* UDP協議發送數據:
* A:創建發送端Socket對象
* B:創建數據,並把數據打包
* C:調用Socket對象的發送方法發送數據包
* D:釋放資源
*/
public class SendDemo {
public static void main(String[] args) throws IOException {
// 創建發送端Socket對象
// DatagramSocket()
DatagramSocket ds = new DatagramSocket();
// 創建數據,並把數據打包
// DatagramPacket(byte[] buf, int length, InetAddress address, int port)
// 創建數據
byte[] bys = "hello,udp,我來了".getBytes();
// 長度
int length = bys.length;
// IP地址對象
InetAddress address = InetAddress.getByName("192.168.12.92");
// 端口
int port = 10086;
DatagramPacket dp = new DatagramPacket(bys, length, address, port);
// 調用Socket對象的發送方法發送數據包
// public void send(DatagramPacket p)
ds.send(dp);
// 釋放資源
ds.close();
}
}
5:可以對資源進行關閉
UDP傳輸-接收端代碼
/*
* UDP協議接收數據:
* A:創建接收端Socket對象
* B:創建一個數據包(接收容器)
* C:調用Socket對象的接收方法接收數據
* D:解析數據包,並顯示在控制台
* E:釋放資源
*/
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
// 創建接收端Socket對象
// DatagramSocket(int port)
DatagramSocket ds = new DatagramSocket(10086);
// 創建一個數據包(接收容器)
// DatagramPacket(byte[] buf, int length)
byte[] bys = new byte[1024];
int length = bys.length;
DatagramPacket dp = new DatagramPacket(bys, length);
// 調用Socket對象的接收方法接收數據
// public void receive(DatagramPacket p)
ds.receive(dp); // 阻塞式
// 解析數據包,並顯示在控制台
// 獲取對方的ip
// public InetAddress getAddress()
InetAddress address = dp.getAddress();
String ip = address.getHostAddress();
// public byte[] getData():獲取數據緩沖區
// public int getLength():獲取數據的實際長度
byte[] bys2 = dp.getData();
int len = dp.getLength();
String s = new String(bys2, 0, len);
System.out.println(ip + "傳遞的數據是:" + s);
// 釋放資源
ds.close();
}
}
優化後代碼
/*
* 多次啟動接收端:
* java.net.BindException: Address already in use: Cannot bind
* 端口被占用。
*/
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
// 創建接收端的Socket對象
DatagramSocket ds = new DatagramSocket(12345);
// 創建一個包裹
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
// 接收數據
ds.receive(dp);
// 解析數據
String ip = dp.getAddress().getHostAddress();
String s = new String(dp.getData(), 0, dp.getLength());
System.out.println("from " + ip + " data is : " + s);
// 釋放資源
ds.close();
}
}
public class SendDemo {
public static void main(String[] args) throws IOException {
// 創建發送端的Socket對象
DatagramSocket ds = new DatagramSocket();
// 創建數據並打包
byte[] bys = "helloworld".getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length,
InetAddress.getByName("192.168.12.92"), 12345);
// 發送數據
ds.send(dp);
// 釋放資源
ds.close();
}
}
/*
* 多次啟動接收端:
* java.net.BindException: Address already in use: Cannot bind
* 端口被占用。
*/
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
// 創建接收端的Socket對象
DatagramSocket ds = new DatagramSocket(12345);
while (true) {
// 創建一個包裹
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
// 接收數據
ds.receive(dp);
// 解析數據
String ip = dp.getAddress().getHostAddress();
String s = new String(dp.getData(), 0, dp.getLength());
System.out.println("from " + ip + " data is : " + s);
}
// 釋放資源
// 接收端應該一直開著等待接收數據,是不需要關閉
// ds.close();
}
}
/*
* 數據來自於鍵盤錄入
* 鍵盤錄入數據要自己控制錄入結束。
*/
public class SendDemo {
public static void main(String[] args) throws IOException {
// 創建發送端的Socket對象
DatagramSocket ds = new DatagramSocket();
// 封裝鍵盤錄入數據
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while ((line = br.readLine()) != null) {
if ("886".equals(line)) {
break;
}
// 創建數據並打包
byte[] bys = line.getBytes();
// DatagramPacket dp = new DatagramPacket(bys, bys.length,
// InetAddress.getByName("192.168.12.92"), 12345);
DatagramPacket dp = new DatagramPacket(bys, bys.length,InetAddress.getByName("192.168.12.255"), 12345);
// 發送數據
ds.send(dp);
}
// 釋放資源
ds.close();
}
}
把剛才發送和接收程序分別用線程進行封裝,完成一個UDP的聊天程序。
/*
* 通過多線程改進剛才的聊天程序,這樣我就可以實現在一個窗口發送和接收數據了
*/
public class ChatRoom {
public static void main(String[] args) throws IOException {
DatagramSocket dsSend = new DatagramSocket();
DatagramSocket dsReceive = new DatagramSocket(12306);
SendThread st = new SendThread(dsSend);
ReceiveThread rt = new ReceiveThread(dsReceive);
Thread t1 = new Thread(st);
Thread t2 = new Thread(rt);
t1.start();
t2.start();
}
}
public class ReceiveThread implements Runnable {
private DatagramSocket ds;
public ReceiveThread(DatagramSocket ds) {
this.ds = ds;
}
@Override
public void run() {
try {
while (true) {
// 創建一個包裹
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
// 接收數據
ds.receive(dp);
// 解析數據
String ip = dp.getAddress().getHostAddress();
String s = new String(dp.getData(), 0, dp.getLength());
System.out.println("from " + ip + " data is : " + s);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class SendThread implements Runnable {
private DatagramSocket ds;
public SendThread(DatagramSocket ds) {
this.ds = ds;
}
@Override
public void run() {
try {
// 封裝鍵盤錄入數據
BufferedReader br = new BufferedReader(new InputStreamReader(
System.in));
String line = null;
while ((line = br.readLine()) != null) {
if ("886".equals(line)) {
break;
}
// 創建數據並打包
byte[] bys = line.getBytes();
// DatagramPacket dp = new DatagramPacket(bys, bys.length,
// InetAddress.getByName("192.168.12.92"), 12345);
DatagramPacket dp = new DatagramPacket(bys, bys.length,
InetAddress.getByName("192.168.12.255"), 12306);
// 發送數據
ds.send(dp);
}
// 釋放資源
ds.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Socket流
,Socket流中既有讀取流,也有寫入流。 3:通過Socket對象的方法,可以獲取這兩個流。 4:通過流的對象可以對數據進行傳輸
5:如果傳輸數據完畢,關閉資源
TCP傳輸-客戶端代碼
/*
* 連接被拒絕。TCP協議一定要先開服務器。
* java.net.ConnectException: Connection refused: connect
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 創建發送端的Socket對象
// Socket(InetAddress address, int port)
// Socket(String host, int port)
// Socket s = new Socket(InetAddress.getByName("192.168.12.92"), 8888);
Socket s = new Socket("192.168.12.92", 8888);
// 獲取輸出流,寫數據
// public OutputStream getOutputStream()
OutputStream os = s.getOutputStream();
os.write("hello,tcp,我來了".getBytes());
// 釋放資源
s.close();
}
}
socket服務
,需要一個端口
2:服務端沒有直接流的操作,而是通過accept方法
獲取客戶端對象,再通過獲取到的客戶端對象的流和客戶端進行通信。 3:通過客戶端的獲取流對象的方法,讀取數據或者寫入數據
4:如果服務完成,需要關閉客戶端,然後關閉服務器,但是,一般會關閉客戶端,不會關閉服務器,因為服務端是一直提供服務的。
TCP傳輸-服務器端代碼
/*
* TCP協議接收數據:
* A:創建接收端的Socket對象
* B:監聽客戶端連接。返回一個對應的Socket對象
* C:獲取輸入流,讀取數據顯示在控制台
* D:釋放資源
*/
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 創建接收端的Socket對象
// ServerSocket(int port)
ServerSocket ss = new ServerSocket(8888);
// 監聽客戶端連接。返回一個對應的Socket對象
// public Socket accept()
Socket s = ss.accept(); // 偵聽並接受到此套接字的連接。此方法在連接傳入之前一直阻塞。
// 獲取輸入流,讀取數據顯示在控制台
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys); // 阻塞式方法
String str = new String(bys, 0, len);
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip + "---" + str);
// 釋放資源
s.close();
// ss.close(); //這個不應該關閉
}
}
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 創建服務器Socket對象
ServerSocket ss = new ServerSocket(9999);
// 監聽客戶端的連接
Socket s = ss.accept(); // 阻塞
// 獲取輸入流
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys); // 阻塞
String server = new String(bys, 0, len);
System.out.println("server:" + server);
// 獲取輸出流
OutputStream os = s.getOutputStream();
os.write("數據已經收到".getBytes());
// 釋放資源
s.close();
// ss.close();
}
}
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 創建客戶端Socket對象
Socket s = new Socket("192.168.12.92", 9999);
// 獲取輸出流
OutputStream os = s.getOutputStream();
os.write("今天天氣很好,適合睡覺".getBytes());
// 獲取輸入流
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys);// 阻塞
String client = new String(bys, 0, len);
System.out.println("client:" + client);
// 釋放資源
s.close();
}
}
客戶端鍵盤錄入,服務器輸出到控制台
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 創建服務器Socket對象
ServerSocket ss = new ServerSocket(22222);
// 監聽客戶端連接
Socket s = ss.accept();
// 包裝通道內容的流
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
// br.close();
s.close();
// ss.close();
}
}
/*
* 客戶端鍵盤錄入,服務器輸出到控制台
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 創建客戶端Socket對象
Socket s = new Socket("192.168.12.92", 22222);
// 鍵盤錄入數據
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 把通道內的流給包裝一下
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
String line = null;
while ((line = br.readLine()) != null) {
// 鍵盤錄入數據要自定義結束標記
if ("886".equals(line)) {
break;
}
bw.write(line);
bw.newLine();
bw.flush();
}
// 釋放資源
// bw.close();
// br.close();
s.close();
}
}
客戶端鍵盤錄入,服務器輸出文本文件
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 創建服務器Socket對象
ServerSocket ss = new ServerSocket(23456);
// 監聽客戶端連接
Socket s = ss.accept();
// 封裝通道內的數據
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
// 封裝文本文件
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
bw.close();
// br.close();
s.close();
// ss.close();
}
}
客戶端文本文件,服務器輸出到控制台
/*
* 客戶端文本文件,服務器輸出到控制台
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 創建Socket對象
Socket s = new Socket("192.168.12.92", 34567);
// 封裝文本文件
BufferedReader br = new BufferedReader(new FileReader(
"InetAddressDemo.java"));
// 封裝通道內的流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
br.close();
s.close();
}
}
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 創建服務器Socket對象
ServerSocket ss = new ServerSocket(34567);
// 監聽客戶端連接
Socket s = ss.accept();
// 封裝通道內的流
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
s.close();
}
}
/*
* 問題:按照我們正常的思路加入反饋信息,結果卻沒反應。為什麼呢?
* 讀取文本文件是可以以null作為結束信息的,但是呢,通道內是不能這樣結束信息的。
* 所以,服務器根本就不知道你結束了。而你還想服務器給你反饋。所以,就相互等待了。
*
* 如何解決呢?
* A:再多寫一條數據,告訴服務器,讀取到這條數據說明我就結束,你也結束吧。
* 這樣做可以解決問題,但是不好。
* B:Socket對象提供了一種解決方案
* public void shutdownOutput()
*/
public class UploadClient {
public static void main(String[] args) throws IOException {
// 創建客戶端Socket對象
Socket s = new Socket("192.168.12.92", 11111);
// 封裝文本文件
BufferedReader br = new BufferedReader(new FileReader(
"InetAddressDemo.java"));
// 封裝通道內流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
String line = null;
while ((line = br.readLine()) != null) { // 阻塞
bw.write(line);
bw.newLine();
bw.flush();
}
//Socket提供了一個終止,它會通知服務器你別等了,我沒有數據過來了
s.shutdownOutput();
// 接收反饋
BufferedReader brClient = new BufferedReader(new InputStreamReader(
s.getInputStream()));
String client = brClient.readLine(); // 阻塞
System.out.println(client);
// 釋放資源
br.close();
s.close();
}
}
public class UploadServer {
public static void main(String[] args) throws IOException {
// 創建服務器端的Socket對象
ServerSocket ss = new ServerSocket(11111);
// 監聽客戶端連接
Socket s = ss.accept();// 阻塞
// 封裝通道內的流
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
// 封裝文本文件
BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java"));
String line = null;
while ((line = br.readLine()) != null) { // 阻塞
// if("over".equals(line)){
// break;
// }
bw.write(line);
bw.newLine();
bw.flush();
}
// 給出反饋
BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
bwServer.write("文件上傳成功");
bwServer.newLine();
bwServer.flush();
// 釋放資源
bw.close();
s.close();
}
}
上傳圖片案例
public class UploadServer {
public static void main(String[] args) throws IOException {
// 創建服務器Socket對象
ServerSocket ss = new ServerSocket(19191);
// 監聽客戶端連接
Socket s = ss.accept();
// 封裝通道內流
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
// 封裝圖片文件
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("mn.jpg"));
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
bos.flush();
}
// 給一個反饋
OutputStream os = s.getOutputStream();
os.write("圖片上傳成功".getBytes());
bos.close();
s.close();
}
}
public static void main(String[] args) throws IOException {
// 創建客戶端Socket對象
Socket s = new Socket("192.168.12.92", 19191);
// 封裝圖片文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
"林青霞.jpg"));
// 封裝通道內的流
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
bos.flush();
}
s.shutdownOutput();
// 讀取反饋
InputStream is = s.getInputStream();
byte[] bys2 = new byte[1024];
int len2 = is.read(bys2);
String client = new String(bys2, 0, len2);
System.out.println(client);
// 釋放資源
bis.close();
s.close();
}
}
服務器的代碼用線程進行封裝,這樣可以模擬一個同時接收多人上傳文件的服務器。(用循環也可以但是效率低,是單線程的程序)
public class UploadClient {
public static void main(String[] args) throws IOException {
// 創建客戶端Socket對象
Socket s = new Socket("192.168.12.92", 11111);
// 封裝文本文件
// BufferedReader br = new BufferedReader(new FileReader(
// "InetAddressDemo.java"));
BufferedReader br = new BufferedReader(new FileReader(
"ReceiveDemo.java"));
// 封裝通道內流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
String line = null;
while ((line = br.readLine()) != null) { // 阻塞
bw.write(line);
bw.newLine();
bw.flush();
}
// Socket提供了一個終止,它會通知服務器你別等了,我沒有數據過來了
s.shutdownOutput();
// 接收反饋
BufferedReader brClient = new BufferedReader(new InputStreamReader(
s.getInputStream()));
String client = brClient.readLine(); // 阻塞
System.out.println(client);
// 釋放資源
br.close();
s.close();
}
}
public class UserThread implements Runnable {
private Socket s;
public UserThread(Socket s) {
this.s = s;
}
@Override
public void run() {
try {
// 封裝通道內的流
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
// 封裝文本文件
// BufferedWriter bw = new BufferedWriter(new
// FileWriter("Copy.java"));
// 為了防止名稱沖突
String newName = System.currentTimeMillis() + ".java";
BufferedWriter bw = new BufferedWriter(new FileWriter(newName));
String line = null;
while ((line = br.readLine()) != null) { // 阻塞
bw.write(line);
bw.newLine();
bw.flush();
}
// 給出反饋
BufferedWriter bwServer = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
bwServer.write("文件上傳成功");
bwServer.newLine();
bwServer.flush();
// 釋放資源
bw.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class UploadServer {
public static void main(String[] args) throws IOException {
// 創建服務器Socket對象
ServerSocket ss = new ServerSocket(11111);
while (true) {
Socket s = ss.accept();
new Thread(new UserThread(s)).start();
}
}
}