简介
单播有TCP和UDP两种实现,组播(多播)和广播只有UDP一种实现。单播和广播基本一样,只是广播的数据包IP为广播IP。
单播
DatagramSocket和DatagramPacket
服务端:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPServer {
public static void main(String[] args) throws Exception {
System.out.println("启动UDP单播服务端");
// 构造DatagramSocket实例,指定本地端口6666
try (DatagramSocket datagramSocket = new DatagramSocket(6666)) {
//设置超时时间为10秒
datagramSocket.setSoTimeout(10000);
while (true) {
// 构造DatagramPacket实例,用来接收最大长度为512字节的数据包
byte[] data = new byte[512];
DatagramPacket receivePacket = new DatagramPacket(data, 512);
// 接收报文,此方法在接收到数据报前一直阻塞
datagramSocket.receive(receivePacket);
System.out.println("客户端地址:" + receivePacket.getAddress().getHostAddress());
System.out.println("客户端端口:" + receivePacket.getPort());
System.out.println("接收到的数据长度:" + receivePacket.getLength());
System.out.println("接收到的数据:" + new String(receivePacket.getData(), 0, receivePacket.getLength(), "UTF-8"));
}
}
}
}
客户端:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class UDPClient {
public static void main(String[] args) throws Exception {
{
// 构造DatagramSocket实例,指定本地端口8888
try (DatagramSocket datagramSocket = new DatagramSocket(8888)) {
for (int i = 0; i < 5; i++) {
String data = "当前循环:" + i;
// 构造数据报包,用来将data发送到指定主机上的指定端口号。
DatagramPacket sendPacket = new DatagramPacket(data.getBytes("UTF-8"), data.getBytes("UTF-8").length, new InetSocketAddress("10.206.16.67", 6666));
//发送报文
datagramSocket.send(sendPacket);
}
}
}
}
}
服务端打印:
启动UDP单播服务端
客户端地址:10.206.16.67
客户端端口:8888
接收到的数据长度:16
接收到的数据:当前循环:0
客户端地址:10.206.16.67
客户端端口:8888
接收到的数据长度:16
接收到的数据:当前循环:1
客户端地址:10.206.16.67
客户端端口:8888
接收到的数据长度:16
接收到的数据:当前循环:2
客户端地址:10.206.16.67
客户端端口:8888
接收到的数据长度:16
接收到的数据:当前循环:3
客户端地址:10.206.16.67
客户端端口:8888
接收到的数据长度:16
接收到的数据:当前循环:4
Exception in thread "main" java.net.SocketTimeoutException: Receive timed out
at java.net.DualStackPlainDatagramSocketImpl.socketReceiveOrPeekData(Native Method)
at java.net.DualStackPlainDatagramSocketImpl.receive0(DualStackPlainDatagramSocketImpl.java:120)
at java.net.AbstractPlainDatagramSocketImpl.receive(AbstractPlainDatagramSocketImpl.java:144)
at java.net.DatagramSocket.receive(DatagramSocket.java:812)
at UDPServer.main(UDPServer.java:16)
Socket和ServerSocket
服务端:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
public class TCPServer {
public static void main(String[] args) throws Exception {
System.out.println("启动单播TCP服务端");
//构造ServerSocket实例,指定监听的本地端口为6666
try (ServerSocket serverSocket = new ServerSocket(6666)) {
//设置超时时间为10秒
serverSocket.setSoTimeout(10000);
while (true) {
//侦听并接收到此套接字的连接,此方法在连接传入之前一直阻塞。
Socket socket = serverSocket.accept();
TCPServerHandleThread tcpServerHandleThread = new TCPServerHandleThread(socket);
Thread thread = new Thread(tcpServerHandleThread);
thread.start();
}
}
}
}
/**
* 为每个连接进来的请求单独起一个处理线程
*/
class TCPServerHandleThread implements Runnable {
private Socket socket;
public TCPServerHandleThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
try (BufferedInputStream inputStream = new BufferedInputStream(socket.getInputStream()); BufferedOutputStream outputStream = new BufferedOutputStream(socket.getOutputStream())) {
//每次读取的最大字节数,为了演示特地写小一点
byte[] bytes = new byte[8];
//每次读取的有效字节数
int count;
//结果数组
byte[] result = new byte[0];
while ((count = inputStream.read(bytes)) != -1) {
//本次读取的有效字节数组
byte[] temp1 = Arrays.copyOfRange(bytes, 0, count);
//复制结果数组到新数组,新数组长度为结果数组的长度加上本次读取的有效字节数,用0填充
byte[] temp2 = Arrays.copyOf(result, result.length + count);
// 将本次读取的有效字节数组放到新数组里
System.arraycopy(temp1, 0, temp2, result.length, count);
result = temp2;
}
InetSocketAddress inetSocketAddress = (InetSocketAddress) socket.getRemoteSocketAddress();
System.out.println("客户端的ip和端口:" + inetSocketAddress.getAddress() + " " + inetSocketAddress.getPort());
InetSocketAddress inetSocketAddressL = (InetSocketAddress) socket.getLocalSocketAddress();
System.out.println("本地绑定的ip和端口:" + inetSocketAddressL.getAddress() + " " + inetSocketAddressL.getPort());
System.out.println("接收到的数据长度:" + result.length);
System.out.println("接收到的数据:" + new String(result, "UTF-8"));
//关闭此socket输入流
socket.shutdownInput();
outputStream.write("接收完毕".getBytes("UTF-8"));
outputStream.flush();
//关闭此socket输出流
socket.shutdownOutput();
} finally {
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
客户端:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args) throws Exception {
// 构造Socket实例
try (Socket socket = new Socket("10.206.16.67", 6666); BufferedInputStream inputStream = new BufferedInputStream(socket.getInputStream()); BufferedOutputStream outputStream = new BufferedOutputStream(socket.getOutputStream())) {
InetSocketAddress inetSocketAddress = (InetSocketAddress) socket.getRemoteSocketAddress();
System.out.println("服务端的ip和端口:" + inetSocketAddress.getAddress() + " " + inetSocketAddress.getPort());
InetSocketAddress inetSocketAddressL = (InetSocketAddress) socket.getLocalSocketAddress();
System.out.println("本地绑定的ip和端口:" + inetSocketAddressL.getAddress() + " " + inetSocketAddressL.getPort());
outputStream.write("abcdefg测试数据1234567890".getBytes("UTF-8"));
outputStream.flush();
//关闭此socket输出流
socket.shutdownOutput();
byte[] bytes = new byte[1024];
int count;
if ((count = inputStream.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, count, "UTF-8"));
}
//关闭此socket输入流
socket.shutdownInput();
}
}
}
服务端打印:
启动单播TCP服务端
客户端的ip和端口:/10.206.16.67 62414
本地绑定的ip和端口:/10.206.16.67 6666
接收到的数据长度:29
接收到的数据:abcdefg测试数据1234567890
Exception in thread "main" java.net.SocketTimeoutException: Accept timed out
at java.net.DualStackPlainSocketImpl.waitForNewConnection(Native Method)
at java.net.DualStackPlainSocketImpl.socketAccept(DualStackPlainSocketImpl.java:135)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:404)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:199)
at java.net.ServerSocket.implAccept(ServerSocket.java:545)
at java.net.ServerSocket.accept(ServerSocket.java:513)
at TCPServer.main(TCPServer.java:17)
客户端打印:
服务端的ip和端口:/10.206.16.67 6666
本地绑定的ip和端口:/10.206.16.67 62414
接收完毕
客户端还可以采用如下方式设置连接超时时间为10秒:
Socket socket = new Socket();
socket.connect(new InetSocketAddress("10.206.16.67", 6666), 10000);
组播(多播)
组播是一对多的通信,允许同时向大量的接收方发送数据包。一个主机可以有多个组播进程,这些进程可以加入到同一个组播组也可以加入到不同的组播组,主机会跟踪记录当前哪些进程属于哪个组播组。进程随时可以要求主机主动告诉路由器加入和退出哪个组播组,而且每隔一段时间(大概1分钟)路由器也会向它所在的LAN发送一个查询数据包,要求主机告诉路由器它自己属于哪个组播组。支持组播的路由器负责向所有组内成员发送数据包,但不确保每个成员一定会收到。目的地址是组播ip的数据包会被路由器转发到对应组播组内的主机。
组播ip地址是D类地址,可以使用224.0.2.0~238.255.255.255这个范围的ip地址,组播进程根据组播ip进行分组。组播进程监听某端口并通过此端口接收数据,当一个组播数据包被转发到主机上的时候,主机会根据数据包的目的端口将数据包交给监听此目的端口的组播进程。
如果主机是多网卡,那么此时就需要注意了,一定要设置用哪个网卡发送和接受数据,因为组播是无法跨网段的,否则会导致数据接收不到。
MulticastSocket继承于DatagramSocket,因此可以发送也可以接收数据包。MulticastSocket绑定的端口是接收和发送数据的,如果数据包目的端口和此端口一致,则这个程序就能接收到数据包。setNetworkInterface方法是用来绑定网卡的。joinGroup告诉主机该程序要加入到哪个组播组,leaveGroup则是退出组播组。其他用法和DatagramSocket基本一致。
发送端:
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
public class MultiCastSender {
public static void main(String[] args) throws Exception {
InetAddress inetAddress = InetAddress.getByName("228.0.0.8");
MulticastSocket multicastSocket = new MulticastSocket();
multicastSocket.setNetworkInterface(NetworkInterface.getByInetAddress(InetAddress.getByName("10.206.16.67")));
multicastSocket.joinGroup(inetAddress);
int count = 0;
while (true) {
if (count == 5) {
multicastSocket.leaveGroup(inetAddress);
return;
}
String message = "时间戳:" + System.currentTimeMillis();
byte[] bytes = message.getBytes("UTF-8");
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length,
inetAddress, 8888);
multicastSocket.send(datagramPacket);
count++;
}
}
}
接收端:
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
public class MultiCastReceiver {
public static void main(String[] args) throws Exception {
InetAddress inetAddress = InetAddress.getByName("228.0.0.8");
MulticastSocket multicastSocket = new MulticastSocket(8888);
multicastSocket.setNetworkInterface(NetworkInterface.getByInetAddress(InetAddress.getByName("10.206.16.67")));
multicastSocket.joinGroup(inetAddress);
int count = 0;
while (true) {
if (count == 5) {
multicastSocket.leaveGroup(inetAddress);
return;
}
// 构造DatagramPacket实例,用来接收最大长度为512字节的数据包
byte[] data = new byte[512];
DatagramPacket receivePacket = new DatagramPacket(data, 512);
// 接收报文,此方法在接收到数据报前一直阻塞
multicastSocket.receive(receivePacket);
System.out.println("第" + (count + 1) + "次接收");
System.out.println("客户端地址:" + receivePacket.getAddress().getHostAddress());
System.out.println("客户端端口:" + receivePacket.getPort());
System.out.println("接收到的数据长度:" + receivePacket.getLength());
System.out.println("接收到的数据:" + new String(receivePacket.getData(), 0, receivePacket.getLength(), "UTF-8"));
count++;
}
}
}
接收端打印:
第1次接收
客户端地址:10.206.16.67
客户端端口:54214
接收到的数据长度:25
接收到的数据:时间戳:1559133639107
第2次接收
客户端地址:10.206.16.67
客户端端口:54214
接收到的数据长度:25
接收到的数据:时间戳:1559133639108
第3次接收
客户端地址:10.206.16.67
客户端端口:54214
接收到的数据长度:25
接收到的数据:时间戳:1559133639108
第4次接收
客户端地址:10.206.16.67
客户端端口:54214
接收到的数据长度:25
接收到的数据:时间戳:1559133639108
第5次接收
客户端地址:10.206.16.67
客户端端口:54214
接收到的数据长度:25
接收到的数据:时间戳:1559133639108
广播
服务端:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPServer {
public static void main(String[] args) throws Exception {
System.out.println("启动UDP广播服务端");
// 构造DatagramSocket实例,指定本地端口6666
try (DatagramSocket datagramSocket = new DatagramSocket(6666)) {
//设置超时时间为10秒
datagramSocket.setSoTimeout(10000);
while (true) {
// 构造DatagramPacket实例,用来接收最大长度为512字节的数据包
byte[] data = new byte[512];
DatagramPacket receivePacket = new DatagramPacket(data, 512);
// 接收报文,此方法在接收到数据报前一直阻塞
datagramSocket.receive(receivePacket);
System.out.println("客户端地址:" + receivePacket.getAddress().getHostAddress());
System.out.println("客户端端口:" + receivePacket.getPort());
System.out.println("接收到的数据长度:" + receivePacket.getLength());
System.out.println("接收到的数据:" + new String(receivePacket.getData(), 0, receivePacket.getLength(), "UTF-8"));
}
}
}
}
客户端:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class UDPClient {
public static void main(String[] args) throws Exception {
{
// 构造DatagramSocket实例,指定本地端口8888
try (DatagramSocket datagramSocket = new DatagramSocket(8888)) {
for (int i = 0; i < 5; i++) {
String data = "当前循环:" + i;
// 构造数据报包,指定数据包的IP为广播地址,端口为6666。
DatagramPacket sendPacket = new DatagramPacket(data.getBytes("UTF-8"), data.getBytes("UTF-8").length, new InetSocketAddress("255.255.255.255", 6666));
//发送报文
datagramSocket.send(sendPacket);
}
}
}
}
}
服务端打印:
启动UDP广播服务端
客户端地址:192.168.56.1
客户端端口:8888
接收到的数据长度:16
接收到的数据:当前循环:0
客户端地址:192.168.56.1
客户端端口:8888
接收到的数据长度:16
接收到的数据:当前循环:1
客户端地址:192.168.56.1
客户端端口:8888
接收到的数据长度:16
接收到的数据:当前循环:2
客户端地址:192.168.56.1
客户端端口:8888
接收到的数据长度:16
接收到的数据:当前循环:3
客户端地址:192.168.56.1
客户端端口:8888
接收到的数据长度:16
接收到的数据:当前循环:4
Exception in thread "main" java.net.SocketTimeoutException: Receive timed out
at java.net.DualStackPlainDatagramSocketImpl.socketReceiveOrPeekData(Native Method)
at java.net.DualStackPlainDatagramSocketImpl.receive0(DualStackPlainDatagramSocketImpl.java:120)
at java.net.AbstractPlainDatagramSocketImpl.receive(AbstractPlainDatagramSocketImpl.java:144)
at java.net.DatagramSocket.receive(DatagramSocket.java:812)
at UDPServer.main(UDPServer.java:17)
- netty的Udp单播、组播、广播实例+Java的Udp单播、组播、广播实例
网络上缺乏netty的udp的单播.组播案例,经过一番学习总结之后终于把这两个案例调通,下面把这两个案例的代码放在这里分享一下. 首先推荐博文: http://colobu.com/2014/10/2 ...
- 组播和广播的概念,IGMP的用途
1.组播和广播的概念 1) 组播 主机之间的通讯模式,也就是加入了同一个组的主机可以接收到此组内的所有数据,网络中的交换机和路由器只向有需求者复制并转发其所需数据. 主机可以向路由器请求加入或退出某个 ...
- 浅谈Android中的组播(多播)
组播使用UDP对一定范围内的地址发送相同的一组Packet,即一次可以向多个接受者发出信息,其与单播的主要区别是地址的形式.IP协议分配了一定范围的地址空间给多播(多播只能使用这个范围内的IP),IP ...
- 【VS开发】组播(多播)的C程序实战
每个人都有不同的认知规律和习惯, 有的人喜欢搞一套严密的大理论, 论述起来滔滔不绝, 不管自己懂不懂, 反正读者/听者是没搞懂. 有的人喜欢从实践出发, 没看到代码, 不运行一下, 不看到结果, 就不 ...
- <转>单播,广播,组播的缺点与优点
原文链接:http://apje.blog.163.com/blog/static/145345252007101175714761/ 当前的网络中有三种通讯模式:单播.广播.组播(多播),其中的组播 ...
- Android开发:组播(多播)与广播
近期由于需要编写能够使同一局域网中的Android客户端与PC端进行自动匹配通信功能的程序,学习并试验了JAVA组播与广播的内容,记录一些理解如下: 一.组播(多播) 背景知识:组播使用UDP对一定范 ...
- MAC地址的介绍(单播、广播、组播、数据收发)
MAC地址组成 网络设备的MAC地址是全球唯一的.MAC地址长度为48比特,通常用十六进制表示.MAC地址包含两部分:前24比特是组织唯一标识符(OUI,OrganizationallyUniqueI ...
- TCP/IP 笔记 - 广播和本地组播
在之前第二章介绍IP寻址的时候有介绍到,IP地址有4种:单播.组播.广播.任播. 单播,客户端与服务器之间点到点连接通信: 组播,在发送者和多个接收者(如某个特定的分组)之间实现点对多点的连接通信: ...
- Ztack学习笔记(6)-广播组播点播
Zigbee网络中进行数据通信主要有三种类型:单播.组播.广播.那这三种方式如何设置呢,在哪里设置呢, 一. 广播 当应用程序需要将数据包发送给网络的每一个设备时,使用这种模式.广播的短地址有三种 0 ...
- MAC 地址(单播、组播、广播地址分类)
简介 一个制造商在生产制造网卡之前,必须先向 IEEE 注册,以获取到一个长度为 24bit 的厂商代码,也称为 OUI(Organizationally-Unique Identifier).制造商 ...
随机推荐
- ssm框架之异常处理
异常处理思路 系统中异常包括两类:预期异常和运行时异常runtimeexception,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发.测试通过手段减少运行时异常的发生. 系统的dao.s ...
- SpringBoot2搭建基础架构——开源软件诞生4
技术框架篇--第4篇 用日志记录“开源软件”的诞生 赤龙ERP开源地址: 点亮星标,感谢支持,加微信与开发者交流 kzca2000 码云:https://gitee.com/redragon/redr ...
- openCV - 2. 矩阵的掩膜操作
获取图像像素指针.掩膜操作解释 获取图像像素指针 CV_Assert(myImage.depth() == CV_8U); Mat.ptr<uchar>(int i=0) 获取像素矩阵的指 ...
- Vue企业级优雅实战02-准备工作03-提交 GIT 平台
代码管理.版本管理是件老大难的事情,尤其多人开发中的代码冲突.突击功能时面临的 hotfix 等.本文只是简单说说如何将一套代码提交到两个 Git 平台(GitHub.GitEE)上.其他的 Git ...
- 高可用集群corosync+pacemaker之crmsh使用(二)
上一篇博客我们聊到了crmsh的安装以及配置一个资源到corosync+pacemaker高可用集群上的相关命令的用法,回顾请参考https://www.cnblogs.com/qiuhom-1874 ...
- 深入了解Kafka【五】Partition和消费者的关系
1.消费者与Partition 以下来自<kafak权威指南>第4章. 假设主题T1有四个分区. 1.1.一个消费者组 1.1.1.消费者数量小于分区数量 只有一个消费者时,消费者1将收到 ...
- [第二届全国中学生网络安全竞赛]bypass
前几天拿到了线下赛的源码,做做看.这道主要是命令执行的黑名单绕过 先看看给出的代码: <?php highlight_file(__FILE__); error_reporting(0); $b ...
- 组件给App全局传值vue-bus的使用
npm安装 npm install vue-bus main.js引入 import VueBus from 'vue-bus' Vue.use(VueBus) 组件 getHouse(e){ thi ...
- 跟着尚硅谷系统学习Docker-【day04】
day04-20200716 p18.docker容器数据卷 docker容器中的数据,做持久化. 容器关闭以后容器内的数据就没有了. 保存到数据库或者服务器宿主机里面. 作用:容器间可以 ...
- 用安卓 WebView 做一个“套壳”应用
前言 目前手机应用市场上的 APP 类型主要为以下两种: Native App(原生应用):直接针对平台(Android.iOS 等手机系统)进行开发,属于性能最优的方案,也是开发成本最大的方案. H ...