UDP协议编程(接收端与发送端)
1.UDP协议原理图解:
2.UDP协议通讯的特点:
(1)之间无需建立连接通道(以数据包的形式发往目的地)
(2)属于不可靠协议
(3)无需建立连接通道,执行效率高
(4)发送数据大小有限
3.UDP程序操作步骤:
(1)发送数据包:
①使用DatagramSocket() 创建一个数据包套接字
②使用DatagramPacket(byte[] bys,int offset,int length,InetAddress address,int port) 创建要返送的数据包
③使用DatagramSocket类的send() 方法发送数据包
④关闭资源
(2)接受数据包:
①使用DatagramSocket(int port) 创建数据包套接字,绑定到指定的端口
②使用DatagramPacket(byte[] bys,int length) 创建字节数组来接受数据包(接收容器)
③使用DatagramPacket 类的receive() 方法接受UDP包
④解析数据包里面的内容,输出到控制台
⑤关闭资源
注:
(1)DatagramSocket 类的receive() 方法,如果还没有可以接受的数据,此方法会一直处于阻塞状态
(2)接收端不能多次运行,因为此端口号已经被占用了,不能再使用此端口号。否则会出现:BindException绑定异常(只用运行一次)
InetAddress
1.概述:此类表示互联网协议(IP)地址
注:此类没有构造方法。
如果一个类没有构造方法:则这个类里面的成员方法都是静态的,该类中的某些静态方法的返回值是该类本身
2.成员方法:
(1)public static InetAddress getByName(String host)在给定主机名的情况下确定主机的 IP 地址
注:此主机名可以是pc机器的计算机名称,也可以是IP地址的文本表现形式
(2)public String getHostName()获取此 IP 地址的主机名
注:如果此 InetAddress 是用主机名创建的,则记忆并返回主机名
(3)public String getHostAddress()返回 IP 地址字符串(以文本表现形式)
DatagramSocket
1.概述:此类用来表示发送和接受数据包的套接字
2.构造函数:
(1)DatagramSocket() 构造数据报套接字并将其绑定到本地主机上任何可用的端口
(2)DatagramSocket(int port) 创建数据报套接字并将其绑定到本地主机上的指定端口
(3)DatagramSocket(int port,InetAddress addr) 创建数据报套接字,将其绑定到指定的本地地址
3.成员方法:
(1)public void receive(DatagramPacket p)从此套接字接受数据包
注:此方法在接收到数据包前一直相当于阻塞状态
(2)public void send(DatagramPacket p)从此套接字发送数据包
DatagramPacket
DatagramPacket
,用来接收长度为 length
的数据包
length
的包发送到指定主机上的指定端口号
3.成员方法:
(1)public InetAddress getAddress() 返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的
(2)public byte[]getData()返回数据缓冲区
(3)public intgetLength()返回将要发送或接收到的数据的长度
例1:
// 发送端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Send {
public static void main(String[] args) throws IOException {
// (1)创建发送端DatagramSocket对象
DatagramSocket ds = new DatagramSocket();
// 发送的数据
String str = "我爱国际米兰";
byte [] bys = str.getBytes();
int len = bys.length;
// 创建InetAddress对象,获取IP地址
InetAddress address = InetAddress.getByName("192.168.1.108");
// 指定端口号
int port = 1234;
// (2)创建传输数据包对象
DatagramPacket dp = new DatagramPacket(bys,len,address,port);
// (3)调用DatagramSocket类里的send() 方法发送数据包
ds.send(dp);
// (4)释放资源
ds.close();
}
}
// 接收端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Receive {
public static void main(String[] args) throws IOException {
// (1)创建接收端DatagramSocket对象,并加入端口号
DatagramSocket ds = new DatagramSocket(1234);
// (2)创建数据包对象
byte [] bys = new byte [1024]; // 定义缓冲区
DatagramPacket dp = new DatagramPacket(bys,bys.length);
// (3)调用DatagramSocket中的receive() 方法
ds.receive(dp); // 阻塞式方法,在接收到数据前一直处于阻塞
// (4)读取数据
byte [] bys2 = dp.getData(); // 返回数据缓冲区
int len = dp.getLength(); // 返回将要发送或接收到的数据的长度
InetAddress address = dp.getAddress(); // 返回某台机器的IP地址
String ip = address.getHostAddress(); // 得到IP地址的文本形式
// 将字符串打印至控制台
String str = new String(bys2,0,len);
System.out.println(ip+":给你发送了:"+str);
}
}
结果:
例2:UDP的优化
//发送端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Send {
public static void main(String[] args) throws Exception {
// (1)创建发送端DatagramSocket对象
DatagramSocket ds = new DatagramSocket();
// (2)创建数据报包
byte[] bys = "我爱国际米兰".getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.1.108"), 1234);
// (3)将数据以数据报包的形式发出去
ds.send(dp);
// (4)关闭资源
ds.close();
}
}
// 接受端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Receive {
public static void main(String[] args) throws Exception {
// (1)创建接收端的DatagramSocket对象
DatagramSocket ds = new DatagramSocket(1234);
// (2)创建一个接收容器接收数据
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
// (3)接收数据
ds.receive(dp);
// (4)解析数据并显示在控制台
String ip = dp.getAddress().getHostAddress(); // 获取ip地址的字符串表现形式
// 获取缓存区中的实际数据
byte[] bys2 = dp.getData();
// 获取实际缓冲区数据的长度
int len = dp.getLength();
String str = new String(bys2, 0, len);
// 输出在控制台
System.out.println(ip + "发送的数据是:" + str);
// 关闭资源
ds.close();
}
}
例3:发送端使用键盘不停地录入数据,接收端不停的接收
// 发送端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Send {
public static void main(String[] args) throws IOException {
// 创建发送端的DatagramSocket对象
DatagramSocket ds = new DatagramSocket();
// 创建字符缓冲流读取键盘录入的数据
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 一次读取一行
String line = null;
while ((line = br.readLine()) != null) {
// 自定义结束条件
if ("再见".equals(line)) {
break;
}
// 创建数据报包
byte[] bys = line.getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.1.108"), 1234);
// 发送数据
ds.send(dp);
}
// 释放资源
ds.close();
}
}
// 接收端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Receive {
public static void main(String[] args) throws IOException {
// 创建接收端的Datagramsocket对象
DatagramSocket ds = new DatagramSocket(1234);
while (true) {
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
// 接收数据
ds.receive(dp);
// 获取ip字符串的表现形式
String ip = dp.getAddress().getHostAddress();
// 解析数据接收容器,获取里面实际的内容
byte[] bys2 = dp.getData();
int len = dp.getLength(); // 获取实际数据缓冲区实际长度
String s = new String(bys2, 0, len);
// 显示到控制台
System.out.println("from" + ip + "data is :" + s);
}
}
}
结果:
发送端:
接收端:
例4:使用多线程方法实现UDP协议编程(让发送端和接收端位于一个窗口下main)
// 测试类
import java.net.DatagramSocket;
import java.net.SocketException;
public class Test {
public static void main(String[] args) {
try {
// 创建发送端和接收端的Socket对象
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receiveSocket = new DatagramSocket(1234);
// 分别创建资源对象
SendThread st = new SendThread(sendSocket);
ReceiveThread rt = new ReceiveThread(receiveSocket);
// 创建线程类对象
Thread t1 = new Thread(st);
Thread t2 = new Thread(rt);
// 启动线程
t1.start();
t2.start();
} catch (SocketException e) {
e.printStackTrace();
}
}
}
// 发送端线程
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class SendThread implements Runnable {
// 定义一个变量
private DatagramSocket ds;
public SendThread(DatagramSocket ds) {
this.ds = ds;
}
@Override
public void run() {
try {
// 以标准IO流的方式读取键盘录入的数据
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while ((line = br.readLine()) != null) {
// 自定义结束条件
if ("over".equals(line)) {
break;
}
// 创建数据报包
byte[] bys = line.getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.1.108"), 1234);
// 发送数据
ds.send(dp);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放资源
if (ds != null) {
ds.close();
}
}
}
}
// 接收端线程
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ReceiveThread implements Runnable {
// 定义一个变量
private DatagramSocket ds;
public ReceiveThread(DatagramSocket ds) {
this.ds = ds;
}
@Override
public void run() {
try {
while (true) {
// 创建数据缓冲区
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
// 接收数据
ds.receive(dp);
// 获取ip字符串的表现形式
String ip = dp.getAddress().getHostAddress();
// 解析数据接收容器,获取里面实际的内容
byte[] bys2 = dp.getData();
int len = dp.getLength(); // 获取实际数据缓冲区实际长度
String str = new String(bys2, 0, len);
// 显示到控制台
System.out.println("from" + ip + "data is :" + str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
结果: