Java學習筆記 第十章 網路程式設計
阿新 • • 發佈:2021-06-10
JAVA學習筆記第十章
10.網路程式設計
10.1引入
TCP建立連線:
釋放連線:
10.2InetAddress & InetSocketAddress
InetAddress有兩個方法:
InetSocketAddress封裝了ip和埠號
10.3套接字
10.4TCP通訊
10.4.1客戶端和服務端通訊
客戶端傳送一句話到伺服器
public class TestClient { // 客戶端 public static void main(String[] args) throws IOException { // 建立套接字:指定伺服器ip和埠號 Socket s = new Socket("127.0.0.1", 8888); //對程式設計師來說,向外傳送資料 感受 --》 利用輸出流; OutputStream os = s.getOutputStream(); DataOutputStream dos = new DataOutputStream(os); //利用OutputStream就可以向外傳送資料流,但是沒有直接傳送String的方法 //所以我們又在OutputStream外面套了一個處理流:DataOutputStream dos.writeUTF("你好"); // 關閉流 + 關閉網路資源 dos.close(); os.close(); s.close(); } }
public class TestServer { // 伺服器 public static void main(String[] args) throws IOException { // 1.建立套接字:指定伺服器的埠 ServerSocket ss = new ServerSocket(8888); // 2.等待客戶端傳送來的資訊 Socket s = ss.accept(); // 阻塞方法:等待客戶端的資料,什麼時候收到資料,什麼時候程式繼續向下執行 // 3.accept返回值為Socket,這個Socket其實就是客戶端的Socket //接到這個Socket之後,客戶端和服務端才真正產生了連線,才真正可以通訊了 //感受到的操作流 InputStream is = s.getInputStream(); DataInputStream dis = new DataInputStream(is); // 4.讀取客戶端發來的資料 String str = dis.readUTF(); System.out.println("客戶端發來的資料為:" + str); // 5.關閉流 + 關閉網路資源 dis.close(); is.close(); s.close(); } }
先開伺服器,然後開啟客戶端,在伺服器端可以看到控制檯打印出客戶端發來的資料為:你好
如果先開客戶端,會報出連線被拒絕的錯誤
服務端也可以向客戶端傳送資訊
public class TestServer { // 伺服器 public static void main(String[] args) throws IOException { // 1.建立套接字:指定伺服器的埠 ServerSocket ss = new ServerSocket(8888); // 2.等待客戶端傳送來的資訊 Socket s = ss.accept(); // 阻塞方法:等待客戶端的資料,什麼時候收到資料,什麼時候程式繼續向下執行 // 3.accept返回值為Socket,這個Socket其實就是客戶端的Socket //接到這個Socket之後,客戶端和服務端才真正產生了連線,才真正可以通訊了 //感受到的操作流 InputStream is = s.getInputStream(); DataInputStream dis = new DataInputStream(is); // 4.讀取客戶端發來的資料 String str = dis.readUTF(); System.out.println("客戶端發來的資料為:" + str); // 6.向客戶端傳送資料 OutputStream os = s.getOutputStream(); DataOutputStream dos = new DataOutputStream(os); dos.writeUTF("伺服器端接收到你的訊息"); // 5.關閉流 + 關閉網路資源 dos.close(); os.close(); dis.close(); is.close(); s.close(); } }
public class TestClient { // 客戶端
public static void main(String[] args) throws IOException {
// 建立套接字:指定伺服器ip和埠號
Socket s = new Socket("127.0.0.1", 8888);
//對程式設計師來說,向外傳送資料 感受 --》 利用輸出流;
OutputStream os = s.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
//利用OutputStream就可以向外傳送資料流,但是沒有直接傳送String的方法
//所以我們又在OutputStream外面套了一個處理流:DataOutputStream
dos.writeUTF("你好");
//客戶端接收伺服器的資料
InputStream is = s.getInputStream();
DataInputStream dis = new DataInputStream(is);
String str = dis.readUTF();
System.out.println("伺服器對我說:" + str);
// 關閉流 + 關閉網路資源
dis.close();
is.close();
dos.close();
os.close();
s.close();
}
}
10.4.2登入驗證
模擬登入時輸入賬號密碼,並進行驗證的過程
public class User implements Serializable {
private static final long serialVersionUID = 6158646660109509935L;
private String name;
private String pwd;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public User(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
}
需要實現Serializable介面,並且通過idea建立UID
public class Client { //客戶端
public static void main(String[] args) throws IOException {
// 1.建立套接字
Socket s = new Socket("127.0.0.1", 8888);
// 2.錄入賬號和密碼
Scanner sc = new Scanner(System.in);
System.out.println("username:");
String name = sc.next();
System.out.println("password:");
String pwd = sc.next();
// 3.將賬號和密碼封裝為一個User物件
User user = new User(name, pwd);
// 4.向外傳送物件,利用輸出流
OutputStream os = s.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(user);
// 5.接收服務端傳送的結果
InputStream is = s.getInputStream();
DataInputStream dis = new DataInputStream(is);
Boolean flag = dis.readBoolean();
System.out.println(flag);
// 6.關閉流 + 網路資源
dis.close();
is.close();
oos.close();
os.close();
s.close();
}
}
public class Server { //服務端
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 1.建立套接字
ServerSocket ss = new ServerSocket(8888);
// 2.等待客戶端發來的資訊
Socket s = ss.accept();
// 3.獲取傳入的資料
InputStream is = s.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
// 4.讀取客戶端傳送的資料
User user = (User) ois.readObject();
// 5.對物件進行驗證:
boolean flag = false;
if (user.getName().equals("wrr") && user.getPwd().equals("1234")){
flag = true;
}
// 6.向客戶端輸出結果
OutputStream os = s.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeBoolean(flag);
// 7.關閉流 + 網路資源
dos.close();
os.close();
ois.close();
is.close();
ss.close();
}
}
10.4.3完整的異常處理方式
Socket提出去,其他的流在外面初始化為null
關閉流和資源放到finally,再對裡面的流進行異常處理
public class Client { //客戶端
public static void main(String[] args){
// 1.建立套接字
Socket s = null;
OutputStream os = null;
ObjectOutputStream oos = null;
InputStream is = null;
DataInputStream dis = null;
try {
s = new Socket("127.0.0.1", 8888);
// 2.錄入賬號和密碼
Scanner sc = new Scanner(System.in);
System.out.println("username:");
String name = sc.next();
System.out.println("password:");
String pwd = sc.next();
// 3.將賬號和密碼封裝為一個User物件
User user = new User(name, pwd);
// 4.向外傳送物件,利用輸出流
os = s.getOutputStream();
oos = new ObjectOutputStream(os);
oos.writeObject(user);
// 5.接收服務端傳送的結果
is = s.getInputStream();
dis = new DataInputStream(is);
Boolean flag = dis.readBoolean();
System.out.println(flag);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 6.關閉流 + 網路資源
try {
if(dis != null) {
dis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(s != null) {
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class Server {
public static void main(String[] args){
// 1.建立套接字
ServerSocket ss = null;
InputStream is = null;
ObjectInputStream ois = null;
OutputStream os = null;
DataOutputStream dos = null;
try {
ss = new ServerSocket(8888);
// 2.等待客戶端發來的資訊
Socket s = ss.accept();
// 3.獲取傳入的資料
is = s.getInputStream();
ois = new ObjectInputStream(is);
// 4.讀取客戶端傳送的資料
User user = null;
try {
user = (User) ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 5.對物件進行驗證:
boolean flag = false;
if (user.getName().equals("wrr") && user.getPwd().equals("1234")){
flag = true;
}
// 6.向客戶端輸出結果
os = s.getOutputStream();
dos = new DataOutputStream(os);
dos.writeBoolean(flag);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 7.關閉流 + 網路資源
try {
if(dos != null) {
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(ss != null) {
ss.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
10.4.4多個客戶端和一個伺服器
客戶端的程式碼不變
public class Client { //客戶端
public static void main(String[] args){
// 1.建立套接字
Socket s = null;
OutputStream os = null;
ObjectOutputStream oos = null;
InputStream is = null;
DataInputStream dis = null;
try {
s = new Socket("127.0.0.1", 8888);
// 2.錄入賬號和密碼
Scanner sc = new Scanner(System.in);
System.out.println("username:");
String name = sc.next();
System.out.println("password:");
String pwd = sc.next();
// 3.將賬號和密碼封裝為一個User物件
User user = new User(name, pwd);
// 4.向外傳送物件,利用輸出流
os = s.getOutputStream();
oos = new ObjectOutputStream(os);
oos.writeObject(user);
// 5.接收服務端傳送的結果
is = s.getInputStream();
dis = new DataInputStream(is);
Boolean flag = dis.readBoolean();
System.out.println(flag);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 6.關閉流 + 網路資源
try {
if(dis != null) {
dis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(s != null) {
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服務端 Server.java
public class Server {
public static void main(String[] args){
// 1.建立套接字
ServerSocket ss = null;
Socket s = null;
int count = 0; // 定義一個計數器,用來計數 客戶端的請求
try {
ss = new ServerSocket(8888);
// 2.等待客戶端發來的資訊
while (true) { // 加入死迴圈,伺服器一直監聽客戶端是否傳送資料
s = ss.accept();
//每次過來的客戶端的請求靠執行緒處理
new ServerThread(s).start();
count++;
System.out.println("當前是第"+count+"個使用者訪問我們的伺服器,對應的使用者是"+s.getInetAddress());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
新建ServerThread.java
public class ServerThread extends Thread{ // 執行緒:專門處理客戶端的請求
InputStream is = null;
ObjectInputStream ois = null;
OutputStream os = null;
DataOutputStream dos = null;
Socket s = null;
public ServerThread(Socket s){
this.s = s;
}
@Override
public void run() {
// 3.獲取傳入的資料
try{
is = s.getInputStream();
ois = new ObjectInputStream(is);
// 4.讀取客戶端傳送的資料
User user = null;
try {
user = (User) ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 5.對物件進行驗證:
boolean flag = false;
if (user.getName().equals("wrr") && user.getPwd().equals("1234")){
flag = true;
}
// 6.向客戶端輸出結果
os = s.getOutputStream();
dos = new DataOutputStream(os);
dos.writeBoolean(flag);
} catch (IOException e){
e.printStackTrace();
} finally {
try {
if(dos != null) {
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
10.5UDP通訊
10.5.1單向通訊
public class TestSend {
public static void main(String[] args) throws IOException { //傳送方
System.out.println("學生上線");
// 1.準備套接字,指定傳送方的埠號
DatagramSocket ds = new DatagramSocket(8888);
// 2.準備資料包
String str = "你好";
byte[] bytes = str.getBytes();
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"), 9999);
/*
需要四個引數: 1.傳送資料轉為位元組的陣列 2.位元組陣列的長度 3.接收方的IP地址 4.接收方的埠號
*/
// 3.傳送
ds.send(dp);
// 4.關閉資源
ds.close();
}
}
public class TestReceive {
public static void main(String[] args) throws IOException { //接收方
System.out.println("接收訊息");
// 1.準備套接字
DatagramSocket ds = new DatagramSocket(9999);
// 2.有一個空的資料包,準備接收對方傳過來的資料
byte[] b = new byte[1024];
DatagramPacket dp = new DatagramPacket(b, b.length);
// 3.接收對方的資料包,然後放入到我們的dp資料包中填充
ds.receive(dp); //接收完以後,dp裡面就填充好內容了
// 4.取出資料:
byte[] data = dp.getData();
String s = new String(data, 0, dp.getLength()); // dp.getLength()陣列包中的有效長度
System.out.println(s);
// 5.關閉資源
ds.close();
}
}
10.5.2雙向通訊
public class TestReceive {
public static void main(String[] args) throws IOException { //接收方
System.out.println("接收訊息");
// 1.準備套接字
DatagramSocket ds = new DatagramSocket(9999);
// 2.有一個空的資料包,準備接收對方傳過來的資料
byte[] b = new byte[1024];
DatagramPacket dp = new DatagramPacket(b, b.length);
// 3.接收對方的資料包,然後放入到我們的dp資料包中填充
ds.receive(dp); //接收完以後,dp裡面就填充好內容了
// 4.取出資料:
byte[] data = dp.getData();
String s = new String(data, 0, dp.getLength()); // dp.getLength()陣列包中的有效長度
System.out.println(s);
// 5.回覆
String str = "Hello";
byte[] bytes = str.getBytes();
DatagramPacket dp1 = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"), 8888);
ds.send(dp1);
// 5.關閉資源
ds.close();
}
}
public class TestSend {
public static void main(String[] args) throws IOException { //傳送方
System.out.println("學生上線");
// 1.準備套接字,指定傳送方的埠號
DatagramSocket ds = new DatagramSocket(8888);
// 2.準備資料包
String str = "你好";
byte[] bytes = str.getBytes();
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"), 9999);
/*
需要四個引數: 1.傳送資料轉為位元組的陣列 2.位元組陣列的長度 3.接收方的IP地址 4.接收方的埠號
*/
// 3.傳送
ds.send(dp);
// 4.接收
byte[] b = new byte[1024];
DatagramPacket dp1 = new DatagramPacket(b, b.length);
ds.receive(dp1);
byte[] data = dp1.getData();
String s = new String(data, 0, dp1.getLength());
System.out.println(s);
// 4.關閉資源
ds.close();
}
}
10.5.3完整的異常處理方式
與TCP協議的通訊一致,如10.4.3