更詳細網(wǎng)絡知識請點擊這里
兩臺計算機進行網(wǎng)絡通信,必須滿足一下三點
- IP 地址:兩臺主機必須有唯一的標識
- 協(xié)議:雙方必須擁有共同的語言,否則出現(xiàn)言語不通,無法交流。
- 端口號:一臺主機上可以運行多個應用程序蕉世,如何辨別不同應用程序的通信,就需要端口號來區(qū)分
TCP/IP 協(xié)議
TCP/IP 協(xié)議是目前引用最為廣泛的協(xié)議婆硬,是以 TCP 和 IP 為基礎的不同層次上多個協(xié)議的集合狠轻,也稱 TCP/IP 協(xié)議族或協(xié)議棧
- TCP:Transmission Control Protocol 傳輸控制協(xié)議
- IP: Internet Protocol 互聯(lián)網(wǎng)協(xié)議
TCP/IP 模型
物理層——>鏈路層——>網(wǎng)絡層——>傳輸層——>引用層
- 物理層是用戶最直觀接觸到的,比如網(wǎng)線彬犯、雙絞線向楼、網(wǎng)卡等
- TCP/IP 協(xié)議實際上是在第4層傳輸層
- 應用層有許多協(xié)議是大家熟知的:HTTP 超文本傳輸協(xié)議、FTP 文件傳輸協(xié)議(可進行文件上傳谐区、下載等傳輸)湖蜕、SMTP 簡單郵件傳輸協(xié)議、Trlnet 遠程登錄服務
IP 地址
為實現(xiàn)網(wǎng)絡中不同計算機之間的通信宋列,每臺計算機都必須有一個唯一的標識---IP地址(類似生活中手機通信昭抒,雙方就必須擁有各自的手機號才能通信)
- IP 地址格式:數(shù)字型,如 192.168.0.1(類似手機號碼規(guī)定了11位)
端口
一臺主機可以使用多個應用程序炼杖,當某個應用與另一臺計算機通信時灭返,如何保證發(fā)送的消息被對面正確接收(比如說A計算機中QQ發(fā)送消息到B計算機QQ中,而不是B計算機的微信中)坤邪,使用端口就能保證熙含,每個應用都有各自的唯一端口號。
- 用于區(qū)分不同應用程序
- 端口號范圍為065535艇纺,其中01023為系統(tǒng)所保留
- IP 地址和端口號組成了所謂的 Socket怎静,Socket是網(wǎng)絡上運行的程序之間雙向通信鏈路的終結點,是 TCP 和 UDP 的基礎
- 常見默認協(xié)議端口黔衡,http:20消约,ftp:21,telnet:23
JAVA 中的網(wǎng)絡支持
針對網(wǎng)絡通信的不同層次员帮,Java 提供的網(wǎng)絡功能有四大類
- InetAddress:用于標識網(wǎng)絡上的硬件資源,即標識 IP 地址
- URL:統(tǒng)一資源定位符导饲,表示Internet 上某一資源的地址捞高,通過 URL 可以直接讀取或寫入網(wǎng)絡上的數(shù)據(jù)
- Socket:使用 TCP 協(xié)議實現(xiàn)網(wǎng)絡通信的 Socket 相關的類
- Datagram:使用 UDP 協(xié)議氯材,將數(shù)據(jù)保存在數(shù)據(jù)報中,通過網(wǎng)絡進行通信
InetAddress 類常用方法的使用
package com.fy.inetaddress;
import java.lang.reflect.Array;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
public class Demo1 {
public static void main(String[] args) {
try {
//獲取本機的InetAddress實例
InetAddress inetAddress = InetAddress.getLocalHost();
System.out.println("計算機地址:"+inetAddress.getHostAddress());
System.out.println("計算機名:"+inetAddress.getHostName());
byte[] bytes = inetAddress.getAddress();//直接獲取字節(jié)數(shù)組形式的IP地址
System.out.println("字節(jié)數(shù)組形式的IP"+Arrays.toString(bytes));
System.out.println(inetAddress);//直接輸出InetAddtress實例
System.out.println("---------------");
//根據(jù)電腦名稱獲取InetAddress實例
InetAddress inetAddress2 = InetAddress.getByName("DESKTOP-FCI9M7Q");
System.out.println("計算機地址:"+inetAddress2.getHostAddress());
System.out.println("計算機名:"+inetAddress2.getHostName());
System.out.println("---------------");
//根據(jù)電腦IP地址也能獲取實例
InetAddress inetAddress3 = InetAddress.getByName("192.168.56.1");
System.out.println("計算機地址:"+inetAddress2.getHostAddress());
System.out.println("計算機名:"+inetAddress2.getHostName());
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
URL 類常用方法的使用
package com.fy.url;
import java.net.MalformedURLException;
import java.net.URL;
public class URLDemo {
public static void main(String[] args) {
try {
//創(chuàng)建一個URL實例
URL imooc = new URL("http://www.baidu.com");
//通過一個存在的URL實例創(chuàng)建一個新的實例硝岗?表示后面的參數(shù)氢哮,#表示錨點
URL url = new URL(imooc, "/index.html?username=tom#test");
//獲取協(xié)議
System.out.println(url.getProtocol());
System.out.println(url.getHost());
//獲取端口號,如果端口號未指定即使用的是默認端口號型檀,此時返回-1
System.out.println("查詢端口:"+url.getPort());
System.out.println("查詢文件路徑:"+url.getPath());
System.out.println("查詢文件名:"+url.getFile());
System.out.println("查詢相對路徑:"+url.getRef());
System.out.println("查詢字符串:"+url.getQuery());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
package com.fy.url;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
public class URLDemo2 {
public static void main(String[] args) {
try {
URL url = new URL("http://www.baidu.com");
//通過URL的openStream 方法獲取URL對象所表示的資源的字節(jié)輸入流
InputStream is = url.openStream();
//轉化為字符輸入流
InputStreamReader isr = new InputStreamReader(is,"utf-8");
//為字符輸入流添加緩沖
BufferedReader br = new BufferedReader(isr);
String data = br.readLine();//讀取數(shù)據(jù)
while (data != null) {//循環(huán)讀取數(shù)據(jù)
System.out.print(data);//輸出數(shù)據(jù)
data = br.readLine();
}
br.close();
isr.close();
is.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Tcp SocKet 通信
TCP 協(xié)議是面向連接冗尤、可靠、有序的胀溺,以字節(jié)流的方式發(fā)送數(shù)據(jù)基于 TCP 協(xié)議實現(xiàn)網(wǎng)絡通信的類:
- 客戶端的 Socket 類
- 服務器端的 ServerSocket 類
Tcp Socket 通信實現(xiàn)步驟
- 創(chuàng)建 ServerSocket 和 Socket
- 打開連接到 Socket 的輸入/輸出流
- 按照協(xié)議對 Socket 進行讀/寫操作
- 關閉輸入輸出流裂七、關閉 Socket
服務器端
- 創(chuàng)建 ServerSocket 對象,綁定監(jiān)聽端口
- 通過 accept() 方法監(jiān)聽客戶端的請求
- 建立連接后仓坞,通過輸入流獲取客戶端的請求數(shù)據(jù)
- 通過輸出流向客戶端發(fā)送相應消息
- 關閉所有資源
package com.fy.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/*
* 基于 TCP 協(xié)議的 Socket 通信背零,實現(xiàn)用戶登錄
* 服務器端
*/
public class TcpServer {
public static void main(String[] args) {
try {
// 1. 創(chuàng)建一個服務器端的ServerSocket,指定綁定端口并且監(jiān)聽
ServerSocket serverSocket = new ServerSocket(6666);
System.out.println("---服務器即將啟動,等待客戶端連接---");
// 2. 調用 accept()方法監(jiān)聽客戶端請求
Socket socket = serverSocket.accept();
// 3. 利用輸入流讀取客戶端信息
InputStream is = socket.getInputStream();//字節(jié)輸入流
InputStreamReader isr = new InputStreamReader(is);//將字節(jié)流轉化為字符流
BufferedReader br = new BufferedReader(isr);//為輸入字符流創(chuàng)建緩沖
String info = null;
while((info=br.readLine())!=null){//循環(huán)讀取客戶端的信息
System.out.println("我是服務器无埃,客戶端說"+info);
}
socket.shutdownInput();// 關閉輸入流
// 4.創(chuàng)建輸出流徙瓶,響應客戶端的請求
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);//包裝為打印流(與輸出字符流類似)
pw.write("服務器端說,歡迎你嫉称!");
pw.flush();//調用 flush()方法刷新緩存
// 5.關閉所有資源
pw.close();
os.close();
br.close();
isr.close();
is.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客戶端
- 創(chuàng)建 Socket 對象侦镇,指明需要連接的服務器地址和端口號
- 連接建立后,通過輸出流向服務器發(fā)出需求
- 通過輸入流接收來自服務器的相應
- 關閉所有資源
package com.fy.socket;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
/*
* 基于 TCP 協(xié)議的 Socket 通信织阅,實現(xiàn)用戶登錄
* 客戶端
*/
public class TcpClient {
public static void main(String[] args) {
try {
// 1.創(chuàng)建一個Socket壳繁,指定服務器地址和端口
Socket socket = new Socket("localhost", 6666);
// 2.創(chuàng)建一個輸出流向服務器發(fā)送消息
OutputStream os = socket.getOutputStream();// 創(chuàng)建輸出字節(jié)流
OutputStreamWriter osw = new OutputStreamWriter(os);//轉化為字符流
BufferedWriter bw = new BufferedWriter(osw);//創(chuàng)建緩沖流
bw.write("用戶名:admin;密碼:123");
bw.flush();
socket.shutdownOutput();// 關閉輸出流
// 3. 創(chuàng)建輸入流蒲稳,接受服務器響應
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String info = null;
while ((info = br.readLine()) != null) {
System.out.println("我是客戶端:"+info);
}
// 4.關閉所有資源
br.close();
isr.close();
is.close();
bw.close();
os.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
利用多線程實現(xiàn)多客戶端的通信
- 服務器端穿件 ServerSocket氮趋,循環(huán)調用 accept() 方法等待客戶端連接
- 客戶端創(chuàng)建一個 Socket 并請求和服務器端連接
- 服務器端接受客戶端請求后,創(chuàng)建 socket 與該客戶建立專線連接
- 建立連接的兩個 socket 在一個單獨的線程進行通信
- 服務器端繼續(xù)等待新的連接
線程代碼
package com.fy.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
/*
* 服務器線程處理類
*/
public class TcpServerThread extends Thread {
// 和本線程相關的Socket
Socket socket = null;
// 構造方法初始化socket
public ServerThread(Socket socket) {
this.socket = socket;
}
// 線程操作執(zhí)行響應客戶端請求
public void run() {
InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
OutputStream os = null;
PrintWriter pw = null;
try {
// 創(chuàng)建輸入流江耀,響應客戶端的請求
is = socket.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
String info = null;
while ((info = br.readLine()) != null) {// 循環(huán)讀取客戶端的信息
System.out.println("我是服務器剩胁,客戶端說" + info);
}
socket.shutdownInput();// 關閉輸入流
os = socket.getOutputStream();
pw = new PrintWriter(os);
pw.write("服務器端說,歡迎你祥国!");
pw.flush();// 調用 flush()方法刷新緩存
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 關閉所有資源
if (pw != null) {
pw.close();
}
if (os != null) {
os.close();
}
if (br != null) {
br.close();
}
if (isr != null) {
isr.close();
}
if (is != null) {
is.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
同時將服務器該為如下代碼
package com.fy.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
/*
* 基于 TCP 協(xié)議的 Socket 通信昵观,實現(xiàn)用戶登錄
* 服務器端
*/
public class TcpServer {
public static void main(String[] args) {
try {
// 創(chuàng)建一個服務器端的ServerSocket,指定綁定端口并且監(jiān)聽
ServerSocket serverSocket = new ServerSocket(6666);
Socket socket = null;
int count = 0;
System.out.println("---服務器即將啟動,等待客戶端連接---");
// 利用while循環(huán)監(jiān)聽
while(true){
// 調用 accept()方法監(jiān)聽,等待客戶端請求
socket = serverSocket.accept();
// 創(chuàng)建一個新的線程
TcoServerThread sThread = new TcpServerThread(socket);
// 啟動一個線程
sThread.start();
count++;// 統(tǒng)計客戶端的數(shù)量
System.out.println("客戶端的數(shù)量:"+count);
//創(chuàng)建InetAdress對象舌稀,以獲取客戶端IP地址
InetAddress address = socket.getInetAddress();
System.out.println("當前客戶端的Ip地址:"+ address.getHostAddress());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}