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流
前面介绍了如何使用字符流读写文件,并指出字符流工具的处理局限,进而给出随机文件工具加以改进.随机文件工具除了支持访问文件内部的任意位置,更关键的一点是通过字节数组读写文件数据,采取字节方式比起字符方式 ...
随机推荐
- 初学FWT(快速沃尔什变换) 一点心得
FWT能解决什么 有的时候我们会遇到要求一类卷积,如下: Ci=∑j⊕k=iAj∗Bk\large C_i=\sum_{j⊕k=i}A_j*B_kCi=j⊕k=i∑Aj∗Bk此处乘号为普通乘法 ...
- 算法笔记求序列A每个元素左边比它小的数的个数(树状数组和离散化)
#include <iostream> #include <algorithm> #include <cstring> using namespace std ; ...
- go选项模式
package main import "fmt" type optionClient func(*options) func setAge(a int) optionClient ...
- 使用Ajax实现三级联动
首先准备数据库只有一张表 分析数据库根据 parentid来查 jsp代码 servlet代码 <%-- Created by IntelliJ IDEA. User: 60590 Date: ...
- java 数组逆序输出(方法内部的代码)
//现在数组中有1, 2, 4, 5, 6, 7, 8 请逆序输出 int [] arrs={1,2,3,4,5,6,7,8}; for(int i=arrs.length-1;i>-1;i-- ...
- Visual C++ 里的异常处理
微软Visual C++是Win32最广泛使用的编译器,因此Win32反向器对其内部工作非常熟悉.能够识别编译器生成的粘合代码有助于快速集中于程序员编写的实际代码.它还有助于恢复程序的高级结构.我将集 ...
- Win32下的中断和异常
本文是Matt Pietrek在1997年月10月的MSJ杂志Under The Hood专栏上发表的文章.中断和异常在DOS时代是整个系统的灵魂,但Windows已将其隐藏到了系统深处.Matt P ...
- 54、Spark Streaming:DStream的transformation操作概览
一. transformation操作概览 Transformation Meaning map 对传入的每个元素,返回一个新的元素 flatMap 对传入的每个元素,返回一个或多个元素 filter ...
- 在windows环境下可以裁剪.jpg格式的图片
发现在windows操作系统下,可以利用图片编辑器裁剪.jpg格式的尺寸大小.其四方有四个推子.可以移动.注意点击右方的“确定”按钮.
- Java Spring 使用 Redis
在 Java 中使用 Redis 需要使用 Jedis.jar github 页面 https://github.com/xetorthio/jedis javadocs http://xetorth ...