Java网络编程(三):UDP编程
- 本文涉及到的源码以及打包供参考:链接:https://pan.baidu.com/s/1CosT1-Y-G8USVcfSMOqXcw 提取码:4fci
@author:超爱学习的可琛同学
对于本博客你将学习:
- UDP与TCP基本概念
- 简单实现UDP编程
- 实战:老师学生一对一聊天室(运用多线程)
1.UDP与TCP基本概念
UDP | TCP |
---|---|
一种无连接的运输层协议,提供面向事务的简单不可靠信息传送服务 | 一种面向连接的、可靠的、基于字节流的传输层通信协议 |
非面向连接,传输不可靠,可能丢失,发送不管对方是否准备好,接收方收到也不确认可以广播发送非常简单的协议,开销小 | 面向连接,点到点通信,高可靠性,占用资源多,效率低 |
Socket 套接字 | |
---|---|
基于TCP协议的Socket编程 | 基于UDP协议的Socket编程 |
通信双方需要建立连接 | 通信双方无需建立连接 |
连接建立时双方存在主次之分 | 通信双方完全平等 |
举例:打电话 | 举例:QQ聊天 |
2.简单实现UDP编程
package edu.hue.jk;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
public class UdpSend {
/**
* 发送端
* 1.使用DatagramSocket 指定端口 创建发送
* 2.准备数据 一定要转为字节数组
* 3.封装成DatagramSocket包裹,需要指定目的地
* 4.发送包裹send(DatagramPacket p)
* 5.释放资源
* byte[] getData() //获取数据方法,返回字节数组
* @author 超爱学习的可琛同学.QQ1126140903
* @throws SocketException
*
*/
public static void main(String[] args) throws Exception {
System.out.println("我是发送方");
// 1.使用DatagramSocket 指定端口 创建发送
DatagramSocket sender = new DatagramSocket(5555);
// 2.准备数据 一定要转为字节数组
String data = "这是发送方发过来的一条数据!";
byte[] datas = data.getBytes();
// 3.封装成DatagramSocket包裹,需要指定目的地
DatagramPacket packet = new DatagramPacket(datas,0,datas.length,new InetSocketAddress("localhost",7777));
// 4.发送包裹send(DatagramPacket p)
sender.send(packet);
// 5.释放资源
sender.close();
}
}
package edu.hue.jk;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UdpReceiver {
/**
* 接收端
* 1.使用DatagramSocket 指定端口 创建接收端
* 2.准备容器 封装成DatagramSocket包裹
* 3.阻塞式接受包裹recive(DatagramPacket p)
* 4.分析数据
* 5.释放资源
* byte[] getData() //获取数据方法,返回字节数组
* @author Mr . Xu's PC
* @throws SocketException
*
*/
public static void main(String[] args) throws Exception {
System.out.println("我是接收端");
// 1.使用DatagramSocket 指定端口 创建接收端
DatagramSocket receiver = new DatagramSocket(7777);
// 2.准备容器 封装成DatagramSocket包裹
byte[] container = new byte[1024*1024];
DatagramPacket packet = new DatagramPacket(container, 0, container.length);
// 3.阻塞式接受包裹receive(DatagramPacket p)
receiver.receive(packet);
// 4.分析数据
String string = new String(packet.getData(), 0, packet.getData().length);
System.out.println(string);
// 5.释放资源
receiver.close();
}
}
2.1数据不是准备好的,而是IO流数据呢?
package edu.hue.jk;
import java.io.Serializable;
/**
* 这是一个javabean
* @author 超爱学习的可琛同学
*
*/
//注意 使用ObjectInputStream和ObjectOutputStream进行传输Object对象时 对象必须implements Serializable
public class Emp implements Serializable{
private String name;
private double salary;
public Emp() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Emp [name=" + name + ", salary=" + salary + "]";
}
public Emp(String name, double salary) {
super();
this.name = name;
this.salary = salary;
}
}
package edu.hue.jk;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import javax.naming.spi.DirObjectFactory;
/**
* 用UDP收发Object类型数据(引用类型)
* 1.使用DatagramSocket 指定端口 创建接收端
* 2.准备容器 封装成DatagramSocket包裹
* 3.阻塞式接受包裹recive(DatagramPacket p)
* 4.分析数据 将字节数组还原为对应的基本类型
* 5.释放资源
* byte[] getData() //获取数据方法,返回字节数组
*
* @author 超爱学习的可琛同学
*
*/
public class UdpObjectReceiver {
public static void main(String[] args) throws Exception {
System.out.println("我是Receiver。。。");
// 1.使用DatagramSocket 指定端口 创建接收端
DatagramSocket receiver = new DatagramSocket(9999);
// 2.准备容器 封装成DatagramSocket包裹
byte[] container = new byte[1024*1024];
DatagramPacket packet = new DatagramPacket(container, 0,container.length);
// 3.阻塞式接受包裹recive(DatagramPacket p)
receiver.receive(packet);
// 4.分析数据 将字节数组还原为对应的基本类型
ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(container)));
String msg = ois.readUTF();
int age = ois.readInt();
boolean flag = ois.readBoolean();
char ch = ois.readChar();
//object 注意发送和接受顺序要一致
Object str = ois.readObject();
Object date = ois.readObject();
Object emp = ois.readObject();
System.out.println(msg+"--"+age+"--"+flag+"--"+ch+"--"+str+"--"+date+"--"+emp);
//I'm sender,I'M happy--20--true--G--I'm Coding --Tue Jul 16 10:46:07 CST 2019--Emp [name=超爱学习的可琛同学, salary=10000.0]
// 5.释放资源
}
}
package edu.hue.jk;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.Date;
/**
* 用UDP收发基本数据类型
* 1.使用DatagramSocket 指定端口 创建发送
* 2.将基本类型转为字节数组
* 3.封装成DatagramSocket包裹,需要指定目的地
* 4.发送包裹send(DatagramPacket p)
* 5.释放资源
* @author Mr . Xu's PC
*
*/
public class UdpObjectSender {
public static void main(String[] args) throws Exception {
System.out.println("我是发送方。。。");
// 1.使用DatagramSocket 指定端口 创建发送
DatagramSocket sender = new DatagramSocket(8888);
// 2.将基本类型转为字节数组
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(baos));
//操作数据类型+数据
oos.writeUTF("I'm sender,I'M happy");
oos.writeInt(20);
oos.writeBoolean(true);
oos.writeChar('G');
oos.flush();
//对象
oos.writeObject("I'm Coding ");
oos.writeObject(new Date());
Emp emp = new Emp("超爱学习的可琛同学",10000);
oos.writeObject(emp);
oos.flush();
byte[] datas = baos.toByteArray();
// 3.封装成DatagramSocket包裹,需要指定目的地
DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("localhost", 9999));
// 4.发送包裹send(DatagramPacket p)
sender.send(packet);
// 5.释放资源
sender.close();
}
}
2.3 那传输对象是 图片或者是其他文件呢
package edu.hue.jk;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* 这是我手写封装的一个工具类,里面有两个静态方法
* public static byte[] fileToByteArray(String filePath);//传入图片路径 返回字节数组
* public static void byteArrayToFile(byte[] src,String filePath);传入字节数组和图片另存为的路径
*1、 图片读取到字节数组
*2、 字节数组写出到文件
* @author 超爱学习的可琛同学. QQ1126140903
*
*/
public class IOUtils {
/**
* 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; //接收长度
while((len=is.read(flush))!=-1) {
baos.write(flush,0,len); //写出到字节数组中
}
baos.flush();
return baos.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException 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 {
//4、释放资源
try {
if (null != os) {
os.close();
}
} catch (Exception e) {
}
}
}
}
package edu.hue.jk;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import javax.naming.spi.DirObjectFactory;
/**
* 用UDP收文件
* 1.使用DatagramSocket 指定端口 创建接收端
* 2.准备容器 封装成DatagramSocket包裹
* 3.阻塞式接受包裹recive(DatagramPacket p)
* 4.分析数据 将字节数组还原为文件
* 5.释放资源
* byte[] getData() //获取数据方法,返回字节数组
*
* @author 超爱学习的可琛同学
*
*/
public class UdpFileReceiver {
public static void main(String[] args) throws Exception {
System.out.println("我是Receiver。。。");
// 1.使用DatagramSocket 指定端口 创建接收端
DatagramSocket receiver = new DatagramSocket(9999);
// 2.准备容器 封装成DatagramSocket包裹
byte[] container = new byte[1024*1024];
DatagramPacket packet = new DatagramPacket(container, 0,container.length);
// 3.阻塞式接受包裹recive(DatagramPacket p)
receiver.receive(packet);
// 4.分析数据 将字节数组还原为文件
IOUtils.byteArrayToFile(container, "etc/UdpCreate.png");
// 5.释放资源
receiver.close();
}
}
package edu.hue.jk;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
/**
* 用UDP发文件
* 1.使用DatagramSocket 指定端口 创建发送
* 2.将文件转为字节数组
* 3.封装成DatagramSocket包裹,需要指定目的地
* 4.发送包裹send(DatagramPacket p)
* 5.释放资源
* @author 超爱学习的可琛同学
*
*/
public class UdpFileSender {
public static void main(String[] args) throws Exception {
System.out.println("我是发送方。。。");
// 1.使用DatagramSocket 指定端口 创建发送
DatagramSocket sender = new DatagramSocket(8888);
// 2.将文件转为字节数组
byte[] datas =IOUtils.fileToByteArray("etc/logo.png");
// 3.封装成DatagramSocket包裹,需要指定目的地
DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("localhost", 9999));
// 4.发送包裹send(DatagramPacket p)
sender.send(packet);
// 5.释放资源
sender.close();
}
}
logo.png为原始图片
UdpCreate.png为Udp收发创建的文件
3.实战:老师学生一对一聊天室(运用多线程)
package edu.hue.jk;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* 多线程接收器
* @author 超爱学习的可琛同学
*
*/
public class UdpThreadReceiver implements Runnable{
private DatagramSocket receiver;
private String from;
public UdpThreadReceiver(int port,String from) {
this.from = from;
// 1.使用DatagramSocket 指定端口 创建接收端
try {
receiver = new DatagramSocket(port);
} catch (SocketException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
@Override
public void run() {
while(true) {
byte[] container = new byte[1024*60];
DatagramPacket packet = new DatagramPacket(container, 0,container.length);
// 3.阻塞式接受包裹receive(DatagramPacket p)
try {
receiver.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("exit")) {
break;
}
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}
package edu.hue.jk;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
/**
* 多线程发送器
* @author 超爱学习的可琛同学
*
*/
public class UdpThreadSender implements Runnable{
private DatagramSocket sender ;
private String toIp;
private int toPort;
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line;
public UdpThreadSender(int port,String toIp,int toPort) {
this.toIp = toIp;
this.toPort = toPort;
// 1.使用DatagramSocket 指定端口 创建发送
try {
sender = new DatagramSocket(port);
} catch (SocketException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
@Override
public void run() {
while(true) {
try {
line = reader.readLine();
byte[] datas = line.getBytes();
// 3.封装成DatagramSocket包裹,需要指定目的地
DatagramPacket packet = new DatagramPacket(datas, 0,datas.length, new InetSocketAddress(toIp,toPort));
// 4.发送包裹send(DatagramPacket p)
sender.send(packet);
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
if(line.equals("exit")) {
break;
}
}
}
}
package edu.hue.jk;
/**
* 老师端
* 开启两条线程,一条收消息,一条发消息
* @author Mr . Xu's PC
*
*/
public class UdpThreadTeacher {
public static void main(String[] args) {
System.out.println("老师端系统");
new Thread(new UdpThreadReceiver(8888,"学生")).start();;//老师端接收
new Thread(new UdpThreadSender(5545, "localhost", 5555)).start();//老师端发送
}
}
package edu.hue.jk;
/**
* 这是学生端
* 开启两条线程:一条发消息,一条收消息
* @author Mr . Xu's PC
*
*/
public class UdpThreadStudent {
public static void main(String[] args) {
System.out.println("学生端系统");
new Thread(new UdpThreadSender(9999, "localhost", 8888)).start();//学生端发送
new Thread(new UdpThreadReceiver(5555,"老师")).start();//学生端接受
}
}
测试:老师学生一对一咨询聊天室