Java开发笔记(一百一十六)采用UDP协议的Socket通信
前面介绍了如何通过Socket接口传输文本与文件,在示例代码中,Socket客户端得先调用connect方法连接服务端,确认双方成功连上后才能继续运行后面的代码,这种确认机制确保客户端与服务端的的确确成功连接了,因而是可靠的网络连接,并且该可靠连接属于TCP连接。为啥这么说呢?因为TCP协议(全称“Transmission Control Protocol”,传输控制协议)不仅是一种传输层的通信协议,而且它具备面向可靠连接、以及基于字节流两大特征。之前联合Socket与ServerSocket实现消息通信的过程,正是遵从TCP协议的精神
虽然可靠连接能够保证一定会把信息送达对方,但是有时需要批量向一群目标设备发送消息,也就是俗称的“群发”,倘若每个设备都经历建立连接、发送消息、关闭连接三个步骤,整个群发操作的资源开销将是巨大的。鉴于群发功能一般为单向过程,消息发送方既不关心那些接收方是否收到消息,也不指望那些接收方会有什么反馈结果,总之消息发送方就像电台做广播那样,在固定的频率波段发送信息,它才不管别人的收音机有没有开着、有没有接收这个频道,只有收音机开着且调至对应的频道,方能收到该电台的广播节目。像这样的广播功能用到了传输层的另一种UDP协议(全称“User Datagram Protocol”,用户数据报协议),由于UDP并非可靠连接,它只管扔沙包,而不管对方有没有接到沙包,因此实现过程相较TCP要更简单,毕竟随便丢东西不费多少劲儿。
就UDP协议而言,Java给出的实现工具包括数据包套接字DatagramSocket和数据包裹DatagramPacket。其中DatagramSocket提供了设备间的数据交互动作,它的主要方法说明如下:
构造方法:对于服务端来说,构造方法需要指定待侦听的端口号;对于客户端来说,构造方法无需任何参数。
receive:该方法用于服务端接收数据。
send:该方法用于客户端发送数据。
close:关闭数据包套接字。
注意上面的receive和send两个方法,它们的输入参数类型为DatagramPacket,也就是说,必须先将数据封装为DatagramPacket格式,才能在UDO的服务端与客户端之间传输。下面是DatagramPacket的主要方法说明:
用于服务端的构造方法:此时构造方法只有两个参数,分别为字节数组及其长度。
用于客户端的构造方法:此时构造方法拥有四个参数,依次为字节数组、数组长度、数据要发往的服务器InetAddress地址、服务器的端口号。
getData:获取数据包裹里的字节数组。
getOffset:获取数据的起始偏移。
getLength:获取数据的长度。
接下来举个简单的应用案例,采取UDP协议在设备之间传输文本消息,此时的UDP服务端代码示例如下:
//演示Socket服务器的运行(UDP协议的不可靠连接)
public class TestUdpServer {
private static final int UDP_PORT = 61000; // UDP传输专用端口 public static void main(String[] args) {
startUdpServer(); // 启动UDP服务器接收文本消息
} // 启动UDP服务器接收文本消息
private static void startUdpServer() {
PrintUtils.print("UDP服务器已启动");
// 创建一个监听指定端口的DatagramSocket对象
try (DatagramSocket socket = new DatagramSocket(UDP_PORT)) {
byte[] data = new byte[1024]; // 接收数据的字节数组
// 创建一个DatagramPacket对象,并指定数据包的字节数组及其大小
DatagramPacket packet = new DatagramPacket(data, data.length);
while (true) { // 持续侦听
socket.receive(packet); // 接收到了数据包
// 把收到的数据转换为字符串。字符串构造方法的三个参数依次为:
// 已收到的数据、起始偏移、数据的长度。
String message = new String(packet.getData(),
packet.getOffset(), packet.getLength());
PrintUtils.print("UDP服务器收到消息:" + message);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
原来UDP方式的服务端代码如此简洁,UDP客户端的代码同样简约,即便是发送两条消息的完整代码也只有以下数行:
//演示Socket客户端的运行(UDP协议的不可靠连接)
public class TestUdpClient {
// 以下为Socket服务器的IP和端口,根据实际情况修改
private static final String SOCKET_IP = "192.168.1.8";
private static final int UDP_PORT = 61000; // UDP传输专用端口 public static void main(String[] args) {
startUdpClient("Hello World"); // 启动UDP客户端发送文本消息
startUdpClient("你好,世界"); // 启动UDP客户端发送文本消息
} // 启动UDP客户端发送文本消息
private static void startUdpClient(String message) {
PrintUtils.print("UDP客户端发送消息:" + message);
// 创建一个DatagramSocket对象
try (DatagramSocket socket = new DatagramSocket()) {
// 根据IP地址获得对应的网络地址对象
InetAddress serverAddress = InetAddress.getByName(SOCKET_IP);
byte data[] = message.getBytes(); // 把字符串转换为字节数组
// 创建一个DatagramPacket对象,构造方法的四个参数依次为:
// 待发送的数据、数据的长度、服务器的网络地址、服务器的端口号。
DatagramPacket packet = new DatagramPacket(data, data.length, serverAddress, UDP_PORT);
socket.send(packet); // 向服务器发送数据包
} catch (Exception e) {
e.printStackTrace();
}
}
}
然后先后运行服务端与客户端的测试代码,观察到的客户端日志如下:
12:16:12.316 main UDP客户端发送消息:Hello World
12:16:12.366 main UDP客户端发送消息:你好,世界
同时观察到下面的服务端日志:
12:15:46.998 main UDP服务器已启动
12:16:12.366 main UDP服务器收到消息:Hello World
12:16:12.368 main UDP服务器收到消息:你好,世界
根据以上的客户端日志以及服务端日志,可知通过UDP协议也成功完成了文本传输。
更多Java技术文章参见《Java开发笔记(序)章节目录》
Java开发笔记(一百一十六)采用UDP协议的Socket通信的更多相关文章
- Java开发笔记(八十六)通过缓冲区读写文件
前面介绍了利用文件写入器和文件读取器来读写文件,因为FileWriter与FileReader读写的数据以字符为单位,所以这种读写文件的方式被称作“字符流I/O”,其中字母I代表输入Input,字母O ...
- Java开发笔记(二十六)方法的输出参数
前面介绍了方法的输入参数,与输入参数相对应的则为输出参数,输出参数也被称作方法的返回值,意思是经过方法的处理最终得到的运算数值.这个返回值可能是整型数,也可能是双精度数,也可能是数组等其它类型,甚至允 ...
- Java开发笔记(三十六)字符串的常用方法
不管是给字符串赋值,还是对字符串格式化,都属于往字符串填充内容,一旦内容填充完毕,则需开展进一步的处理.譬如一段Word文本,常见的加工操作就有查找.替换.追加.截取等等,按照字符串的处理结果异同,可 ...
- Java开发笔记(四十六)类的构造方法
前面介绍了如何定义一个简单的类,以及它的成员属性和成员方法,从示例代码可以看到,不管是OrangeSimple还是OrangeMember,都要先利用关键字new创建一个实例,然后才能通过实例名称访问 ...
- Java开发笔记(五十六)利用枚举类型实现高级常量
前面介绍了联合利用final和static可实现常量的定义,该方式用于简单的常量倒还凑合,要是用于复杂的.安全性高的常量,那就力不从心了.例如以下几种情况,final结合static的方式便缺乏应对之 ...
- Java开发笔记(七十六)如何预防异常的产生
每个程序员都希望自己的程序稳定运行,不要隔三岔五出什么差错,可是程序运行时冒出来的各种异常着实烦人,令人不胜其扰.虽然可以在代码中补上try/catch语句捕捉异常,但毕竟属于事后的补救措施.与其后知 ...
- Java开发笔记(四十)日期与字符串的互相转换
前面介绍了如何通过Date工具获取各个时间数值,但是用户更喜欢形如“2018-11-24 23:04:18”这种结构清晰.简洁明了的字符串,而非啰里八唆依次汇报每个时间单位及其数值的描述.既然日期时间 ...
- Java开发笔记(三十二)字符型与整型相互转化
前面提到字符类型是一种新的变量类型,然而编码实践的过程中却发现,某个具体的字符值居然可以赋值给整型变量!就像下面的例子代码那样,把字符值赋给整型变量,编译器不但没报错,而且还能正常运行! // 字符允 ...
- Java开发笔记(八十八)文件字节I/O流
前面介绍了如何使用字符流读写文件,并指出字符流工具的处理局限,进而给出随机文件工具加以改进.随机文件工具除了支持访问文件内部的任意位置,更关键的一点是通过字节数组读写文件数据,采取字节方式比起字符方式 ...
随机推荐
- [ARIA] Create an Accessible Tooltip on a Text Input
Here we use HTML and CSS to create a stylish yet semantic tooltip on a form input. I am using aria-d ...
- LeetCode 983. Minimum Cost For Tickets
原题链接在这里:https://leetcode.com/problems/minimum-cost-for-tickets/ 题目: In a country popular for train t ...
- Python爬虫进阶 | 多线程
一.简介 为了提高爬虫程序效率,由于python解释器GIL,导致同一进程中即使有多个线程,实际上也只会有一个线程在运行,但通过request.get发送请求获取响应时有阻塞,所以采用了多线程依然可以 ...
- WinDbg常用命令系列---断点操作b*
ba (Break on Access) ba命令设置处理器断点(通常称为数据断点,不太准确).此断点在访问指定内存时触发. 用户模式下 [~Thread] ba[ID] Access Size [O ...
- linux系统管理——账号权限及归属管理练习
1.创建/guanli 目录,在/guanli下创建zonghe 和 jishu 两个目录(一条命令) 2.添加组帐号zonghe.caiwu.jishu,GID号分别设置为2001.2002.200 ...
- vuex 之既生‘mutation’何生‘action’
vuex 中,action 及 mutation 均为操作数据的作用而存在,既然二者均可改变数据,为什么要分成两个方法来处理呢,因为: Mutation 必须是同步函数 mutations: { so ...
- mysql 日期处理
mysql> select curdate(); +------------+ | curdate() | +------------+ | -- | +------------+ row in ...
- YoTube 视频如何下载
因我学习自动化测试 ,国内的C# selenium 搭建的环境的资料甚少,然后去国外网站找资料, 曹鼠给我的gogle安装一个下载YoTube视频插件,特此非常感谢他. 前提条件需要一个服务器:Sha ...
- docker容器启动后添加端口映射
DOCKER 给运行中的容器添加映射端口 方法1 1.获得容器IP 将container_name 换成实际环境中的容器名 docker inspect `container_name` | grep ...
- 范仁义html+css课程---8、新元素布局
范仁义html+css课程---8.新元素布局 一.总结 一句话总结: 推荐用新标签(语义化的标签)来布局:header(头部),footer(尾部),aside(侧边栏),nav(导航),artic ...