目錄
一.回顯服務器的基本概念
二.回顯服務器的簡單示意圖
三.實現回顯服務器(基于UDP)必須要知道的API?
1.DatagramSocket
2.DatagramPacket?
3.InetSocketAddress
4.二者區別?
1. 功能職責
2. 核心作用
3. 使用場景流程
四.實現服務器端的主要思路?
代碼部分如下
實現服務器端的注意事項?
五.實現客戶端的主要思路
實現客戶端的注意事項?
六.完整代碼?
運行測試結果?
一.回顯服務器的基本概念
回顯服務器(Echo)可以看成是網絡編程中的"hello world",是學習網絡編程的入門。簡單介紹一下,回顯服務器就是服務端收到什么,就給客戶端發送什么。我會基于UDP來實現一個回顯服務器。
二.回顯服務器的簡單示意圖
三.實現回顯服務器(基于UDP)必須要知道的API?
1.DatagramSocket
方法簽名 | 方法說明 |
DatagramSocket | 創建一個 UDP 數據報套接字的 Socket,綁定到本機指定的端口(一般用于服務端) |
DatagramSocket(int port) | 創建一個 UDP 數據報套接字的 Socket,綁定到本機任意一個隨機端口(一般用于客戶端) |
void receive(DatagramPacket p) ??? | 從此套接字接收數據報(如果沒有接收到數據報,該方法會阻塞等待) |
void send(DatagramPacket p) | 從此套接字發送數據報(不會阻塞等待,直接發送) |
void close() | 關閉此數據報套接字 |
2.DatagramPacket?
方法簽名 | 方法說明 |
DatagramPacket(byte[] buf, int length) | 構造一個DatagramPacket 以用來接收數據報,接收的數據保存在字節數組(第一個參數buf )中,接收指定長度(第二個參數length ) |
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) | 構造一個DatagramPacket 以用來發送數據報,發送的數據為字節數組(第一個參數buf )中,從0 到指定長度(第二個參數length )。address 指定目的主機的IP 和端口號 |
InetAddress getAddress() | 從接收的數據報中,獲取發送端主機IP 地址;或從發送的數據報中,獲取接收端主機IP 地址 |
int getPort() | 從接收的數據報中,獲取發送端主機的端口號;或從發送的數據報中,獲取接收端主機端口號 |
byte[] getData() | 獲取數據報中的數據 |
3.InetSocketAddress
方法簽名 | 方法說明 |
InetSocketAddress(InetAddress addr, int port) | 創建一個 Socket 地址,包含 IP 地址和端口號 |
4.二者區別?
1. 功能職責
DatagramSocket
:是套接字類,相當于 “碼頭”,負責發送、接收DatagramPacket
數據報,還能綁定端口、管理網絡連接(雖 UDP 無連接,但它提供收發的基礎通道 ),比如通過send
發送數據報、receive
接收數據報,close
關閉套接字。DatagramPacket
:是數據報類,相當于 “集裝箱”,負責封裝 UDP 通信中要發送或接收的數據,包含數據內容、數據長度,以及發送 / 接收時的目標地址(IP + 端口 )或源地址信息。
2. 核心作用
DatagramSocket
聚焦網絡收發操作,決定 “怎么傳”(用哪個端口、怎么建立收發通道 );DatagramPacket
聚焦數據封裝,決定 “傳什么”(數據內容 )和 “傳給誰 / 從哪收”(地址信息 ) 。
3. 使用場景流程
UDP 通信時,先創建DatagramSocket
作為收發的 “通道”,再創建DatagramPacket
封裝數據 / 地址,最后通過DatagramSocket
的send
/receive
完成數據收發 。比如客戶端用DatagramSocket
發送封裝了數據和目標地址的DatagramPacket
;服務器端用DatagramSocket
綁定端口,接收包含數據和源地址的DatagramPacket
?。
四.實現服務器端的主要思路?
1.讀取請求并解析
2.根據請求,計算響應
3.把響應返回給客戶端
4.打印日志(方便觀察)
代碼部分如下
public void start() throws IOException {while(true){//1.讀取請求并解析DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);//輸出型參數socket.receive(requestPacket);String request=new String(requestPacket.getData(),0,requestPacket.getLength());//2.根據請求,計算響應String response=process(request);//3.把響應返回給客戶端DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);//4.打印日志System.out.printf("[%s:%d,request:%s response:%s]\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);}}
實現服務器端的注意事項?
五.實現客戶端的主要思路
1.在控制臺讀取用戶要輸入的內容
2.把請求發送給服務器
3.發送請求數據包給服務器端
4.接收服務器端的響應
5.把從服務器讀取的數據進行解析,打印出來
public void start() throws IOException {Scanner scanner=new Scanner(System.in);while(true){System.out.println("請輸入內容");//按ctrl+d會breakif(!scanner.hasNext()){break;}//1。從控制臺讀取用戶要輸入的內容String resquest=scanner.next();//2.把請求發送給服務器DatagramPacket resquestPacket=new DatagramPacket(resquest.getBytes(),resquest.getBytes().length,InetAddress.getByName(ServerIp),ServerPort);
// socket.receive(resquestPacket);//3.發送請求數據包給服務器端socket.send(resquestPacket);//4.接收服務器端的響應DatagramPacket responsePacket=new DatagramPacket(new byte[4096],4096);socket.receive(responsePacket);//5.把從服務器讀取的數據進行解析,打印出來String response=new String(responsePacket.getData(),0,responsePacket.getLength());System.out.println(response);}}
實現客戶端的注意事項?
六.完整代碼?
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket socket=null;//記錄ip與端口號private String ServerIp;private int ServerPort;//服務器的ip與端口號public UdpEchoClient(String ServerIp,int ServerPort) throws SocketException {this.ServerIp=ServerIp;this.ServerPort=ServerPort;//客戶端這里的端口號等到操作系統隨機分配socket=new DatagramSocket();}public void start() throws IOException {Scanner scanner=new Scanner(System.in);while(true){System.out.println("請輸入內容");//按ctrl+d會breakif(!scanner.hasNext()){break;}//1。從控制臺讀取用戶要輸入的內容String resquest=scanner.next();//2.把請求發送給服務器DatagramPacket resquestPacket=new DatagramPacket(resquest.getBytes(),resquest.getBytes().length,InetAddress.getByName(ServerIp),ServerPort);
// socket.receive(resquestPacket);//3.發送請求數據包給服務器端socket.send(resquestPacket);//4.接收服務器端的響應DatagramPacket responsePacket=new DatagramPacket(new byte[4096],4096);socket.receive(responsePacket);//5.把從服務器讀取的數據進行解析,打印出來String response=new String(responsePacket.getData(),0,responsePacket.getLength());System.out.println(response);}}public static void main(String[] args) throws IOException {UdpEchoClient udpEchoClient=new UdpEchoClient("127.0.0.1",1777);udpEchoClient.start();}
}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;public class UdpEchoServer {private DatagramSocket socket=null;public UdpEchoServer(int port) throws SocketException {socket=new DatagramSocket(port);}public void start() throws IOException {while(true){//1.讀取請求并解析DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);//輸出型參數socket.receive(requestPacket);String request=new String(requestPacket.getData(),0,requestPacket.getLength());//2.根據請求,計算響應String response=process(request);//3.把響應返回給客戶端DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);//4.打印日志System.out.printf("[%s:%d,request:%s response:%s]\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);}}//將private改為public,方便方面有新功能,可以進行重寫public String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer udpEchoServer=new UdpEchoServer(1777);udpEchoServer.start();}
}