实现效果:在同一个局域网下,A主机发送的数据,B,C,D,E主机都能看到
因为要实现一个主机能并发的发送消息和接收消息,所以要用多线程实现
方法一:用半双工实现
每台主机用一个端口接收和发送数据
Test1
发送端
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
//用半双工方式发送和接收数据,所以发送端口和接收端口是同一个
//发送数据的线程和接收数据的线程是并发执行的,不可能同时占用同一个端口,所以端口使用不用互斥操作
//因为发送端口和接收端口是同一个,所以要在既不发数据,也不收数据的时候关闭socket对象。
// 做个约定让接收线程关闭socket
class SendData2 implements Runnable {
DatagramSocket socket;
private String toIp;
private int toPort;
public SendData2() {
}
//socket对象,目的IP,目的端口
public SendData2(DatagramSocket socket, String toIp, int toPort) {
this.socket = socket;//用来发送数据的socket对象
this.toIp = toIp;
this.toPort = toPort;
}
@Override
public void run() {
//从键盘写数据并发送出去
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String data;
try {
while ((data = reader.readLine()) != null) {
byte[] bytes = data.getBytes();//String->byte[]
//把要发送的数据包装成数据报包
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, new InetSocketAddress(this.toIp, this.toPort));
socket.send(packet);//发送数据报包
if (data.equals("bye")) {//发送bye表示要停止发送数据
Test1.sendIsOff = true;
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
接收端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
class ReceiveData2 implements Runnable {
DatagramSocket socket;
public ReceiveData2() {
}
public ReceiveData2(DatagramSocket socket) {
this.socket = socket;//用来接收数据的socket对象
}
@Override
public void run() {
while (true) {
//创建充当缓冲区的字节数组
byte[] bytes = new byte[1024];
//让数据报包的缓冲区指针指向bytes
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);
try {//接收数据报包,并送到缓冲区
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
}
//解析数据包
byte[] datas = packet.getData();
int offset = packet.getOffset();
int length = packet.getLength();
String receiveData = new String(datas, offset, length);
//获得发送方的主机名
InetAddress fromIP = packet.getAddress();
System.out.println(fromIP.getHostName() + ":" + receiveData);
if (receiveData.equals("bye")) {//数据接收结束
break;
}
}
//1、当代码执行到这里的时候,我们能确定,接收线程不再需要使用DatagramSocket对象
//2、判断,发送线程是否发出bye
while (true) {
try {//没有这个会出现,A发送bye给B,B发送bye给A,A程序结束,B程序一直没结束
//这里要有休眠,不然会出现接收方先收到bye,然后接收方一直占用CPU去判断sendIsOff是否为true,
// 导致发送方发了bye后根本没机会把sendIsOff置为true
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Test1.sendIsOff) {
break;
}
}
//当发送线程和接收线程都不在使用套接字对象的时候关闭资源
socket.close();
}
}
主方法入口
import java.net.DatagramSocket;
import java.net.SocketException;
//Test1和Test2聊天
public class Test1 {
static boolean sendIsOff=false;//判断数据是否发送完,true是发送完
public static void main(String[] args) throws SocketException {
DatagramSocket socket=new DatagramSocket(8888);
//Test1用端口8888发送消息;目的IP是广播地址,消息会发给本网络的所有主机,我所在的网络号为192.168.5.0
new Thread(new SendData2(socket, "192.168.5.255", 9999)).start();
//也用端口8888接收消息
new Thread(new ReceiveData2(socket)).start();
}
}
Test2
发送端
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class Send2 implements Runnable {
DatagramSocket socket;
private String toIp;
private int toPort;
public Send2() {
}
//socket对象,目的IP,目的端口
public Send2(DatagramSocket socket, String toIp, int toPort) {
this.socket = socket;//用来发送数据的socket对象
this.toIp = toIp;
this.toPort = toPort;
}
@Override
public void run() {
//从键盘写数据并发送出去
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String data;
try {
while ((data = reader.readLine()) != null) {
byte[] bytes = data.getBytes();//String->byte[]
//把要发送的数据包装成数据报包
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, new InetSocketAddress(this.toIp, this.toPort));
socket.send(packet);//发送数据报包
if (data.equals("bye")) {//发送bye表示要停止发送数据
Test2.sendIsOff = true;
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
接收端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Receive2 implements Runnable {
DatagramSocket socket;
public Receive2() {
}
public Receive2(DatagramSocket socket) {
this.socket = socket;//用来接收数据的socket对象
}
@Override
public void run() {
while (true) {
//创建充当缓冲区的字节数组
byte[] bytes = new byte[1024];
//让数据报包的缓冲区指针指向bytes
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);
try {//接收数据报包,并送到缓冲区
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
}
//解析数据包
byte[] datas = packet.getData();
int offset = packet.getOffset();
int length = packet.getLength();
String receiveData = new String(datas, offset, length);
//获得发送方的主机名
InetAddress fromIP = packet.getAddress();
System.out.println(fromIP.getHostName() + ":" + receiveData);
if (receiveData.equals("bye")) {//数据接收结束
break;
}
}
//1、当代码执行到这里的时候,我们能确定,接收线程不再需要使用DatagramSocket对象
//2、判断,发送线程是否发出bye
while (true) {
try {//没有这个会出现,A发送bye给B,B发送bye给A,A程序结束,B程序一直没结束
//这里要有休眠,不然会出现接收方先收到bye,然后接收方一直占用CPU去判断sendIsOff是否为true,
// 导致发送方发了bye后根本没机会把sendIsOff置为true
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Test2.sendIsOff)
break;
}
//当发送线程和接收线程都不在使用套接字对象的时候关闭资源
socket.close();
}
}
主方法入口
import java.net.DatagramSocket;
import java.net.SocketException;
//Test1和Test2聊天
public class Test2 {
static boolean sendIsOff=false;//判断数据是否发送完,true是发送完
public static void main(String[] args) throws SocketException {
DatagramSocket socket=new DatagramSocket(9999);
//Test2用端口9999发送消息;目的IP是广播地址,消息会发给本网络的所有主机,我所在局域网的网络号为192.168.5.0
new Thread(new Send2(socket, "192.168.5.255", 8888)).start();
//也用端口9999接收消息
new Thread(new Receive2(socket)).start();
}
}
方法二:用全双工实现
每台主机用一个端口接收数据,一个端口发送数据
发送端
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 SendData1 implements Runnable {
DatagramSocket socket = null;
private int fromPort;
private String toIp;
private int toPort;
public SendData1() {
}
public SendData1(int fromPort, String toIp, int toPort) {//源端口,目的IP,目的端口
this.fromPort = fromPort;
this.toIp = toIp;
this.toPort = toPort;
try {
socket = new DatagramSocket(fromPort);//用来发送数据的socket对象
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
//从键盘写数据并发送出去
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String data;
try {
while ((data = reader.readLine()) != null) {
byte[] bytes = data.getBytes();//String->byte[]
//把要发送的数据包装成数据报包
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, new InetSocketAddress(this.toIp, this.toPort));
socket.send(packet);//发送数据报包
if (data.equals("bye")) {//发送bye表示要停止发送数据
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
socket.close();//关闭资源
}
}
接收端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//用全双工方式发送和接收数据,所以发送端口和接收端口不能重复
public class ReceiveData1 implements Runnable {
DatagramSocket socket = null;
private int port;
public ReceiveData1() {
}
public ReceiveData1(int port) {//用来接收数据的端口
this.port = port;
try {
socket = new DatagramSocket(port);//用来接收数据的socket对象
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true) {
//创建充当缓冲区的字节数组
byte[] bytes = new byte[1024];
//让数据报包的缓冲区指针指向bytes
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);
try {//接收数据报包,并送到缓冲区
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
}
//解析数据包
byte[] datas = packet.getData();
int offset = packet.getOffset();
int length = packet.getLength();
String receiveData = new String(datas, offset, length);
//获得发送方的主机名
InetAddress fromIP = packet.getAddress();
System.out.println(fromIP.getHostName() + ":" + receiveData);
if (receiveData.equals("bye")) {//数据接收结束
break;
}
}
socket.close();//关闭资源
}
}
主方法入口
//Test1和Test2聊天
public class Test1 {
public static void main(String[] args) {
//Test1用端口7777发送消息;目的IP是广播地址,消息会发给本网络的所有主机,我所在局域网的网络号为192.168.5.0
new Thread(new SendData1(7777, "192.168.5.255", 9999)).start();
//用端口8888接收消息
new Thread(new ReceiveData1(8888)).start();
}
}
public class Test2 {
public static void main(String[] args) {
//Test2用端口5555发送消息;目的IP是广播地址,消息会发给本网络的所有主机
new Thread(new SendData1(5555, "192.168.5.255", 8888)).start();
//用端口9999接收消息
new Thread(new ReceiveData1(9999)).start();
//System.out.println(InetAddress.getLocalHost());
}
}