目录
一.回显服务器的基本概念
回显服务器(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会break
if(!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会break
if(!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();
}
}