??博客主頁:?【小扳_-CSDN博客】
?感謝大家點贊??收藏?評論?
文章目錄
? ? ? ? 1.0 UDP 通信
????????1.1 DatagramSocket 類
????????1.2 DatagramPacket 類
????????1.3 實現(xiàn) UDP 通信(一發(fā)一收)
? ? ? ? 1.3.1 客戶端的開發(fā)
? ? ? ? 1.3.2 服務(wù)端的開發(fā)
????????1.4 實現(xiàn) UDP 通信(多發(fā)多收)
? ? ? ? 1.4.1 客戶端的開發(fā)
? ? ? ? 1.4.2 服務(wù)端的開發(fā)
? ? ? ? 2.0 TCP 網(wǎng)絡(luò)通信
? ? ? ? 2.1 Socket 類
????????2.2 ServerSocket 類
? ? ? ? 2.3 實現(xiàn) TCP 網(wǎng)絡(luò)通信(一發(fā)一收)
????????2.3.1 客戶端的開發(fā)
? ? ? ? 2.3.2 服務(wù)端的開發(fā)
? ? ? ? 2.4?實現(xiàn) TCP 網(wǎng)絡(luò)通信(多發(fā)多收)
? ? ? ? 2.4.1 客戶端的開發(fā)
? ? ? ? 2.4.2 服務(wù)端的開發(fā)
? ? ? ? 2.5 實現(xiàn) TCP 網(wǎng)絡(luò)通信(支持與多個客戶端通信)
? ? ? ? 2.6 實現(xiàn) TCP 網(wǎng)絡(luò)通信(綜合案例:群聊)
? ? ? ? 2.6.1 客戶端的開發(fā)
? ? ? ? 2.6.2 服務(wù)端的開發(fā)
? ? ? ? 1.0 UDP 通信
? ? ? ? 無連接、不可靠通信。
? ? ? ? 不事先建立連接;發(fā)送端每次把要發(fā)送的數(shù)據(jù)(限制在 64 KB 內(nèi))、接收端 IP 等信息封裝成一個數(shù)據(jù)包,發(fā)出去就不管了。
? ? ? ? Java 提供了一個 Java.net.DatagramSocket 類來實現(xiàn) UDP 通信。
????????1.1 DatagramSocket 類
? ? ? ? 用于創(chuàng)建客戶端、服務(wù)端。Java 中用于實現(xiàn) UDP 協(xié)議的套接字類,用于創(chuàng)建 UDP 套接字并進行數(shù)據(jù)傳輸。UDP 是無連接的、不可靠的傳輸協(xié)議,適用于一些要求實時性較高、容忍少量數(shù)據(jù)丟失的應(yīng)用場景。
構(gòu)造器:
? ? ? ? 1)public DatagramSocket():創(chuàng)建客戶端的 Socket 對象,系統(tǒng)會隨機分配一個端口號。
? ? ? ? 2)public DatagramSocket(int port):創(chuàng)建服務(wù)端的 Socket 兌現(xiàn),并指定端口號。
方法:
? ? ? ? 1)public void send(DatagramPacket dp):發(fā)送數(shù)據(jù)包。
? ? ? ? 2)public void receive(DatagramPacket p):使用數(shù)據(jù)包接收數(shù)據(jù)。
? ? ? ? 3)public void close():關(guān)閉 UDP 套接字。
????????1.2 DatagramPacket 類
? ? ? ? 創(chuàng)建數(shù)據(jù)包。用于在 UDP 通信中封裝數(shù)據(jù)和目標地址信息。DatagramPacket 類包含了要發(fā)送或接收的數(shù)據(jù)、數(shù)據(jù)的長度、目標地址和端口等信息。
構(gòu)造器:
? ? ? ? 1)public DatagramPacket(byte[] buf,int length,InetAddress address,int port):創(chuàng)建發(fā)出去的數(shù)據(jù)包對象。
? ? ? ? 2)public DatagramPacket(byte[] buf,int length):創(chuàng)建用來接收的數(shù)據(jù)的數(shù)據(jù)包。
方法:
? ? ? ? 1)getData():獲取數(shù)據(jù) 數(shù)據(jù)的字節(jié)數(shù)組。
? ? ? ? 2)getLength():獲取數(shù)據(jù)的長度。字節(jié)數(shù)組的長度。
? ? ? ? 3)getAddress():獲取數(shù)據(jù)報的目標地址。返回的是 InetAddress 類對象,即接收的 IP 對象。
? ? ? ? 4)getPort():獲取數(shù)據(jù)報的目標端口。
????????1.3 實現(xiàn) UDP 通信(一發(fā)一收)
? ? ? ? 整個的過程可以比作:廚師將碟子上的“炒粉”隔空拋出去給服務(wù)員(客戶端發(fā)消息過程),服務(wù)員用碟子接收“炒粉”(服務(wù)端接收消息過程)。注意的是拋出去的只是“炒粉”,而碟子是不拋出去的。
? ? ? ? 1.3.1 客戶端的開發(fā)
????????廚師將碟子上的“炒粉”隔空拋出去給服務(wù)員(客戶端發(fā)消息過程)。
? ? ? ? 首先是對客戶端的開發(fā),先創(chuàng)建客戶端對象,利用無參構(gòu)造器 DatagramSocket() 來創(chuàng)建一個由系統(tǒng)分配的 IP 地址的客戶端對象。把客戶端對象可以比作成一個廚師。
? ? ? ? 再來創(chuàng)建發(fā)出去的數(shù)據(jù)包對象,利用 DatagramPacket(byte[] buf,int length,InetAddress address,int port) 構(gòu)造器來創(chuàng)建數(shù)據(jù)包對象。把數(shù)據(jù)包對象可以比作廚師手上的碟子,其中參數(shù) buf 是存放數(shù)據(jù)的容器,把 buf 比如碟子上盛的“炒粉”,參數(shù) length 是用來記錄發(fā)送數(shù)據(jù)的大小,參數(shù) address 是發(fā)送的 IP 地址對象,將 address 可以比作碟子上的“炒粉”通過 address 來定位到具體要發(fā)送的目標服務(wù)員。參數(shù) port 是用來記錄發(fā)送的目標程序即端口號。
代碼如下:
import java.io.IOException; import java.net.*; public class demo1 { public static void main(String[] args) throws IOException { DatagramSocket socket = new DatagramSocket(); String msg = "需要發(fā)送的消息"; //將字符串轉(zhuǎn)化為字節(jié)數(shù)組 byte[] m = msg.getBytes(); //假如:發(fā)送的地方正是本機,就可以通過 InetAddress.getLocalHost() 方法來獲取到 IP 地址對象, //發(fā)送的程序端口號假設(shè)為: 8888 DatagramPacket packet = new DatagramPacket(m,m.length, InetAddress.getLocalHost(),8888); //接著就可以發(fā)送消息了 socket.send(packet); //最后記得要關(guān)閉資源 socket.close(); } }
? ? ? ? 廚師將碟子上的“炒粉”隔空拋出去給服務(wù)員,服務(wù)員用碟子接收“炒粉”。注意的是拋出去的只是“炒粉”,而碟子是不拋出去的。
? ? ? ? 1.3.2 服務(wù)端的開發(fā)
? ? ? ??服務(wù)員用碟子接收“炒粉”(服務(wù)端接收消息過程)。
? ? ? ? 首先創(chuàng)建服務(wù)端對象,利用有參構(gòu)造器 DatagramSocket(8888) 來創(chuàng)建端口號為 8888 的服務(wù)端對象。
????????這里用到有參構(gòu)造器來創(chuàng)建指定的端口號是因為:為了其他客戶端精確無誤的發(fā)送消息到目標程序上。不用無參構(gòu)造器的是因為:利用無參構(gòu)造器創(chuàng)建的服務(wù)端是由系統(tǒng)隨機分配端口號,不利于其他客戶端訪問服務(wù)端。
? ? ? ? 接著創(chuàng)建接收數(shù)據(jù)的數(shù)據(jù)包對象,利用 DatagramPacket(byte[] buf,int length) 構(gòu)造器來創(chuàng)建數(shù)據(jù)包對象,用來接收數(shù)據(jù)。
? ? ? ? 接收完畢之后,需要關(guān)閉資源。
代碼如下:
import java.net.InetAddress; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; public class Server { public static void main(String[] args) throws IOException { //創(chuàng)建服務(wù)端對象,且指定端口號 DatagramSocket socket = new DatagramSocket(8888); //創(chuàng)建數(shù)據(jù)包對象 //接收數(shù)據(jù)的容器的大小,最大發(fā)送過來的數(shù)據(jù)大小不會超過64KB, //因此用64KB大小的字節(jié)數(shù)組來接收數(shù)據(jù) byte[] bytes = new byte[1024*64]; DatagramPacket packet = new DatagramPacket(bytes,bytes.length); //此時就可以用數(shù)據(jù)包來接收數(shù)據(jù)了 socket.receive(packet); //輸出數(shù)據(jù) byte[] data = packet.getData(); //轉(zhuǎn)化為字符串 String str = new String(data,0,packet.getLength()); System.out.println(str); //還可以獲取到客戶端的IP對象、客戶端的IP對象地址、端口號 InetAddress client = packet.getAddress(); String address = client.getHostAddress(); int clientPort = packet.getPort(); System.out.println("客戶端IP地址:" + address + " , " + "客戶端端口號:" + clientPort); //關(guān)閉資源 socket.close(); } }
????????1.4 實現(xiàn) UDP 通信(多發(fā)多收)
? ? ? ? 客戶端可以多次發(fā)送消息給服務(wù)端,服務(wù)端可以多次接收客戶端發(fā)送的消息。
? ? ? ? 1.4.1 客戶端的開發(fā)
? ? ? ? 在以上代碼進行改造,對發(fā)送消息的代碼設(shè)置循環(huán)。注意退出程序的時候需要關(guān)閉資源。
import java.io.IOException; import java.net.*; import java.util.Scanner; public class Client { public static void main(String[] args) throws IOException { DatagramSocket socket = new DatagramSocket(); Scanner scanner = new Scanner(System.in); while (true) { System.out.println("請輸出需要發(fā)送的消息:"); String msg = scanner.nextLine(); if ("exit".equals(msg)){ System.out.println("成功退出!!!"); //最后記得要關(guān)閉 socket.close(); break; } //將字符串轉(zhuǎn)化為字節(jié)數(shù)組 byte[] m = msg.getBytes(); //假如:發(fā)送的地方正是本機,就可以通過 InetAddress.getLocalHost() 方法來獲取到 IP 地址對象, //發(fā)送的程序端口號假設(shè)為: 8888 DatagramPacket packet = new DatagramPacket(m,m.length, InetAddress.getLocalHost(),8888); //接著就可以發(fā)送消息了 socket.send(packet); } } }
? ? ? ? 1.4.2 服務(wù)端的開發(fā)
? ? ? ? 設(shè)置循環(huán)接收消息。其實對于服務(wù)端來說,不應(yīng)該關(guān)閉資源,因為隨時等待著客戶端來訪問。
代碼如下:
import java.net.InetAddress; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; public class Server { public static void main(String[] args) throws IOException { //創(chuàng)建服務(wù)端對象,且指定端口號 DatagramSocket socket = new DatagramSocket(8888); //創(chuàng)建數(shù)據(jù)包對象 //接收數(shù)據(jù)的容器的大小,最大發(fā)送過來的數(shù)據(jù)大小不會超過64KB, //因此用64KB大小的字節(jié)數(shù)組來接收數(shù)據(jù) byte[] bytes = new byte[1024*64]; DatagramPacket packet = new DatagramPacket(bytes,bytes.length); while (true) { //此時就可以用數(shù)據(jù)包來接收數(shù)據(jù)了 socket.receive(packet); //輸出數(shù)據(jù) byte[] data = packet.getData(); //轉(zhuǎn)化為字符串 String str = new String(data,0,packet.getLength()); System.out.println(str); //還可以獲取到客戶端的IP對象、客戶端的IP對象地址、端口號 InetAddress client = packet.getAddress(); String address = client.getHostAddress(); int clientPort = packet.getPort(); System.out.println("客戶端IP地址:" + address + " , " + "客戶端端口號:" + clientPort); System.out.println("========================"); } /* //關(guān)閉資源 socket.close();*/ } }
運行結(jié)果:
服務(wù)端:
客戶端:
? ? ? ? 需要注意的是:在啟動程序的時候,應(yīng)該先啟動服務(wù)端程序,再啟動客戶端程序。
? ? ? ? 2.0 TCP 網(wǎng)絡(luò)通信
? ? ? ? 面向連接、可靠連接。
? ? ? ? 2.1 Socket 類
? ? ? ??Java 中用于實現(xiàn) TCP 通信的類,主要用于在客戶端和服務(wù)器之間建立 TCP 連接。通過 Socket 類,可以在客戶端和服務(wù)器之間進行雙向通信,實現(xiàn)數(shù)據(jù)的傳輸和交互。
? ? ? ? 1)創(chuàng)建 Socket 對象:通過 Socket 類的構(gòu)造方法可以創(chuàng)建一個 Socket 對象,用于表示客戶端與服務(wù)器之間的 TCP 連接。可以連接指定的服務(wù)器和端口。
? ? ? ? 2)發(fā)送數(shù)據(jù):通過 Socket 類的 getOutputStream 方法獲取輸出流,可以向服務(wù)器發(fā)送數(shù)據(jù)。
? ? ? ? 3)接收數(shù)據(jù):通過 Socket 類的 getInputStream 方法獲取輸入流,可以從服務(wù)器接收數(shù)據(jù)。
? ? ? ? 4)關(guān)閉連接:通過 Socket 類的 close 方法可以關(guān)閉 TCP 連接。
其他常用的方法:
? ? ? ? 1)getInetAddress() 方法:獲取遠程主機的地址,返回 InetAddress 對象。
? ? ? ? 2)getPort() 方法:獲取遠程主機的端口號。
? ? ? ? 3)getLocalAddress() 方法:獲取本地主機的地址,返回 InetAddress 對象。
? ? ? ? 4)getLocalPort() 方法:獲取本地主機的端口號。
? ? ? ? 簡單來說,通過構(gòu)造方法指定的 IP 地址和端口號來創(chuàng)建的客戶端與指定的服務(wù)器建立連接。建立連接相當于創(chuàng)建了管道。創(chuàng)建管道來支持字節(jié)流的發(fā)送或者接收。通過 getOutputStream 方法或者 getInputStream 方法來發(fā)送或者接收字節(jié)流。
????????2.2 ServerSocket 類
????????Java 中用于實現(xiàn) TCP 服務(wù)器端的類,它用于監(jiān)聽客戶端的連接請求并創(chuàng)建對應(yīng)的 Socket 對象進行通信。
構(gòu)造方法:
????????ServerSocket(int port) 構(gòu)造方法:創(chuàng)建一個新的 ServerSocket 對象,綁定到指定的端口。
常用的方法:
? ? ? ? 1)accept() 方法:監(jiān)聽并接受客戶端的連接請求,返回一個新的 Socket 對象用于與客戶端通信。
? ? ? ? 2)close() 方法:關(guān)閉 ServerSocket 對象,停止監(jiān)聽新的連接請求。
? ? ? ? 3)getInetAddress() 方法:獲取 ServerSocket?綁定的本地地址。
? ? ? ? 4)getLocalPort() 方法:獲取 ServerSocket 綁定的本地端口號。
????????ServerSocket 類用于在服務(wù)器端監(jiān)聽客戶端的連接請求,并創(chuàng)建對應(yīng)的 Socket 對象用于與客戶端進行通信。通過 ServerSocket 類提供的方法,您可以管理服務(wù)器端的 TCP 連接、設(shè)置相關(guān)參數(shù)以及處理客戶端的連接請求。類用于在服務(wù)器端監(jiān)聽客戶端的連接請求,并創(chuàng)建對應(yīng)的 Socket?對象用于與客戶端進行通信。通過 ServerSocket 類提供的方法,您可以管理服務(wù)器端的 TCP 連接、設(shè)置相關(guān)參數(shù)以及處理客戶端的連接請求。
? ? ? ? 2.3 實現(xiàn) TCP 網(wǎng)絡(luò)通信(一發(fā)一收)
????????2.3.1 客戶端的開發(fā)
? ? ? ? 先創(chuàng)建管道對象,利用有參構(gòu)造器 Socket(String host,int port) 創(chuàng)建連接指定的服務(wù)器的管道。接著,利用 Socket 對象提供的方法 getOutputStream() 方法來獲取字節(jié)輸出流對象,利用該對象進行寫操作,最后發(fā)送完畢之后,需要關(guān)閉資源。
代碼如下:
import java.io.*; import java.net.Socket; public class Client { public static void main(String[] args) throws IOException { //創(chuàng)建管道對象 Socket socket = new Socket("127.0.0.1",8888); //獲取到輸出流 OutputStream os = socket.getOutputStream(); //將其低級的輸出流升級為高級的輸出流 DataOutputStream dos = new DataOutputStream(os); dos.writeUTF("需要發(fā)送的消息!!!"); //關(guān)閉資源 dos.close(); socket.close(); } }
? ? ? ? 注意關(guān)閉資源的順序,先關(guān)閉后開啟的資源,再關(guān)閉前面開啟的資源。
? ? ? ? 2.3.2 服務(wù)端的開發(fā)
? ? ? ? 先創(chuàng)建 ServerSocket 類,用有參構(gòu)造器 ServerSocket(int port) 創(chuàng)建指定端口號的對象,再用 accept() 方法來等待接收來自客戶端發(fā)送來的連接請求,客戶端與服務(wù)端連接完畢之后,就會返回一個 Socket 對象給服務(wù)端,該 Socket 對象可以理解為管道的服務(wù)器的一端,客戶端已經(jīng)有 Socket 對象了,該對象就可以理解為管道的客戶端的一端。
? ? ? ? 接著,通過 Socket 提供的方法來獲取二進制輸出流對象或者是二進制輸入流對象,通過管道將數(shù)據(jù)進行傳輸?shù)娇蛻舳恕?/strong>
代碼如下:
import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws IOException { //創(chuàng)建服務(wù)端,并指定端口號 ServerSocket serverSocket = new ServerSocket(8888); //等待客戶端發(fā)送消息來獲取管道對象 Socket socket = serverSocket.accept(); //通過管道對象再來獲取到讀取流對象 InputStream inputStream = socket.getInputStream(); //再將低級流轉(zhuǎn)化為高級流 DataInputStream dis = new DataInputStream(inputStream); String s = dis.readUTF(); System.out.println(s); //關(guān)閉資源 dis.close(); socket.close(); } }
運行結(jié)果:
????????客戶端發(fā)送數(shù)據(jù),服務(wù)端接收數(shù)據(jù)的代碼就實現(xiàn)了。
? ? ? ? 2.4?實現(xiàn) TCP 網(wǎng)絡(luò)通信(多發(fā)多收)
? ? ? ? 2.4.1 客戶端的開發(fā)
? ? ? ? 將以上客戶端的代碼輸出數(shù)據(jù)進行循環(huán)即可,每次輸出完數(shù)據(jù)之后,最好刷新一下。
代碼如下:
import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; public class MyClient { //TCP 實現(xiàn)多發(fā)多收 public static void main(String[] args) throws IOException { //創(chuàng)建管道對象 Socket socket = new Socket("127.0.0.1",8888); //獲取寫數(shù)據(jù)流 OutputStream os = socket.getOutputStream(); //將低級流轉(zhuǎn)換為高級流 DataOutputStream dos = new DataOutputStream(os); Scanner scanner = new Scanner(System.in); while (true) { String msg = scanner.nextLine(); if ("exit".equals(msg)){ System.out.println("退出成功!!!!"); //關(guān)閉資源 dos.close(); socket.close(); break; } //寫數(shù)據(jù) dos.writeUTF(msg); //刷新數(shù)據(jù),將緩存的數(shù)據(jù)進行刷新 dos.flush(); } } }
? ? ? ? 2.4.2 服務(wù)端的開發(fā)
? ? ? ? 將以上服務(wù)端的輸入數(shù)據(jù)的代碼加上循環(huán)即可,需要注意的是:在客戶端管道一端假設(shè)要斷開連接的時候,此時服務(wù)端還在連接著管道,這就會導致服務(wù)端一端會報異常,因此,需要對服務(wù)端輸入數(shù)據(jù)這一段代碼加上 try-catch 捕獲異常處理。
代碼如下:
import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; public class MyServer { public static void main(String[] args) throws IOException { //創(chuàng)建服務(wù)端管道對象 ServerSocket serverSocket = new ServerSocket(8888); //等待接收客戶端,創(chuàng)建管道對象 Socket socket = serverSocket.accept(); //創(chuàng)建完管道之后,就要創(chuàng)建字節(jié)流 InputStream inputStream = socket.getInputStream(); //再將低級流升級為高級流 DataInputStream dis = new DataInputStream(inputStream); while (true) { try { //接著就可以讀數(shù)據(jù)了 String s = dis.readUTF(); System.out.println(s); //通過管道對象來獲取到發(fā)送消息的IP對象 InetAddress clientInetAddress = socket.getInetAddress(); //socket.getRemoteSocketAddress() //通過管道對象就可以獲取到端口號 int clientPort = socket.getPort(); //通過IP對象就可以獲取到IP地址 String address = clientInetAddress.getHostAddress(); //通過IP對象就可以獲取到IP名字 String clientName = clientInetAddress.getHostName(); System.out.println("端口號: "+ clientPort + " ,IP名字: " + clientName + " ,IP地址: " + address); System.out.println("====================="); } catch (IOException e) { System.out.println(" IP名字: " + socket.getInetAddress().getHostName() + " ,IP地址: " + socket.getInetAddress().getHostAddress() + " 離線了!!!"); dis.close(); socket.close(); break; } } } }
? ? ? ? 2.5 實現(xiàn) TCP 網(wǎng)絡(luò)通信(支持與多個客戶端通信)
? ? ? ? 實現(xiàn)思路:當前服務(wù)端只有主線程負責一個客戶端進行通信,那么實現(xiàn)服務(wù)端與多個客戶端實現(xiàn)通信需要實現(xiàn)多線程,在服務(wù)端中創(chuàng)建多個線程,每一個線程都對應(yīng)一個客戶端進行通信,而主線線程將將接收到的 Socket 對象通過構(gòu)造方法交給線程類來創(chuàng)建線程對象。至于客戶端沒有其他要改動的地方。
客戶端代碼如下:
import java.io.*; import java.net.Socket; import java.util.Scanner; public class Client { public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1",9999); OutputStream os = socket.getOutputStream(); DataOutputStream dos = new DataOutputStream(os); Scanner scanner = new Scanner(System.in); while (true){ System.out.println("請輸入:"); String str = scanner.nextLine(); if ("exit".equals(str)){ System.out.println("退出成功!!!!"); dos.close(); socket.close(); break; } dos.writeUTF(str); //刷新數(shù)據(jù) dos.flush(); } } }
服務(wù)端代碼如下:
繼承 Thread 類:
import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.net.Socket; public class ThreadServer extends Thread{ private final Socket socket ; public ThreadServer(Socket socket){ this.socket = socket; } @Override public void run() { try { InputStream inputStream = socket.getInputStream(); DataInputStream dis = new DataInputStream(inputStream); while (true){ try { String str = dis.readUTF(); System.out.println(" 來自主機IP為:"+socket.getInetAddress().getHostAddress() + " ,主機名為:" + socket.getInetAddress().getHostName() + ",端口號為:" + socket.getPort() + "發(fā)送的信息:"+str); } catch (IOException e) { System.out.println(socket.getRemoteSocketAddress()+" 已下線了 !!!!"); dis.close(); socket.close(); break; } } } catch (Exception e) { e.printStackTrace(); } } }
服務(wù)端代碼:
import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(9999); while (true){ Socket socket = serverSocket.accept(); new ThreadServer(socket).start(); System.out.println(socket.getRemoteSocketAddress() + " 已上線啦!!!!"); } } }
????????這就實現(xiàn)了服務(wù)端可以與多個客戶端進行通信了。
? ? ? ? 2.6 實現(xiàn) TCP 網(wǎng)絡(luò)通信(綜合案例:群聊)
? ? ? ? 比如說:微信群聊,客戶端一方發(fā)送了一條消息,不僅當前客戶端可以看到這條消息,其他在群內(nèi)的客戶端也可以看得見這條消息。
? ? ? ? 實現(xiàn)思路:想要實現(xiàn)客戶端發(fā)送的消息,群里的客戶端都可以看見,那么需要將客戶端發(fā)送的這條消息先要發(fā)送給服務(wù)端,再有服務(wù)端分發(fā)給其他群里的客戶端即可。
? ? ? ? 2.6.1 客戶端的開發(fā)
? ? ? ? 對于客戶端來說,既可以有發(fā)送消息的功能,也有接收消息的能力。那么就可以再創(chuàng)建一個線程來負責接收消息。
代碼如下:
繼承 Thread 類:
import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.net.Socket; public class ThreadClient extends Thread{ private Socket socket; public ThreadClient(Socket socket){ this.socket = socket; } @Override public void run() { try { //開始接收數(shù)據(jù) InputStream inputStream = socket.getInputStream(); DataInputStream dis = new DataInputStream(inputStream); while (true){ try { String str = dis.readUTF(); System.out.println( " : " + str); } catch (IOException e) { System.out.println(" 本機已離線!!! "); dis.close(); socket.close(); break; } } } catch (Exception e) { e.printStackTrace(); } } }
客戶端代碼:
import java.io.*; import java.net.Socket; import java.util.Scanner; public class Client { public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1",9999); //負責接收消息的線程 new ThreadClient(socket).start(); //主線程負責發(fā)送消息 OutputStream os = socket.getOutputStream(); DataOutputStream dos = new DataOutputStream(os); Scanner scanner = new Scanner(System.in); while (true){ System.out.println("請輸入:"); String str = scanner.nextLine(); String sign = "exit"; if (sign.equals(str)){ System.out.println("退出成功!!!!"); dos.close(); socket.close(); break; } dos.writeUTF(str); //刷新數(shù)據(jù) dos.flush(); } } }
? ? ? ? 2.6.2 服務(wù)端的開發(fā)
????????首先需要記住群內(nèi)都有哪些客戶端對象,可以用數(shù)組來接收這些客戶端對象,每一個客戶端上線的時候,將該客戶端對象放到數(shù)組容器中。再接收完某一個客戶端發(fā)送的消息后,再將其數(shù)組進行遍歷發(fā)送消息即可。
代碼如下:
繼承 Thread 類:
import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class ThreadServer extends Thread{ private Socket socket ; public ThreadServer(Socket socket){ this.socket = socket; } @Override public void run() { try { InputStream inputStream = socket.getInputStream(); DataInputStream dis = new DataInputStream(inputStream); while (true){ try { String str = dis.readUTF(); System.out.println(" 來自主機IP為:"+socket.getInetAddress().getHostAddress() + " ,主機名為:" + socket.getInetAddress().getHostName() + ",端口號為:" + socket.getPort() + "發(fā)送的信息:"+str); //將消息發(fā)送到每個客戶端中 sendClient(str); } catch (IOException e) { System.out.println(socket.getRemoteSocketAddress()+" 已下線了 !!!!"); Server.socketList.remove(socket); dis.close(); socket.close(); break; } } } catch (Exception e) { e.printStackTrace(); } } private void sendClient(String str) throws IOException { for (Socket s : Server.socketList) { OutputStream os = s.getOutputStream(); DataOutputStream dos = new DataOutputStream(os); dos.writeUTF(str); dos.flush(); } } }
服務(wù)端代碼:
import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; public class Server { public static List<Socket> socketList = new ArrayList<>(); public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(9999); while (true){ Socket socket = serverSocket.accept(); new ThreadServer(socket).start(); socketList.add(socket); System.out.println(socket.getRemoteSocketAddress() + " 已上線啦!!!!"); } } }
運行結(jié)果:
服務(wù)端的運行輸出的結(jié)果:
客戶端 1 運行輸出的結(jié)果:
客戶端 2 運行輸出的結(jié)果:
客戶端 3 運行輸出的結(jié)果:
? ? ? ? 這就是實現(xiàn)了群聊了,一共有三個客戶端,每個客戶端發(fā)消息給服務(wù)器,然后由服務(wù)器再分發(fā)送消息給三個客戶端。如果有某一個客戶端先斷開連接,輸出 "exit" 就可以斷開與服務(wù)器的連接,那么數(shù)組中就會刪除退出的 Socket 對象,其它客戶端不受影響。
? ? ? ? 由于 IP 的限制(IP 為內(nèi)網(wǎng)),不能讓其他的機器連接目前的機器,所以只能本機的程序連接本地的程序。如果 IP 為外網(wǎng),就可以讓其他機器來連接了,實現(xiàn)跨主機通信了。文章來源:http://www.zghlxwxcb.cn/news/detail-858562.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-858562.html
到了這里,關(guān)于JavaEE 初階篇-深入了解 UDP 通信與 TCP 通信(綜合案例:實現(xiàn) TCP 通信群聊)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!