本章目錄
技巧:在windows作業系統的命令提示字元下,輸入ipconfig可以檢視本機ip地址
提問:
提醒:
前1024個埠已被系統佔用,使用時儘量避開常用埠
Java 提供了 InetAddress 類代表 IP 地址,InetAddress 下還有兩個子類 Inet4Address 和 Inet6Address,它們分別代表 IPv4 和 IPv6 地址 InetAddress 類沒有提供構造方法,而是提供了靜態方法來獲取 InetAddress 範例
方法名 | 作用 |
public static InetAddress getName(String host) | 根據主機獲取對應的 InetAddress 物件 |
Public static InetAddress getLocalHost() | 獲取本機 IP 地址所對應的 InetAddress 範例 |
public String getHostAddress() | 返回該 InetAddress 範例所對應的 IP 地址字串 |
public String getHostName() | 返回此 IP 地址的主機名稱 |
範例:根據主機名獲取對應的 InetAddress 物件
// 根據主機名獲取對應的 InetAddress 物件
InetAddress ip = InetAddress.getByName("www.sina.com");
// 獲取該 InetAddress 範例的 IP 地址字串
String ipStr = ip.getHostAddress();
System.out.println(" 新浪網地址 :" + ipStr);
// 獲取該 InetAddress 範例的主機名稱
String hostName = ip.getHostName();
System.out.println(" 新浪主機名 :" + hostName);
基於TCP協定伺服器端和使用者端建立連線步驟
在 Java 中使用的 ServerSocket 物件使用者監聽是來自使用者端的 Socket 類連線,如果沒有連線,它將一直處於等待狀態,ServerSocket 類包含一個來自使用者端的連線請求的監聽方法
ServerSocket 類的常用方法
方法名 | 作用 |
public ServerSocket(int port) | 構造方法,用指定埠建立 ServerSocket 範例 |
public Socket accept() | 接收使用者端的 Socket 類請求 |
public InetAddress getInetAddress() | 返回伺服器端主機 IP 地址 |
public void close() | 關閉 ServerSocket 物件 |
serverSocket範例呼叫accept方法後,就處於等待狀態
範例:使用ServerSocket建立伺服器端
// 建立 ServerScoket 範例,並在 8888 埠監聽使用者端
ServerSocket server = new ServerSocket(8888);
// 呼叫 accep() 方法等待使用者端的連線,該方法是一個阻塞方法,如果沒有使用者端請求服務,該方法下的程式碼將不會執行
System.out.println(" 伺服器端通訊端已經建立,開始等待來自使用者端的連線 ");
Socket socket = server.accept();
System.out.println(" 有使用者端已成功連線 ");
使用者端使用 Socket 類連線到指定的伺服器端,每個 Socket 物件代表一個使用者端,Socket 類的常用方法如下表
方法名 | 作用 |
public Socket(String host,int port) | public Socket(String host,int port) 構造方法,建立連線到指定遠端主機和埠的 Socket 範例 |
public InputStream getInputStream() | 返回該 Socket 物件對應的輸入流 |
public OutputStream getOutputStream() | 返回該 Socket 物件對應的輸出流 |
public void close() | 關閉 ServerSocket 物件 |
int port Ip和埠必須和伺服器端保持一致
範例:使用Socket建立使用者端和伺服器端連線
// 建立使用者端 Socket 範例,連線指定 IP 地址和指定埠的伺服器端
Socket socket = new Socket("127.0.0.1", 8888);
說明:
提問:
剛才實現的僅僅為兩臺裝置通過TCP方式建立連線,如何進行通訊?
在建立連線的基礎之上,可以通過前面學習的輸入流、輸出流進行資訊的傳送和接收,從Socket中可以獲取輸入流、輸出流
範例:使用輸入輸出流進行資訊傳送和接收
使用者端程式傳送資料
// 建立 ServerSocket 類,用於監聽使用者端 Socket 類的請求連線
ServerSocket server = new ServerSocket(8888);
// 等待使用者端的連線,使用者端連線後,與使用者端對應一個 Socket 管道
Socket socket = server.accept();
// 獲得 Socket 管道輸入的資料的位元組輸入流
OutputStream out = socket.getOutputStream();
// 使用列印流包裝位元組輸出流,更為方便輸出內容
PrintWriter writer = new PrintWriter(out);
writer.println(「 歡迎您的存取 」); //傳送資訊
writer.close();
server.close();
使用者端程式讀取資料
// 使用者端連線到本機埠號是 8888 的伺服器端
Socket socket = new Socket("127.0.0.1", 8888);
// 獲得 Socket 管道中獲取讀取資料的位元組輸入流
InputStream in = (InputStream) socket.getInputStream();
// 為了便於讀取資料,將 in 轉換成字元流
InputStreamReader isr = new InputStreamReader(in);
// 用 BufferdReader 包裝轉換後的字元流
BufferedReader reader = new BufferedReader(isr);
String data = reader.readLine();
System.out.println(" 伺服器端對使用者端說 :" + data);
reader.close();
socket.close();
使用 Socket 類通訊端,可以進行伺服器端和使用者端的通訊。Socket 類通訊主要分為單向通訊和多向通訊兩種
單向通訊就是指只有一端傳送資料,另一端只需接收資料,比如,伺服器端傳送資料到使用者端,使用者端不需要傳送資料到伺服器端
範例程式碼:
// 建立伺服器端通訊端,監聽 8888 介面
server = new ServerSocket(8888);
// 儲存使用者端傳送的資料
String data = null;
while (true) {
// 等待使用者端的連線,返回用來通訊的 Socket 物件
Socket socket = server.accept();
// 獲取使用者端的輸入流,用來讀取傳來的資料
InputStream in = socket.getInputStream();
// 將位元組流包裝成字元流
reader = new BufferedReader(new InputStreamReader(in));
// 判斷輸入流內的資料是否讀取完畢
while ((data = reader.readLine()) != null) {
System.out.println(" 來自使用者端的問候:" + data);}
break;
使用while(true)接收多個使用者端連線
第二個while迴圈讀取使用者端傳送的內容
範例程式碼:
// 建立使用者端通訊端,並連線到伺服器端
Socket client = new Socket("127.0.0.1", 8888);
// 建立一個可以向伺服器端傳送資料的輸出流物件
PrintWriter writer = new PrintWriter(client.getOutputStream());
// 向伺服器端寫入資料
writer.write("hello,server!");
// 清空輸出流快取
writer.flush();
這裡可以使用迴圈模擬不停傳送資料
雙向通訊是指伺服器端和使用者端都可以傳送和接收資料,但是如果伺服器端需要同時接收多個使用者端並且通訊,複雜度將大大提高
ECHO 程式是網路程式設計通訊互動的一個經典案例,稱為迴應程式,即使用者端輸入哪些內容,伺服器端會在這些內容前加上「ECHO」並將資訊發回給使用者端
伺服器端關鍵程式碼
// 建立伺服器端通訊端物件
ServerSocket this.server = new ServerSocket(8888);
// 是否關閉伺服器端連線
boolean flag = true;
while (flag) {
// 獲取連線的使用者端通訊端物件
socket = server.accept();
// 獲取 socket 相關的輸入流和輸出流
BufferedReader reader = getReader(socket);
BufferedWriter writer = getWriter(socket);
// 儲存使用者端傳送的資料
String data = null;
}
while ((data = reader.readLine()) != null) {
// 當獲取的資訊是「bye」時,關閉流
if ("bye".equals(data)) {
flag = false; //退出的標誌
//關閉相關資源 並且 break;}
else {System.out.println(" 來自使用者端的資料:" + data);
// 回寫給使用者端的資料
writer.write("echo:" + data);
// 插入一個行分隔符,readLine() 方法用來判定字串有沒有結束
writer.newLine();
// 重新整理輸出緩衝區
writer.flush();
}
}
迴圈讀取使用者端傳送資料,直到傳送的是bye退出
使用者端關鍵程式碼
// 建立伺服器端通訊端物件
ServerSocket this.server = new ServerSocket(8888);
// 是否關閉伺服器端連線
boolean flag = true;
while (flag) {
// 獲取連線的使用者端通訊端物件
socket = server.accept();
// 獲取 socket 相關的輸入流和輸出流
BufferedReader reader = getReader(socket);
BufferedWriter writer = getWriter(socket);
// 儲存使用者端傳送的資料
String data = null;
}
else {
// readLine() 方法必須讀取到行分隔符才返回讀取。所以傳遞給輸入流的字串必須
包含行分隔符
System.out.println(" 使用者端輸出的資料 --->\t" + data);
writer.write(data);
// 插入一個行分隔符作為內容結束的標識
writer.newLine();
// 清空緩衝區
writer.flush();
// 讀取伺服器端返回的資料
System.out.println(" 伺服器響應的資料 --->\t " + reader.readLine());
}
說明:
- 以上雙向通訊案例僅僅是使用者端傳送一條,然後馬上接收(讀取)伺服器端返回的資料,並不能實現自由聊天,隨意傳送和接收
- 這是因為讀取和傳送資料時受制於主執行緒約束,因為讀取資料時採用迴圈讀取,會佔據主執行緒資源,此時無法進行其他操作
- 如果想實現自由雙向通訊功能,需要在伺服器端和使用者端分別結合多執行緒功能進行實現,但是難度相應增加很多
網路中通過IP地址可以定位到具體裝置,通過埠可以和裝置上的不同軟體進行通訊,Java中使用InetAddress類代表IP地址
基於TCP通訊伺服器端步驟為
基於TCP通訊使用者端步驟為