UDP相关类
UDP是基于数据报的传输协议,java.net.DatagramSocket类负责接收和发送UDP数据报,java.net.DatagramPacket表示UDP数据报。
(1)DatagramSocket类
每个DatagramSocket与一个数据报套接字(内含本地主机的IP和端口)绑定,每个DatagramSocket可以把UDP数据报发往任意一个远程DatagramSocket,也可接收来自任何远程DatagramSocket的数据报。在UDP数据报中包含了目的地址信息,DatagramSocket可根据该信息把数据报发往目的地。在上节讲的TCP通信中,客户端Socket必须先与服务器端建立连接,连接建好后,服务器端也会持有与客户端相连的Socket,且双方的Socket是对应的,共同构成了两个端点之间的虚拟通信链路。而UDP是无连接的协议,客户端DatagramSocket与服务器端DatagramSocket不存在对应关系,两者无须建立连接就能交换数据。每个DatagramSocket对象都会与一个本地端口绑定,在此端口监听发送过来的数据报。在服务器程序中,由程序显式地为DatagramSocket指定端口;而在客户程序中,一般由操作系统为DatagramSocket分配本地端口,这种端口也称为匿名端口。
DatagramSocket有如下一些构造方法。
DatagramSocket类的常用方法
方 法 | 功 能 |
void send(DatagramPacket p) throws IOException | 发送一个UDP数据报,即一个DatagramPacket对象 |
void receive(DatagramPacket p) throws IOException | 接收一个UDP数据报,即一个DatagramPacket对象 |
void connect(InetAddress address, int port) | 将该UDPSocket变成一个连接型的UDPSocket |
void disconnect() | 将该UDPSocket变成一个非连接型的UDPSocket |
void close() | 关闭UDPSocket |
(2)DatagramPacket类
DatagramPacket类的对象代表一个UDP数据报。用UDP发送数据时,先要根据发送的数据生成一个DatagramPacket对象,然后通过它的send()方法发送这个对象;接收时也先要根据要接收数据的缓冲区生成一个DatagramPacket对象,然后通过它的receive()方法接收这个对象的数据内容。 DatagramPacket类的构造方法分为两类:一类创建的对象用来接收数据报;另一类创建的对象则用来发送数据报。它们的区别是,用于发送数据报的构造方法需要设定数据报到达的目的地址(若是“连接型”UDP,则不需要设定这个地址),而用于接收数据报的构造方法无须设定地址。
用于接收数据报的构造方法如下。
DatagramPacket(byte[] buf, int length) 功能:由接收缓冲区(byte[]字节数组与它的长度length)生成一个DatagramPacket对象。buf表示保存传入数据报的缓冲区,length表示要读取的字节数。
DatagramPacket(byte[] buf, int offset, int length) 功能:构造DatagramPacket,用来接收长度为length的包,在缓冲区中指定了偏移量。
用于发送数据报的构造方法如下
DatagramPacket(byte[] buf, int length, InetAddress address, int port) 功能:构造数据报,用来将长度为length的包发送到指定主机上的指定端口。length参数必须小于等于buf.length。因为默认UDPSocket是非连接型,故要在每一个发送用的UDP数据报中携带接收方的IP和端口号。
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) 功能:构造数据报,用来将长度为length、偏移量为offset的包发送到指定主机上的指定端口,length参数必须小于等于buf.length。
DatagramPacket类的常用方法
方 法 | 功 能 |
byte[] getData() | 返回DatagramPacket对象中包含的数据 |
int getLength() | 返回发送/接收数据的长度 |
int getOffset() | 返回发送/接收数据在byte[]中的偏移 |
InetAddress getAddress() | 返回对方的IP地址,用InetAddress对象表示 |
int getPort() | 返回对方的端口号 |
void setData(byte[] buf,int offset,int length) | 设置该DatagramPacket对象中包含的数据 |
void setAddress(InetAddress iaddr) | 设置该DatagramPacket对象中包含的IP地址 |
void setPort(int iport) | 设置该DatagramPacket对象中包含的端口号 |
举例:UDP通信程序
【例14.3】一个简单UDP通信程序,客户程序向服务器程序发送任意的字符串,服务器端收到后,计算字符串的长度并向客户端回送相同的字符串。
(1)首先建立一个UDP服务器程序。在程序中创建了一个在9777端口上等待接收数据报的DatagramSocket对象。当有数据报要接收时,创建一个DatagramPacket对象接收此数据报。收到后分析数据报,并再次创建一个DatagramPacket对象回送此数据报。
(2)客户端程序创建一个DatagramSocket对象准备发送数据。该对象把要发送的数据打包成一个数据包,并在包中写明要发送给远程服务器的IP地址与端口号。
运行上面的两个程序,客户程序向服务器程序发送数据包,服务器接收此包,显示收到的字符串内容以及客户机的IP地址和端口号
服务端代码:
客户端部分:
运行结果:
源代码部分:
服务器端
package org;
import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
public class UDPServer {
public static void main(String[] args) throws Exception {
DatagramSocket ds = new DatagramSocket(9777); // 在9777端口上创建UDPSocket
Frame f = new Frame("UDPServer");
f.setLocation(400, 300);
f.setSize(300, 300);
TextField tf = new TextField(40);
TextArea list = new TextArea();
f.add(list, BorderLayout.NORTH);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() { // 关闭窗口
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
byte[] buf = new byte[1024]; // 接收缓冲区
DatagramPacket dp = null; // 接收UDP数据包
DatagramPacket sdp = null; // 发送UDP数据包
boolean more = true; // 控制UDP服务器
while (more) {
dp = new DatagramPacket(buf, 1024); // 创建一个用于接收的UDP数据包
ds.receive(dp); // 等待任一个客户机发数据包
InetAddress caddr = dp.getAddress(); // 获取客户机的IP地址
int cport = dp.getPort(); // 获取客户机的端口号
// 获取客户机发送的文本内容
String s = new String(dp.getData(), dp.getOffset(), dp.getLength());
String str = "客户机 IP: " + caddr + " 客户机端口号:" + cport+ "\n"
+ " 客户机发送的数据是:" + s + " \n";
list.append(str);
String rs = new String("字符串:" + s + " 的长度是:" + s.length());
byte[] sbuf = rs.getBytes(); // 将串转换成字节数组
// 生成一个发送回特定客户机的UDP数据包的DatagramPacket对象
sdp = new DatagramPacket(sbuf, sbuf.length, caddr, cport);
ds.send(sdp); // 向客户机发回响应信息
}
}
}
客户端:
package org;
import java.io.*;
import java.net.*;
import java.util.Date;
import java.awt.*;
import java.awt.event.*;
public class UDPClient {
static List list = new List(6);
static TextField tf = new TextField(40);
static DatagramPacket sdp = null;
static DatagramPacket rdp = null;
public static void main(String[] args) throws Exception {
Frame f = new Frame("UDPClient");
DatagramSocket ds = new DatagramSocket(); // 生成一个客户机用UDPSocket
f.setLocation(400, 300);
f.setSize(300, 300);
f.add(tf, BorderLayout.SOUTH);
f.add(list, BorderLayout.NORTH);
f.pack();
f.addWindowListener(new WindowAdapter() { // 关闭窗口
public void windowClosing(WindowEvent arg0) {
System.exit(0);
}
});
tf.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
try {
byte[] rbuf = new byte[1024], // 接收缓冲区大小设置为1024
sbuf = null;
String str =tf.getText(); // 获取文本框的内容
list.add(str); // 将数据添加到列表框中
tf.setText(null);
sbuf = str.getBytes(); // 转换字节数组
// 生成一个发送给UDP服务器的UDP数据包
sdp = new DatagramPacket(sbuf,
sbuf.length, InetAddress.getByName("127.0.0.1"), 9777);
// 生成一个发送给UDP服务器的UDP数据包
DatagramSocket ds = new DatagramSocket();
ds.send(sdp); // 发送出去
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
f.setVisible(true);
ds.close(); // 关闭Socket
}
}