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开发笔记(序)章节目录》