Java网络编程—UDP
传输层协议:TCP/UDP
TCP:安全的,面向连接的
UPD:不安全的,非面向连接的(比较高效)
UDP传输协议:一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务
特点:
非面向连接,传输不可靠,可能丢失
发送不管对方是否准备好,接收方收到也不确认
可以广播发送
非常简单的协议,开销小
UDP操作字节数组(字节数组与包裹打交道)
通过封装成包—>拆包—>封包—>拆包
DatagramSocket:用于发送或接收数据报到套接字
DatagramPacket:数据包
在应用层通过Socket 使用传输层(TCP/UDP)的服务
UDP 基本流程 代码示例
代码示例:
/*
* 基本流程:发送端
* 1、使用DatagramSocket 指定端口 创建发送端
* 2、准备数据一定转成字节数组
* 3、封装成DatagramPacket 包裹,需要指定目的地(IP和端口)
* 4、发送包裹send(DatagramPacket p)
* 5、释放资源
*
* DatagramSocket:用于发送或接收数据报到套接字
* DatagramPacket:数据包
*/
public class UdpClient {
public static void main(String[] args) throws Exception {
System.out.println("发送方启动中...");
// 1、使用DatagramSocket 指定端口 创建发送端
DatagramSocket client = new DatagramSocket(8888);
// 2、准备数据一定转成字节数组
String data = "Java网络编程";
byte[] datas = data.getBytes();
// 3、封装成DatagramPacket 包裹,需要指定目的地
DatagramPacket packet = new DatagramPacket(datas,0,datas.length,new InetSocketAddress("localhost", 9999));
// 4、发送包裹send(DatagramPacket p)
client.send(packet);
// 5、释放资源
client.close();
}
}
/*
* 基本流程:接收端
* Address already in use: Cannot bind 同一个协议下端口不允许冲突
* 1、使用DatagramSocket 指定端口 创建接收端
* 2、准备容器 封装成DatagramPacket 包裹
* 3、阻塞式接收包裹receive(DatagramPacket p)
* 4、分析数据
* byte[] getData()
* getLength()
* 5、释放资源
*
* DatagramSocket:用于发送或接收数据报到套接字
* DatagramPacket:数据包
*/
public class UdpServer {
public static void main(String[] args) throws Exception {
System.out.println("接收方启动中...");
// 1、使用DatagramSocket 指定端口 创建接收端
DatagramSocket server = new DatagramSocket(9999);
// 2、准备容器 封装成DatagramPacket 包裹
byte[] container = new byte[1024*60];
DatagramPacket packet = new DatagramPacket(container,0,container.length);
// 3、阻塞式接收包裹receive(DatagramPacket p)
server.receive(packet);//阻塞式
// 4、分析数据
// byte[] getData()
// getLength()
byte[] datas = packet.getData();
int len = packet.getLength();
System.out.println(new String(datas,0,len));
// 5、释放资源
server.close();
}
}
UDP 基本类型 代码示例
代码示例:
/*
* 基本类型:发送端
* 1、使用DatagramSocket 指定端口 创建发送端
* 2、将基本类型 转成字节数组
* 3、封装成DatagramPacket 包裹,需要指定目的地
* 4、发送包裹send(DatagramPacket p)
* 5、释放资源
*
* DatagramSocket:用于发送或接收数据报到套接字
* DatagramPacket:数据包
*/
public class UdpTypeClient {
public static void main(String[] args) throws Exception {
System.out.println("发送方启动中...");
// 1、使用DatagramSocket 指定端口 创建发送端
DatagramSocket client = new DatagramSocket(8888);
// 2、将基本类型 转成字节数组
// 写出
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos));
// 操作数据类型 + 数据
dos.writeUTF("编码辛酸泪");
dos.writeInt(18);
dos.writeBoolean(false);
dos.writeChar('a');
dos.flush();
byte[] datas = baos.toByteArray();
// 3、封装成DatagramPacket 包裹,需要指定目的地
DatagramPacket packet = new DatagramPacket(datas,0,datas.length,new InetSocketAddress("localhost", 9999));
// 4、发送包裹send(DatagramPacket p)
client.send(packet);
// 5、释放资源
client.close();
}
}
/*
* 基本类型:接收端
* Address already in use: Cannot bind 同一个协议下端口不允许冲突
* 1、使用DatagramSocket 指定端口 创建接收端
* 2、准备容器 封装成DatagramPacket 包裹
* 3、阻塞式接收包裹receive(DatagramPacket p)
* 4、分析数据 将字节数组还原为对应的类型
* byte[] getData()
* getLength()
* 5、释放资源
*
* DatagramSocket:用于发送或接收数据报到套接字
* DatagramPacket:数据包
*/
public class UdpTypeServer {
public static void main(String[] args) throws Exception {
System.out.println("接收方启动中...");
// 1、使用DatagramSocket 指定端口 创建接收端
DatagramSocket server = new DatagramSocket(9999);
// 2、准备容器 封装成DatagramPacket 包裹
byte[] container = new byte[1024*60];
DatagramPacket packet = new DatagramPacket(container,0,container.length);
// 3、阻塞式接收包裹receive(DatagramPacket p)
server.receive(packet);//阻塞式
// 4、分析数据 将字节数组还原为对应的类型
// byte[] getData()
// getLength()
byte[] datas = packet.getData();
int len = packet.getLength();
// 读取
DataInputStream dis = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
// 顺序与写出一致
String msg = dis.readUTF();
int age = dis.readInt();
boolean flag = dis.readBoolean();
char ch = dis.readChar();
System.out.println(msg+"-->"+flag);
// 5、释放资源
server.close();
}
}
UDP 引用类型 代码示例
代码示例:
/*
* 引用类型:发送端
* 1、使用DatagramSocket 指定端口 创建发送端
* 2、将基本类型 转成字节数组
* 3、封装成DatagramPacket 包裹,需要指定目的地
* 4、发送包裹send(DatagramPacket p)
* 5、释放资源
*
* DatagramSocket:用于发送或接收数据报到套接字
* DatagramPacket:数据包
*/
public class UdpObjClient {
public static void main(String[] args) throws Exception {
System.out.println("发送方启动中...");
// 1、使用DatagramSocket 指定端口 创建发送端
DatagramSocket client = new DatagramSocket(8888);
// 2、将基本类型 转成字节数组
// 写出
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(baos));
//操作数据类型 + 数据
oos.writeUTF("编码辛酸泪");
oos.writeInt(18);
oos.writeBoolean(false);
oos.writeChar('a');
// 对象
oos.writeObject("谁解其中味");
oos.writeObject(new Date());
Employee emp = new Employee("马云",400);
oos.writeObject(emp);
oos.flush();
byte[] datas = baos.toByteArray();
// 3、封装成DatagramPacket 包裹,需要指定目的地
DatagramPacket packet = new DatagramPacket(datas,0,datas.length,new InetSocketAddress("localhost", 9999));
// 4、发送包裹send(DatagramPacket p)
client.send(packet);
// 5、释放资源
client.close();
}
}
/*
* 引用类型:接收端
* Address already in use: Cannot bind 同一个协议下端口不允许冲突
* 1、使用DatagramSocket 指定端口 创建接收端
* 2、准备容器 封装成DatagramPacket 包裹
* 3、阻塞式接收包裹receive(DatagramPacket p)
* 4、分析数据 将字节数组还原为对应的类型
* byte[] getData()
* getLength()
* 5、释放资源
*
* DatagramSocket:用于发送或接收数据报到套接字
* DatagramPacket:数据包
*/
public class UdpObjServer {
public static void main(String[] args) throws Exception {
System.out.println("接收方启动中...");
// 1、使用DatagramSocket 指定端口 创建接收端
DatagramSocket server = new DatagramSocket(9999);
// 2、准备容器 封装成DatagramPacket 包裹
byte[] container = new byte[1024*60];
DatagramPacket packet = new DatagramPacket(container,0,container.length);
// 3、阻塞式接收包裹receive(DatagramPacket p)
server.receive(packet);//阻塞式
// 4、分析数据 将字节数组还原为对应的类型
// byte[] getData()
// getLength()
byte[] datas = packet.getData();
int len = packet.getLength();
// 读取 -->反序列化
ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
//顺序与写出一致
String msg = ois.readUTF();
int age = ois.readInt();
boolean flag = ois.readBoolean();
char ch = ois.readChar();
System.out.println(flag);
//对象的数据还原
Object str = ois.readObject();
Object data = ois.readObject();
Object employee = ois.readObject();
if (str instanceof String) {
String strObj = (String) str;
System.out.println(strObj);
}
if (data instanceof Data) {
Data dateObj = (Data) data;
System.out.println(dateObj);
}
if (employee instanceof Employee) {
Employee empObj = (Employee) employee;
System.out.println(empObj.getName()+"-->");
}
// 5、释放资源
server.close();
}
}
//Javabean 封装数据
public class Employee implements Serializable{
private transient String name; // 该数据不需要序列化
private double salary;
public Employee() {
}
public Employee(String name,double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
}
}
UDP 文件上传 代码示例
代码示例:
/*
* 文件上传:发送端
* 1、使用DatagramSocket 指定端口 创建发送端
* 2、将文件 转成字节数组
* 3、封装成DatagramPacket 包裹,需要指定目的地
* 4、发送包裹send(DatagramPacket p)
* 5、释放资源
*
* DatagramSocket:用于发送或接收数据报到套接字
* DatagramPacket:数据包
*/
public class UdpFileClient {
public static void main(String[] args) throws Exception {
System.out.println("发送方启动中...");
// 1、使用DatagramSocket 指定端口 创建发送端
DatagramSocket client = new DatagramSocket(8888);
// 2、将文件 转成字节数组
// 写出
byte[] datas = IOUtils.fileToByteArray("p.png");
// 3、封装成DatagramPacket 包裹,需要指定目的地
DatagramPacket packet = new DatagramPacket(datas,0,datas.length,new InetSocketAddress("localhost", 9999));
// 4、发送包裹send(DatagramPacket p)
client.send(packet);
// 5、释放资源
client.close();
}
}
/*
* 文件存储:接收端
* Address already in use: Cannot bind 同一个协议下端口不允许冲突
* 1、使用DatagramSocket 指定端口 创建接收端
* 2、准备容器 封装成DatagramPacket 包裹
* 3、阻塞式接收包裹receive(DatagramPacket p)
* 4、分析数据 将字节数组还原为对应的类型
* byte[] getData()
* getLength()
* 5、释放资源
*
* DatagramSocket:用于发送或接收数据报到套接字
* DatagramPacket:数据包
*/
public class UdpFileServer {
public static void main(String[] args) throws Exception {
System.out.println("接收方启动中...");
// 1、使用DatagramSocket 指定端口 创建接收端
DatagramSocket server = new DatagramSocket(9999);
// 2、准备容器 封装成DatagramPacket 包裹
byte[] container = new byte[1024*60];
DatagramPacket packet = new DatagramPacket(container,0,container.length);
// 3、阻塞式接收包裹receive(DatagramPacket p)
server.receive(packet);//阻塞式
// 4、分析数据 将字节数组还原为文件
// byte[] getData()
// getLength()
byte[] datas = packet.getData();
int len = packet.getLength();
// 读取
// datas为数据
IOUtils.byteArrayToFile(datas, "src/copy.png");
// 5、释放资源
server.close();
}
}
/*
* 1、图片读取到字节数组中
* 2、字节数组写出到文件(图片)
*/
public class IOUtils {
public static void main(String[] args) {
// 图片转成字节数组
byte[] datas = fileToByteArray("E:/gongfang/JavaDemo/linweimao/javaWork/Demo/IO_study02/src/com/lwm/io/p.png");
System.out.println(datas.length);
byteArrayToFile(datas, "p-byte.png");
}
/*
* 1、图片读取到字节数组中
* 1)、图片到程序 FileInputStream
* 2)、程序到字节数组 ByteArrayOutputStream
*/
public static byte[] fileToByteArray(String filePath) {
// 1、创建源与目的地
File src = new File(filePath);
byte[] dest = null;
// 2、选择流
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
is = new FileInputStream(src);
baos = new ByteArrayOutputStream();
// 3、操作(分段读取)
byte[] flush = new byte[1024 * 10];// 缓冲容器
int len = -1;// 接收长度
try {
while ((len = is.read(flush)) != -1) {
// 写出到字节数组中
baos.write(flush,0,len);
}
baos.flush();
return baos.toByteArray();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
// 4、释放资源
try {
if (null != is) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/*
* 2、字节数组写出到文件(图片)
* 1)、字节数组读取到程序 ByteArrayInputStream
* 2)、程序到文件 FileOutputStream
*/
public static void byteArrayToFile(byte[] src,String filePath) {
// 1、创建源
File dest = new File(filePath);
// 2、选择流
InputStream is = null;
OutputStream os = null;
try {
is = new ByteArrayInputStream(src);
os = new FileOutputStream(dest);
// 3、操作(分段读取)
byte[] flush = new byte[5];// 缓冲容器
int len = -1;// 接收长度
while ((len = is.read(flush)) != -1) {
os.write(flush,0,len);
}
os.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 释放资源
try {
if (null != os) {
os.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
UDP 多次交流 代码示例
// 能够发送多次
// 只能发送端说话,接收端接收(接收端说不了话)
代码示例:
// 能够发送多次
// 只能发送端说话,接收端接收(接收端说不了话)
/*
* 多次交流:发送端
* 1、使用DatagramSocket 指定端口 创建发送端
* 2、准备数据一定转成字节数组
* 3、封装成DatagramPacket 包裹,需要指定目的地
* 4、发送包裹send(DatagramPacket p)
* 5、释放资源
*
* DatagramSocket:用于发送或接收数据报到套接字
* DatagramPacket:数据包
*/
public class UdpTalkClient {
public static void main(String[] args) throws Exception {
System.out.println("发送方启动中...");
// 1、使用DatagramSocket 指定端口 创建发送端
DatagramSocket client = new DatagramSocket(8888);
// 2、准备数据一定转成字节数组
// 用控制台进行交流
// BufferedReader为字符流 System.in 为字节流 所以要转换(使用 InputStreamReader)
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String data = reader.readLine();
byte[] datas = data.getBytes();
// 3、封装成DatagramPacket 包裹,需要指定目的地
DatagramPacket packet = new DatagramPacket(datas, 0, datas.length,
new InetSocketAddress("localhost", 9999));
// 4、发送包裹send(DatagramPacket p)
client.send(packet);
if (data.equals("bye")) {
break;
}
}
// 5、释放资源
client.close();
}
}
//能够接收多次
//只能发送端说话,接收端接收(接收端说不了话)
/*
* 多次交流:接收端
* Address already in use: Cannot bind 同一个协议下端口不允许冲突
* 1、使用DatagramSocket 指定端口 创建接收端
* 2、准备容器 封装成DatagramPacket 包裹
* 3、阻塞式接收包裹receive(DatagramPacket p)
* 4、分析数据
* byte[] getData()
* getLength()
* 5、释放资源
*
* DatagramSocket:用于发送或接收数据报到套接字
* DatagramPacket:数据包
*/
public class UdpTalkServer {
public static void main(String[] args) throws Exception {
System.out.println("接收方启动中...");
// 1、使用DatagramSocket 指定端口 创建接收端
DatagramSocket server = new DatagramSocket(9999);
while (true) {
// 准备不同的容器进行接收控制台的输入
// 2、准备容器 封装成DatagramPacket 包裹
byte[] container = new byte[1024*60];
DatagramPacket packet = new DatagramPacket(container,0,container.length);
// 3、阻塞式接收包裹receive(DatagramPacket p)
server.receive(packet);//阻塞式
// 4、分析数据
// byte[] getData()
// getLength()
byte[] datas = packet.getData();
int len = packet.getLength();
String data = new String(datas,0,len);
System.out.println(data);
if (data.equals("bye")) {
break;
}
}
// 5、释放资源
server.close();
}
}
UDP 多次交流,加入多线程,实现双向交流 模拟在线咨询
代码示例:
/*
* 接收端:使用面向对象封装
* 使用面向对象封装:加入属性、加入方法、加入构造器,然后在玩类和类的关系(接口、继承)
*/
public class TalkReceive implements Runnable{
private DatagramSocket server;
private String from;
public TalkReceive(int port,String from) {
this.from = from;
try {
server = new DatagramSocket(port);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true) {
// 准备不同的容器进行接收控制台的输入
// 2、准备容器 封装成DatagramPacket 包裹
byte[] container = new byte[1024*60];
DatagramPacket packet = new DatagramPacket(container,0,container.length);
// 3、阻塞式接收包裹receive(DatagramPacket p)
try {
server.receive(packet); //阻塞式
// 4、分析数据
byte[] datas = packet.getData();
int len = packet.getLength();
String data = new String(datas,0,len);
System.out.println(from+":"+data);
if (data.equals("bye")) {
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 5、释放资源
server.close();
}
}
/*
* 发送端:使用面向对象封装
* 使用面向对象封装:加入属性、加入方法、加入构造器,然后在玩类和类的关系(接口、继承)
*/
public class TalkSend implements Runnable{
private DatagramSocket client;
private BufferedReader reader;
private String toIP;
private int toPort;
public TalkSend(int port,String toIP,int toPort) {
this.toIP = toIP;
this.toPort = toPort;
try {
client = new DatagramSocket(port);
reader = new BufferedReader(new InputStreamReader(System.in));
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true) {
String data;
try {
data = reader.readLine();
byte[] datas = data.getBytes();
// 3、封装成DatagramPacket 包裹,需要指定目的地
DatagramPacket packet = new DatagramPacket(datas, 0, datas.length,
new InetSocketAddress(this.toIP, this.toPort));
// 4、发送包裹send(DatagramPacket p)
client.send(packet);
if (data.equals("bye")) {
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 5、释放资源
client.close();
}
}
/*
* 加入多线程,实现双向交流 模拟在线咨询
*/
public class TalkTeacher {
public static void main(String[] args) {
new Thread(new TalkReceive(9999,"学生")).start();// 接收
new Thread(new TalkSend(5555,"localhost",8888)).start();// 发送
}
}
/*
* 加入多线程,实现双向交流 模拟在线咨询
*/
public class TalkStudent {
public static void main(String[] args) {
new Thread(new TalkSend(7777,"localhost",9999)).start();// 发送
new Thread(new TalkReceive(8888,"老师")).start();// 接收
}
}