前言
在了解了TCP协议中java代码TCP协议中java代码的相关编写后,今天我们继续了解UDP协议
1.UDP: 发短信
[1] 不连接,不稳定
[2] 客户端与服务端没有明确界限
[3] 不管有没有准备好,都可以发送给你
(就好比导弹攻击;DDOS,洪水攻击)
1.1 我们下面先来模拟一个单方面的接收和单方面发送的例子
发送端
package com.gs.lesson03;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
//不需要连接服务器
public class UdpClientDemo01 {
public static void main(String[] args) throws Exception {
// 1.建立一个Socket
DatagramSocket socket = new DatagramSocket();
// 2.建个包
String msg = "你好啊,服务器!";
//2.1发送给谁(IP地址+端口号)
InetAddress localhost = InetAddress.getByName("localhost");
int port = 9090;
//数据 数据的起始位置 数据的结束位置 IP地址 端口号
DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, port);
//3. 发送包
socket.send(packet);
//4.关闭流
socket.close();
}
}
接收端
package gs.lesson03;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
//还是要等待客户端连接
public class UdpServerDemo01 {
public static void main(String[] args) throws Exception {
//1.开放端口
DatagramSocket socket = new DatagramSocket(9090);
//2.接收数据包
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
//阻塞(就是等待接收)
socket.receive(packet);
System.out.println(packet.getAddress().getHostAddress());
System.out.println(new String(packet.getData(),0,packet.getLength()));
//3.关闭连接
socket.close();
}
}
1.2 上面的例子显然只是一个入门,因为他只能接收一条信息;那么我们如何实现接收多条信息,并在适当的时候断开连接呢?
下面我们模拟一个接收方可以接收多条信息,并在接收到bye这个命令后就断开连接。
发送方
package gs.chat;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class UdpSenderDemo01 {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(8888);
//准备数据,控制台取 System.in
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while(true){
String data = reader.readLine();
byte[] datas = data.getBytes();
DatagramPacket packet = new DatagramPacket(datas,0,datas.length,new InetSocketAddress("localhost",6666));
//发送数据包
socket.send(packet);
//当接收到 bye时退出
if(data.startsWith("bye")){
break;
}
}
socket.close();
}
}
接收方
package gs.chat;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UdpReceiverDemo01 {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(6666);
while (true){
// 准备接收包裹
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container,0,container.length);
// 阻塞式接收包裹
socket.receive(packet);
// 当收到 bye时断开连接
byte[] data = packet.getData();
String receiveData = new String(data,0,data.length);
System.out.println(receiveData);
if(receiveData.startsWith("bye")){
break;
}
}
socket.close();
}
}
1.3 上面的例子还是显然不足的,我们还是没有实现真正的通信,也就是你说一句,我能回一句的那种。那我们该如何实现呢?
这里我们可以开启两个线程进行模拟,这两个线程分别代表接收方和发送方。也就是每个人都是接受信息的人,也是发送信息的人。
[1] 编写接受方线程
package gs.chat;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class TalkReceiver implements Runnable {
DatagramSocket socket = null;
//接收方的端口
private int port;
//信息来自谁
private String msgFrom;
public TalkReceiver(int port,String msgFrom) {
this.port = port;
this.msgFrom = msgFrom;
try {
//新建数据报连接
socket = new DatagramSocket(port);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true){
try {
// 准备接收包裹
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container,0,container.length);
// 阻塞式接收包裹
socket.receive(packet);
// 当收到 bye时断开连接
byte[] data = packet.getData();
String receiveData = new String(data,0,data.length);
System.out.println(msgFrom+":"+receiveData);
if(receiveData.startsWith("bye")){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
[2] 编写发送方的线程
package gs.chat;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
public class TalkSend implements Runnable {
DatagramSocket socket = null;
BufferedReader reader = null;
//发送者的端口
private int fromPort;
//接收者的IP
private String toIp;
//接收者的端口
private int toPort;
public TalkSend(int fromPort, String toIp, int toPort) {
this.fromPort = fromPort;
this.toIp = toIp;
this.toPort = toPort;
try {
//新建一个数据报连接
socket = new DatagramSocket(fromPort);
//获取我们键盘上的输入
reader = new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true){
try {
//读取相应的数据形成数据源包
String data = reader.readLine();
byte[] datas = data.getBytes();
DatagramPacket packet = new DatagramPacket(datas,0,datas.length,new InetSocketAddress(this.toIp,this.toPort));
//发送数据包
socket.send(packet);
//当接收到 bye时退出
if(data.startsWith("bye")){
break;
}
}catch (Exception e){
e.printStackTrace();
}
}
socket.close();
}
}
[3] 模拟通信的双方,一个为Teacher,需要开启一个接收线程和发送线程
假设老师要发送给学生的地址是localhost 8888,则学生接收的地址就是localhost 8888
学生要发送给老师的地址是localhost 9999, 则老师接收的地址就是localhost 9999。
package gs.chat;
public class TalkTeacher {
public static void main(String[] args) {
new Thread(new TalkSend(5555,"localhost",8888)).start();
new Thread(new TalkReceiver(9999,"student")).start();
}
}
[4] 一个为Student
package gs.chat;
public class TalkStudent {
public static void main(String[] args) {
//开启两个线程
new Thread(new TalkSend(7777,"localhost",9999)).start();
new Thread(new TalkReceiver(8888,"teacher")).start();
}
}
运行后就是一个mini的聊天室了