網(wǎng)絡(luò)編程概述
就是用來實現(xiàn)網(wǎng)絡(luò)互連的不同計算機上運行的程序間可以進行數(shù)據(jù)交換。
有人說,20世紀最偉大的發(fā)明不是計算機,而是計算機網(wǎng)絡(luò)。哈哈,的確是這樣,現(xiàn)在沒有網(wǎng),簡直不能活下去。
計算機之間要進行通訊的話要以什么樣的規(guī)則進行通信呢,這就是網(wǎng)絡(luò)模型研究的問題,我們下來學(xué)習(xí)。
網(wǎng)絡(luò)模型
- OSI參考模型
- TCP/IP參考模型
OSI參考模型
應(yīng)用層
表示層
會話層
傳輸層
網(wǎng)絡(luò)層
數(shù)據(jù)鏈路層
物理層
TCP/IP參考模型
應(yīng)用層
傳輸層
網(wǎng)絡(luò)層
主機至網(wǎng)絡(luò)層
網(wǎng)絡(luò)通信三要素
- IP地址:InetAddress
- 網(wǎng)絡(luò)中設(shè)備的標識,不易記憶,可用主機名
- 端口號
- 用于標識進程的邏輯地址,不同進程的標識
- 傳輸協(xié)議
- 通訊的規(guī)則
常見協(xié)議:TCP,UDP
- 通訊的規(guī)則
IP地址
說起IP地址,我們每個人都知道自己的電腦有個IP地址,它在計算機中有什么作用呢?我們就來看一下它的具體概念
要想讓網(wǎng)絡(luò)中的計算機能夠互相通信,必須為每臺計算機指定一個標識號,也就是網(wǎng)絡(luò)中計算機的唯一標識,通過這個標識號來指定要接受數(shù)據(jù)的計算機和識別發(fā)送的計算機,在TCP/IP協(xié)議中,這個標識號就是IP地址。
那么,我們?nèi)绾潍@取和操作IP地址呢?java提供了一個類**InetAddress **供我們使用對IP地址的獲取和操作。
我們來獲取本機的主機名和IP地址
public class InetAddressDemo {
public static void main(String[] args) {
try {
InetAddress in = InetAddress.getLocalHost(); System.out.println(in.toString());
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
運行程序,我們就得到了本機的主機名和IP地址
那么我們想要知道別人的IP地址或主機名怎么辦呢?當然也是有方法的
public class InetAddressDemo {
public static void main(String[] args) throws UnknownHostException { InetAddress address = InetAddress.getByName("192.168.2.102");
// public String getHostName()獲取主機名,
String name = address.getHostName();
// public String getHostAddress()獲取IP地址
String ip = address.getHostAddress();
System.out.println(name + "---" + ip);
}
}
如果你和別人在一個局域網(wǎng)內(nèi),知道了他的主機名或者IP地址就可以獲取到另外一個。
端口號
端口號它分為兩種,一種是物理端口,另外一種是邏輯端口。
物理端口就是網(wǎng)卡口,我們要學(xué)的呢就是邏輯端口,它是什么呢?
- 每個網(wǎng)絡(luò)程序都會至少有一個邏輯端口
- 用于標識進程的邏輯地址,不同進程的標識
- 有效端口:065535,其中01024系統(tǒng)使用或保留端口。
TCP和UDP協(xié)議
TCP和UDP協(xié)議它們是通訊的規(guī)則,它們有什么區(qū)別呢?
TCP
建立連接,形成傳輸數(shù)據(jù)的通道
在連接中進行大數(shù)據(jù)量傳輸
通過三次握手完成連接,是可靠協(xié)議
必須建立連接,效率會稍低
UDP
將數(shù)據(jù)源和目的封裝成數(shù)據(jù)包中,不需要建立連接
每個數(shù)據(jù)包的大小在限制在64k
因無連接,是不可靠協(xié)議
不需要建立連接,速度快
一般的軟件它既有UDP,又有TCP,用TCP來保證軟件的可靠性,用UDP來保證軟件的傳輸速度快。
Socket
我們繼續(xù)看Socket,我們說的網(wǎng)絡(luò)編程也就是Socket編程也叫做網(wǎng)絡(luò)套接字。
- Socket套接字:
- 網(wǎng)絡(luò)上具有唯一標識的IP地址和端口號組合在一起才能構(gòu)成唯一能識別的標識符套接字。
- Socket原理機制:
- 通信的兩端都有Socket。
- 網(wǎng)絡(luò)通信其實就是Socket間的通信。
- 數(shù)據(jù)在兩個Socket間通過IO傳輸。
UDP傳輸
我們要用UDP傳輸數(shù)據(jù)時,怎么用Socket建立連接呢?
- DatagramSocket與DatagramPacket
- 建立發(fā)送端,接收端。
- 建立數(shù)據(jù)包。
- 調(diào)用Socket的發(fā)送接收方法。
- 關(guān)閉Socket。
發(fā)送端與接收端是兩個獨立的運行程序。我們用代碼實現(xiàn)一個UDP傳輸數(shù)據(jù)的例子
UDP傳輸-發(fā)送端
/*
* 需求:接收指定端口發(fā)送過來的數(shù)據(jù)
*
* UDP協(xié)議發(fā)送數(shù)據(jù):
* A:創(chuàng)建發(fā)送端Socket對象
* B:創(chuàng)建數(shù)據(jù),并把數(shù)據(jù)打包
* C:調(diào)用Socket對象的發(fā)送方法發(fā)送數(shù)據(jù)包
* D:釋放資源
*/
public class SendDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建發(fā)送端Socket對象
// DatagramSocket()
DatagramSocket ds = new DatagramSocket();
// 創(chuàng)建數(shù)據(jù),并把數(shù)據(jù)打包
// DatagramPacket(byte[] buf, int length, InetAddress address, int port)
// 創(chuàng)建數(shù)據(jù)
byte[] bys = "UDP過來了".getBytes();
// 長度
int length = bys.length;
// IP地址對象
InetAddress address = InetAddress.getByName("192.168.2.102");
// 端口
int port = 12345;
DatagramPacket dp = new DatagramPacket(bys, length, address, port);
// 調(diào)用Socket對象的發(fā)送方法發(fā)送數(shù)據(jù)包
// public void send(DatagramPacket p)
ds.send(dp);
// 釋放資源
ds.close();
}
}
UDP傳輸-接收端
/* * UDP協(xié)議接收數(shù)據(jù):
* A:創(chuàng)建接收端Socket對象
* B:創(chuàng)建一個數(shù)據(jù)包(接收容器)
* C:調(diào)用Socket對象的接收方法接收數(shù)據(jù)
* D:解析數(shù)據(jù)包,并顯示在控制臺
* E:釋放資源
*/
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建接收端Socket對象
// DatagramSocket(int port)
DatagramSocket ds = new DatagramSocket(12345);
// 創(chuàng)建一個數(shù)據(jù)包(接收容器)
// DatagramPacket(byte[] buf, int length)
byte[] bys = new byte[1024];
int length = bys.length;
DatagramPacket dp = new DatagramPacket(bys, length);
// 調(diào)用Socket對象的接收方法接收數(shù)據(jù)
// public void receive(DatagramPacket p)
ds.receive(dp); // 阻塞式
// 解析數(shù)據(jù)包,并顯示在控制臺
// 獲取對方的ip
// public InetAddress getAddress()
InetAddress address = dp.getAddress();
String ip = address.getHostAddress();
// public byte[] getData():獲取數(shù)據(jù)緩沖區(qū)
// public int getLength():獲取數(shù)據(jù)的實際長度
byte[] bys2 = dp.getData();
int len = dp.getLength();
String s = new String(bys2, 0, len);
System.out.println(ip + "傳遞的數(shù)據(jù)是:" + s);
// 釋放資源
ds.close();
}
}
我們先運行接收端的代碼,再運行發(fā)送端的代碼,這樣在接收端就會收到由發(fā)送端發(fā)過來的數(shù)據(jù)。完成了UDP傳輸
多線程UDP聊天
/*
* 通過多線程實現(xiàn)聊天程序,我們就要開啟兩個線程,一個接收數(shù)據(jù),一個發(fā)送數(shù)據(jù),這樣我就可以實現(xiàn)在一個窗口發(fā)送和接收數(shù)據(jù)了
*/
public class ChatRoom {
public static void main(String[] args) throws IOException { DatagramSocket dsSend = new DatagramSocket();
DatagramSocket dsReceive = new DatagramSocket(12345); SendThread st = new SendThread(dsSend);
ReceiveThread rt = new ReceiveThread(dsReceive);
Thread t1 = new Thread(st);
Thread t2 = new Thread(rt);
t1.start(); t2.start();
}
}
/* * 接收數(shù)據(jù) */
public class ReceiveThread implements Runnable {
private DatagramSocket ds;
public ReceiveThread(DatagramSocket ds) {
this.ds = ds;
}
@Override
public void run() {
try { while (true) {
// 創(chuàng)建一個包裹
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
// 接收數(shù)據(jù) ds.receive(dp);
// 解析數(shù)據(jù)
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();
}
}
}
/* * 發(fā)送數(shù)據(jù) */
public class SendThread implements Runnable {
private DatagramSocket ds;
public SendThread(DatagramSocket ds) {
this.ds = ds;
}
@Override
public void run() {
try {
// 封裝鍵盤錄入數(shù)據(jù)
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while ((line = br.readLine()) != null) {
if ("886".equals(line)) {
break;
}
// 創(chuàng)建數(shù)據(jù)并打包
byte[] bys = line.getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length,InetAddress.getByName("192.168.2.102"), 12345);
// 發(fā)送數(shù)據(jù)
ds.send(dp);
}
// 釋放資源
ds.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
我們只需要運行聊天室ChatRoom類,就可以完成單窗口聊天了這里寫圖片描述你發(fā)送一句,他就會接收一句,是不是很有意思呢?