上篇文章:
網絡編程—網絡概念https://blog.csdn.net/sniper_fandc/article/details/146923380?fromshare=blogdetail&sharetype=blogdetail&sharerId=146923380&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link
目錄
1 概念
2 Socket套接字
3 UDP數據報套接字
1 概念
????????(1)網絡編程:指網絡上的主機,通過不同的進程,以編程的方式實現網絡通信(網絡數據傳輸)。
????????進程具有隔離性,因此不同的進程間要通信就得通過共享的公共區域來交換數據,網卡即是一種共享公共區域。
????????(2)客戶端(Client)/服務器(Server):客戶端是數據發送方,服務器是數據接收方,由于服務器是被動接收數據,不知道客戶端什么時候發送數據,因此服務器經常是24小時運行。
????????(3)請求(request)和響應(response):客戶端發送給服務器的數據,服務器返回給客戶端的數據。
????????(4)客戶端/服務器交互方式:一問一答(瀏覽網頁)、多問一答(上傳文件)、一問多答(下載文件)和多問多答(遠程控制、游戲串流(手機連接電腦玩電腦游戲))。
2 Socket套接字
????????Socket即是傳輸層(操作系統)提供給程序員的API,有基于TCP和UDP兩種類型,原始的API是C語言的,經過JVM包裝后,形成了Java的Socket API。
3 UDP數據報套接字
????????基于UDP的Socket主要是DatagramSocket,用于發送和接收UDP數據報,作用和操作方式都類似文件(打開Socket文件(對應網卡),發送/接收數據報,關閉文件)。
構造方法/方法 | 含義 |
DatagramSocket() | 構造方法,創建一個UDP數據報套接字的Socket,綁定到本機任意一個隨機端口(一般用于客戶端) |
DatagramSocket(int port) | 構造方法,創建一個UDP數據報套接字的Socket,綁定到本機指定的端口(一般用于服務端) |
void receive(DatagramPacket p) | 從套接字接收數據報(如果沒有接收到數據報,該方法會阻塞等待) |
void send(DatagramPacket p) | 從套接字發送數據報包(不會阻塞等待,直接發送) |
void close() | 關閉數據報套接字 |
????????DatagramPacket是數據報的API,用來組織需要發送和接收的數據。
構造方法/方法 | 含義 |
DatagramPacket(byte[]?buf, int length) | 構造方法,創建一個DatagramPacket用來接收數據報,接收的數據保存在buf,length是數據長度 |
DatagramPacket(byte[]?buf, int offset, int?length,?SocketAddress address | 構造方法,創建一個DatagramPacket用來發送數據報,發送的數據保存在buf,offset是起始下標,length是數據長度,address指定目標主機的IP和端口號 |
InetAddress?getAddress() | 從接收的數據報中,獲取發送端主機IP地址,或從發送的數據報中,獲取接收端主機IP地址 |
int getPort() | 從接收的數據報中,獲取發送端主機的端口號,或從發送的數據報中,獲 取接收端主機端口號 |
byte[] getData() | 獲取數據報中的數據 |
????????而使用InetSocketAddress(InetAddress addr, int port)即可創建一個Socket地址,包含IP地址和端口號,它是SocketAddress的子類。
????????下面利用UDP數據報套接字實現一個回顯服務器,回顯服務器是指客戶端發送什么,服務器就返回什么。
public class UdpServer {//回顯服務器端口號private ?final int PORT = 8000;public ?DatagramSocket datagramSocket = null;//通過端口創建udp服務器(本質是打開一個socket文件)public UdpServer() throws SocketException {datagramSocket = new DatagramSocket(PORT);}//啟動服務器public void start() throws IOException {System.out.println("服務器已啟動");//服務器需要24小時啟動因此寫成死循環while(true){//1.創建接收請求的數據報(分配字節數組空間)并接收請求DatagramPacket datagramPacket = new DatagramPacket(new byte[4096],4096);//該方法會一直阻塞等待直到客戶端發送請求datagramSocket.receive(datagramPacket);//2.解析請求構造響應數據報String request = new String(datagramPacket.getData(),0,datagramPacket.getLength());//3.根據請求構造響應(服務器業務邏輯)String response = process(request);//要用指定發送地址的構造方法(發送地址可以從接收的請求數據報中獲取)DatagramPacket datagramPacketResponse = new DatagramPacket(response.getBytes(),response.getBytes().length,datagramPacket.getSocketAddress());//4.返回響應datagramSocket.send(datagramPacketResponse);System.out.printf("[%s:%d] request=%s;response=%s\n",datagramPacket.getAddress().toString(),datagramPacket.getPort(),request,response);}}//回顯服務器就是發送什么請求返回什么響應private String process(String request) {return request;}public static void main(String[] args) throws IOException {//啟動服務器UdpServer server = new UdpServer();server.start();}}public class UdpClient {//創建客戶端private DatagramSocket socket = null;public UdpClient() throws SocketException {//客戶端一般隨機生成端口號socket = new DatagramSocket();}//啟動客戶端public void start() throws IOException {Scanner scanner = new Scanner(System.in);while(true){//1.構造請求System.out.print(">");String req = scanner.next();//構造的請求數據報既要包含數據又要包含地址(IP+端口)DatagramPacket request = new DatagramPacket(req.getBytes(),req.getBytes().length,InetAddress.getByName("127.0.0.1"),8000);//2.發送請求socket.send(request);//3.接受響應并解析響應DatagramPacket response = new DatagramPacket(new byte[4096],4096);socket.receive(response);String resp = new String(response.getData(),0,response.getLength());//4.將響應返回給用戶System.out.printf("request=%s,response=%s\n",req,resp);}}public static void main(String[] args) throws IOException {UdpClient udpClient = new UdpClient();udpClient.start();}}
????????在服務器代碼中,需要注意response.getBytes().length不等于response.length(),前一個方法獲取的是字節長度,而后一個方法獲取的是字符長度,當字符中有中文時,字節長度和字符長度并不相等,因此不能替換。而字符串“127.0.0.1”是環回IP,表示本機(自己發給自己)。
????????根據結果顯示,雖然發送的一條請求hello World,但是卻被拆成hello和World兩條請求發送了,這是因為這里使用Scanner類的next()方法,該方法以空格作為結束進行分割,因此發送兩條請求。如果向發送一條,就可以使用nextLine(),該方法以換行符結束。
下篇文章:
網絡編程—Socket套接字(TCP)https://blog.csdn.net/sniper_fandc/article/details/146923783?fromshare=blogdetail&sharetype=blogdetail&sharerId=146923783&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link