UDP(用户数据报协议)就像日常生活中的邮件投递,是不能保证可靠地寄到目的地。UDP是无连接 的,对系统资源的要求较少,UDP可能丢包不保证数据顺序。但是对于网络游戏和在线视频等要求传 输快、实时性高、质量可稍差一点的数据传输,UDP还是非常不错的。
UDP Socket网络编程比TCP Socket编程简单多,UDP是无连接协议,不需要像TCP一样监听端口,建 立连接,然后才能进行通信。
一、DatagramSocket类
java.net包中提供了两个类:DatagramSocket和DatagramPacket用来支持UDP通信。这里先介绍一下DatagramSocket类,DatagramSocket用于在程序之间建立传送数据报的通信连接。
DatagramSocket常用的构造方法:
- DatagramSocket():创建数据报DatagramSocket对象,并将其绑定到本地主机上任何可用的端 口。
- DatagramSocket(int port):创建数据报DatagramSocket对象,并将其绑定到本地主机上的指定端 口。
- DatagramSocket(int port, InetAddress laddr):创建数据报DatagramSocket对象,并将其绑定到指定 的本地地址。
DatagramSocket其他的常用方法有:
- void send(DatagramPacket p):从发送数据报包。
- void receive(DatagramPacket p):接收数据报包。
- int getPort():返回DatagramSocket连接到的远程端口。
- int getLocalPort():返回DatagramSocket绑定到的本地端口。
- InetAddress getInetAddress():返回DatagramSocket连接的地址。
- InetAddress getLocalAddress():返回DatagramSocket绑定的本地地址。
- boolean isClosed():返回DatagramSocket是否处于关闭状态。
- boolean isConnected():返回DatagramSocket是否处于连接状态。
- void close():关闭DatagramSocket。
DatagramSocket也实现了AutoCloseable接口,通过自动资源管理技术关闭DatagramSocket。
二、DatagramPacket类
DatagramPacket用来表示数据报包,是数据传输的载体。DatagramPacket实现无连接数据包投递服务, 每投递数据包仅根据该包中信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个 包可能选择不同的路由,也可能按不同的顺序到达,不保证包都能到达目的。
DatagramPacket的构造方法:
- DatagramPacket(byte[] buf, int length):构造数据报包,buf包数据,length是接收包数据的长度。
- DatagramPacket(byte[] buf, int length, InetAddress address, int port):构造数据报包,包发送到指定主机上的指定端口号。
- DatagramPacket(byte[] buf, int offset, int length):构造数据报包,offset是buf字节数组的偏移量。
- DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):构造数据报包,包发 送到指定主机上的指定端口号。
DatagramPacket常用的方法:
- InetAddress getAddress():返回发往或接收该数据报包相关的主机的IP地址。
- byte[] getData():返回数据报包中的数据。
- int getLength():返回发送或接收到的数据(byte[])的长度。
- int getOffset():返回发送或接收到的数据(byte[])的偏移量。
- int getPort():返回发往或接收该数据报包相关的主机的端口号。
三、案例:文件上传工具
服务器端UploadServer代码如下:
import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* @author : 蔡政洁
* @email :[email protected]
* @date : 2020/2/20
* @time : 3:56 下午
*/
//服务器端
public class UploadServer {
public static void main(String[] args) {
System.out.println("服务器端运行。。。。。");
// 创建一个子线程
Thread thread = new Thread(() ->{
try (
// 创建DatagrameSocket对象,指定端口8080
DatagramSocket socket = new DatagramSocket(8080);
// 创建文件输出流,并创建缓冲输出流
FileOutputStream fout = new FileOutputStream("/Users/caizhengjie/Desktop/qq/e/56.txt");
BufferedOutputStream out = new BufferedOutputStream(fout);
){
// 准备一个缓冲区
byte[] buffer = new byte[1024];
// 循环接受数据报包
while (true){
// 创建数据报包对象,用来接收数据
DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
// 接收数据包
socket.receive(packet);
// 接收数据长度
int len = packet.getLength();
if(len == 3){
// 获得结束标志
String flag = new String(buffer,0,3);
// 判断结束标志,如果是bye,则结束接收
if (flag.equals("bye")){
break;
}
}
// 写入数据
out.write(buffer,0,3);
}
System.out.println("接收完成!");
} catch (SocketException | FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
});
// 启动线程
thread.start();
}
}
创建一个子线程,因为socket.receive(packet)方法会阻塞主线程了。
客户端UploadClient代码如下:
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
* @author : 蔡政洁
* @email :[email protected]
* @date : 2020/2/20
* @time : 4:31 下午
*/
//客户端
public class UploadCilent {
public static void main(String[] args) {
System.out.println("客户端运行。。。");
try(
// 创建DatagrameSocket对象,由系统分配可以使用的端口
DatagramSocket socket = new DatagramSocket();
FileInputStream fin = new FileInputStream("/Users/caizhengjie/Desktop/qq/123.txt");
BufferedInputStream in = new BufferedInputStream(fin);
){
// 创建远程主机IP地址对象
InetAddress address = InetAddress.getByName("localhost");
// 准备一个缓冲区
byte[] buffer = new byte[1024];
// 首次从文件流读数据
int len = in.read(buffer);
while (len != -1){
// 创建数据报包对象
DatagramPacket packet = new DatagramPacket(buffer,len,address,8080);
// 发送数据报包
socket.send(packet);
// 再次从文件流中读取数据
len = in.read(buffer);
}
// 创建数据报包对象
DatagramPacket packet = new DatagramPacket("bye".getBytes(),3,address,8080);
// 发送结束标志
socket.send(packet);
System.out.println("上传完成");
} catch (SocketException | FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
上述是上传文件客户端,发送数据不会堵塞线程,因此没有使用子线程。
四、案例:聊天工具
服务器端ChatServer代码如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
* @author : 蔡政洁
* @email :[email protected]
* @date : 2020/2/20
* @time : 7:57 下午
*/
//服务器端
public class ChatServer {
public static void main(String[] args) {
System.out.println("服务器端运行。。。。");
// 创建一个子线程
Thread thread = new Thread(() ->{
try (
// 创建DatagrameSocket对象,指定端口8080
DatagramSocket socket = new DatagramSocket(8080);
BufferedReader keyboardIn = new BufferedReader(new InputStreamReader(System.in));
){
while (true){
// 接收数据报
// 准备一个缓冲区
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
socket.receive(packet);
// 接收数据长度
int len = packet.getLength();
String str = new String(buffer,0,len);
// 打印接收的数据
System.out.printf("从客户端接收的数据:【%s】\n",str);
// 发送数据
// 从客户端传来的数据包中得到客户端的地址
InetAddress address = packet.getAddress();
// 从客户端传来的数据包中得到客户端端口号
int port = packet.getPort();
// 读取键盘输入的字符串
String keyboardInputString = keyboardIn.readLine();
// 读取键盘的字节数组
byte[] b = keyboardInputString.getBytes();
// 创建 DatagramPacket对象,用于客户端的发送数据
packet = new DatagramPacket(b,b.length,address,port);
// 向客户端发送数据
socket.send(packet);
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
});
// 启动线程
thread.start();
}
}
创建一个子线程,因为socket.receive(packet)方法会阻塞主线程了。
客户端ChatClient代码如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
* @author : 蔡政洁
* @email :[email protected]
* @date : 2020/2/20
* @time : 8:25 下午
*/
//客户端
public class ChatClient {
public static void main(String[] args) {
System.out.println("客户端运行。。。。");
// 创建一个子线程
Thread thread = new Thread(() ->{
try (
// 创建DatagrameSocket对象,由系统自动分配端口
DatagramSocket socket = new DatagramSocket();
BufferedReader keyboardIn = new BufferedReader(new InputStreamReader(System.in));
){
while (true){
// 发送数据
// 准备一个缓冲区
byte[] buffer = new byte[128];
// 服务器IP地址
InetAddress address = InetAddress.getByName("localhost");
// 服务器端口号
int port = 8080;
// 读取键盘输入的端口号
String keyboardInputString = keyboardIn.readLine();
// 退出循环,结束线程
if (keyboardInputString.equals("bye")){
break;
}
// 读取键盘输入的字节数组
byte[] b = keyboardInputString.getBytes();
// 创建 DatagramPacket对象,用于客户端的发送数据
DatagramPacket packet = new DatagramPacket(b,b.length,address,port);
// 发送
socket.send(packet);
// 接收数据报
packet = new DatagramPacket(buffer,buffer.length);
socket.receive(packet);
// 接受数据长度
int len = packet.getLength();
String str = new String(buffer,0,len);
// 打印接收数据
System.out.printf("从客户端接收的数据:【%s】\n",str);
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
});
// 启动线程
thread.start();
}
}
注意的是ChatClient可 以通过键盘输入bye,退出循环结束线程。
先运行服务器端,再运行客户端
运行结果:
以上内容仅供参考学习,如有侵权请联系我删除!
如果这篇文章对您有帮助,左下角的大拇指就是对博主最大的鼓励。
您的鼓励就是博主最大的动力!