一.UDP协议
UDP协议是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。
二.UDP通信用到的类
1.DatagramPacket
用于封装UDP通信中传输或者要接收的数据,而在发送端和接收端进行创建DatagramPacket对象时,使用的构造方法不同。在进行数据接收时,只需要定义一个字节数组存放接收的数据,并指定这个数组的长度即可;而在进行数据发送时,不仅要指定一个字节数组存放发送的数据,定义它的长度,还要指定接收端的IP地址和接收的端口号。示例如下:
//定义接收端的DatagramPacket对象
byte[] buffer = new byte[1024];
DatagramPacket dp = new DatagramPacket(buffer, 1024);
//定义发送端的DatagramPacket对象
byte[] buffer = "hello".getBytes();
DatagramPacket sendPocket =
new DatagramPacket(buffer, buffer.length,InetAddress.getByName("192.168.1.105"), 12306);
DatagramPacket对象中的常用方法
//获得发送者的ip地址
InetAddress ip = dp.getAddress();
//获得发送者的端口号
int port = dp.getPort();
//获得接收的数据
byte[] data = dp.getData();
//获得数据长度
int length = dp.getLength();
String dataStr = new String(data,0,length);
2.DatagramSocket
我们在上面已经将数据封装好了,但是数据不能自己从发送端发送到接收端,因此就需要一个对象将DatagramPocket数据包进行传输,这个对象就是DatagramSocket。在接收端定义时必须要定义端口号,这样发送端才能根据端口号找到接收端。而在发送端定义时则不需要,因为发送端不需要接收数据,通过系统默认分配一个端口号即可。
//接收端必须要指定自己的端口号
DatagramSocket recieveSocket = new DatagramSocket(9999);
//发送端不需要指定端口号,通过系统默认分配即可
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket对象中的常用方法
既然我们已经知道了,DatagramSocket是用来传输和接收数据包的,那么它最主要的方法就是如下两个发送和接收数据的方法。
//发送端将数据包发送出去
sendSocket.send(sendPocket);
//接收端对数据进行接收
recieveSocket.receive(dp);
三.一个完整的UDP通信过程代码
上面我们已经了解了UDP通信过程中需要用的类和方法,那么一个完整的UDP通信过程就很好理解了,代码如下
发送端:
/**
* 1.定义一个DatagramSocket对数据包进行发送,不需要指定端口号
* 2.定义一个DatagramPacket封装数据,里面有四个参数
* 2.1要传输的字节数组
* 2.2字节数组的长度
* 2.3要传输的目的IP地址
* 2.4要传输的目的端口号
* 3.发送数据
* 4.释放流资源
*/
public class UDPSender {
public static void main(String[] args) throws IOException {
//1.创建一个ds发送数据,不需要指定端口号
DatagramSocket ds = new DatagramSocket();
//2.创建一个dp封装数据
byte[] bytes = "Hello UDP".getBytes();
//InetAddress.getByName()方法可以通过域名映射到ip地址
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("192.168.43.104"), 9999);
//3.发送数据
ds.send(dp);
//4.释放流资源
ds.close();
}
}
接收端:
/**
* 1.定义一个DatagramSocket用于接收数据数据包,要指定自己的端口号
* 2.定义一个DatagramPacket用于封装接收的数据包,定义下面的两个参数
* 2.1要接受的字节数组
* 2.2字节数组的长度
* 3.处理接收到的数据
* 4.释放流资源
*/
public class UDPReciever {
public static void main(String[] args) throws Exception {
/*
*1.定义一个DatagramSocket接收数据包,指定端口号,这个端口号,要和
* 发送端定义的端口号一致,这样才能进行联系。
*/
DatagramSocket ds = new DatagramSocket(9999);
//2.定义一个DatagramPacket封装接收来的数据包
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
ds.receive(dp);
//3.处理封装好的数据包
int port = dp.getPort(); //发送端的端口号,我们没有指定,系统自动分配的。
byte[] data = dp.getData();//发送端发送过来的数据
InetAddress address = dp.getAddress();//发送端的IP地址
int length = dp.getLength();//数据长度
String str = new String(data, 0, length);
System.out.println(port+"\t"+address+"\t"+str);
//4.释放流资源
ds.close();
}
}
四.多线程聊天室案例
客户端:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class ChatRoomClient implements Runnable {
//发送数据时 要知道ip地址和端口号
private String ip;
private int port;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
@Override
public void run() {
DatagramSocket ds = null;
try {
ds = new DatagramSocket();
System.out.println("请输入您要发送的信息");
while (true){
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
byte[] bytes = s.getBytes();
int length = bytes.length;
DatagramPacket dp = new DatagramPacket(bytes, length, InetAddress.getByName(ip), port);
ds.send(dp);
}
}catch (Exception e ){
e.printStackTrace();
}finally {
if (ds!=null){
ds.close();
}
}
}
}
服务器:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class ChatRoomServer implements Runnable {
private int port;
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
@Override
public void run() {
DatagramSocket ds = null;
try {
ds = new DatagramSocket(port);
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
System.out.println("服务端已经启动,坐等接收数据");
while (true){
ds.receive(dp);
InetAddress address = dp.getAddress();
byte[] data = dp.getData();
int length = dp.getLength();
String s = new String(data, 0, length);
if("exit".equals(s)){
System.out.println("数据接收完毕");
break;
}
System.out.println("服务端"+address+"接受了"+s);
System.out.println("再次输入你要发送的数据");
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (ds!=null){
ds.close();
}
}
}
}
两个Main方法,代表两个用户:
public class ChatRoomMain {
public static void main(String[] args) {
ChatRoomServer crs = new ChatRoomServer();
crs.setPort(10086);
ChatRoomClient crc = new ChatRoomClient();
crc.setIp("192.168.43.104");
crc.setPort(10087);
Thread t1 = new Thread(crs);
Thread t2 = new Thread(crc);
t1.start();
t2.start();
}
}
/*********************************************************/
public class ChatRoomMain2 {
public static void main(String[] args) {
ChatRoomServer crs = new ChatRoomServer();
crs.setPort(10087);
ChatRoomClient crc = new ChatRoomClient();
crc.setIp("192.168.43.104");
crc.setPort(10086);
Thread t1 = new Thread(crs);
Thread t2 = new Thread(crc);
t1.start();
t2.start();
}
}