实现回显服务器(基于UDP)
目录
一.回显服务器的基本概念
二.回显服务器的简单示意图
三.实现回显服务器(基于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();}
}