网络编程套接字之UDP
目录
一、网络编程
二、UDP数据报套接字编程
DatagramSocket
DatagramPacket
实现客户端服务器程序
UDPchoServer
一、网络编程
我们网络编程的核心: Socket API,操作系统为我们应用程序提供的API,我们的Socket是和传输层密切相关的。
我们传输层为我们提供了两个最核心的协议UDP/TCP,所以我们的Socket API也为我们提供了TCP/UDP。
简单认识一下TCP/UDP:
TCP 有连接 可靠传输 面向字节流 全双工
UDP 无连接 不可靠传输 面向数据报 全双工
TCP:
特点:
- 使用TCP协议,必须双方先建立连接,它是一种面向连接的可靠通信协议
- 传输前,采用”三次握手"方式建立连接,所以是可靠的
- 在连接中可进行大数据量的传输
- 连接、发送数据都需要确认,且传输完毕后,还需释放已建立的连接,通信效率低
应用场景:对信息安全要求较高的场景,例如:文件下载、金融等数据通信
UDP:
特点:
UDP是一种无连接,不可靠传输协议
将源IP、目的IP和端口封装成数据包,不需要建立连接
每个数据包大小限制在64kb内
发送不管对方是否准备好,接收方收到也不确认,所以是不可靠的
可以广播发送,发送数据结束时无需释放资源,开销小,速度快
应用场景: 语音通话,视频会话等
二、UDP数据报套接字编程
DatagramSocket
DatagramSocket 这个类表示一个Socket对象,我们操作系统中,把socket对象是当作一个文件来处理的。
一个Socket对象就可以与另一台主机进行通信了,如果要和不同的主机通信,就需要创建多个Socket对象
方法 | 作用 |
---|---|
DatagramSocket() | 创建一个UDP数据报套接字的Socket,绑定到任意一个随机端口号(一般用于客户端) |
DatagramSocket(int port) | 创建一个UDP数据报套接字的Socket,绑定到本机指定端口(一般用于服务器) |
方法 | 作用 |
---|---|
void receive(DatagramPacket p) | 从此套接字接收数据,如果没有接收到数据报,进行阻塞等待) |
void send(DatagramPacket p) | 从此套接字发送数据包(不会阻塞等待,直接发送) |
void close() | 关闭此数据报套接字 |
我们的receive方法参数传入的是一个空的对象,receive方法内部会对这个对象进行填充,从而构造出结果数据,我们称这样的参数为输出型参数
DatagramPacket
DatagramPacket是UDP socket进行发送和接收的数据报。
方法 | 作用 |
---|---|
DatagramPacket(byte[] buf,int length) | 构造一个DatagramPacket用来接收数据报,接收的数据保存在字节数组里,接受指定长度 |
DatagramPacket(byte[] buf,int offset,int length,SocketAddress address) | 构造一个DatagramPacket用来发送数据报,发送的数据为字节数据,从0到指定长度,address用来指定目的主机的IP和端口号 |
DatagramPacket的一些方法:
方法 | 作用 |
---|---|
InetAddress getAddress() | 从接受的数据报中,获取发送端IP地址,或从发送的数据报中,获取接收端主机IP地址 |
int getPort() | 从接收的数据报中,获取发送端主机的端口号,或从发送的数据报中,获取接收端的端口号 |
byte[] getData() | 获取数据报的数据 |
实现客户端服务器程序
我们在这里编写一个最简单的客户端服务器程序:回显服务器(echo server).
我们的服务器做的工作:收到请求,根据请求计算响应,返回响应,最重要的环节就是计算响应这一部分,我们的echo server省略了这一部分,接收到什么就返回什么。
UDPchoServer
//udp回显服务器
public class UDPchoServer {
//声明一个提供udp服务的对象
private DatagramSocket socket;
// 通过构造方法指定服务器端口号
public UDPchoServer(int port) throws SocketException {
// 校验端口号
if (port < 1024 || port > 65535) {
throw new BindException("端口号建议在1024——65535之间");
}
//初始化udp服务端对象
this.socket=new DatagramSocket(port);
}
//启动服务,处理用户表请求
public void start() throws IOException {
System.out.println("服务器已启动");
//循环处理用户请求
//每循环一次就表示处理了一个用户的请求,工作时间是7*24小时不间断
while (true) {
//1.使用DatagramPacket来接受用户发来的数据
DatagramPacket requestpacket=new DatagramPacket(new byte[1024],1024);
//2.接收数据
socket.receive(requestpacket);
//3.解析得到的数据,获取数据报内容
String request = new String(requestpacket.getData(), 0, requestpacket.getLength(), "UTF-8");
//4.通过请求计算响应
String response=process(request);
//5.定义一个用于响应的DatagramPacket封装响应的数据
DatagramPacket responsePacket=new DatagramPacket(response.getBytes(StandardCharsets.UTF_8),0,response.getBytes().length
,requestpacket.getSocketAddress());
//6.发送响应
socket.send(responsePacket);
//7.打印日志
System.out.printf("[%s:%d] request = %s, response = %s\n",requestpacket.getAddress().toString(),requestpacket.getPort()
,request,response);
}
}
protected String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
UDPchoServer udPchoServer = new UDPchoServer(8888);
udPchoServer.start();
}
}
UDPchoClient
//基于UDP的客户端
public class UDPchoClient {
//声明一个DatagramSocket对象
private DatagramSocket socket;
//声明服务器id
private String serverIp;
//声明服务器端口号
private int serverPort;
public UDPchoClient(String serverId,int serverPort) throws SocketException {
//初始化DatagramSocket对象
this.socket=new DatagramSocket();
this.serverIp=serverId;
this.serverPort=serverPort;
}
public void start() throws IOException {
System.out.println("客服端已启动");
//循环处理客户端的输入
while (true) {
System.out.println("->");
//定义Scanner
Scanner scanner=new Scanner(System.in);
//1.获取用户的输入
String request=scanner.nextLine();
if(request==null||request.isEmpty()){
System.out.println("不能输入空字符串");
continue;
}
SocketAddress address=new InetSocketAddress(serverIp,serverPort);
//用DatagramPacket包装用户请求数据
DatagramPacket requestPacket=new DatagramPacket(request.getBytes(StandardCharsets.UTF_8),0,request.getBytes().length,
address);
//发送真实的数据到服务器
socket.send(requestPacket);
//接收服务器的响应
DatagramPacket responsePacket=new DatagramPacket(new byte[1024],1024);
//接受数据
socket.receive(responsePacket);
//解析数据得到响应
String response = new String(responsePacket.getData(), 0, responsePacket.getLength(), "UTF-8");
System.out.printf("request=%s,response=%s\n",request,response);
}
}
public static void main(String[] args) throws IOException {
UDPchoClient client=new UDPchoClient("127.0.0.1",8888);
client.start();
}
}