基于UDP协议的Socket编程
1 UDP协议
2 DatagramPacket类和DatagramSocket类
2.1 DatagramPacket类
2.2 DatagramSocket类
3 基于UDP协议的Socket编程
3.1基于UDP协议的Socket编程的基本步骤
3.2 应用举例、本章小结
1
UDP协议
用户数据报协议(UserDatagramProtocol,UDP)是OSI参考模型中一种面向无连接的传输层协议,提供面向操作的简单的不可靠数据传送服务。在网络中它与TCP协议一样用于处理数据包。UDP协议直接工作于IP协议的顶层。
UDP协议在发送数据时并不刻意追求数据包会完全发送出去,也不能担保它们抵达的顺序与它们发出时一样,因为它是一种不可靠协议。
UDP协议的特性:
(1)UDP是一个面向无连接的协议,传输数据之前客户端和服务器端不建立连接,当它想发送数据时则获取来自应用程序的数据,并尽可能快地把它放到网络上。
(2)由于传输数据时不需建立连接,不需维护连接状态,因此一台服务器可同时向多个客户传输相同的数据。
UDP使用的类
DatagramPacket类包含具体的要传输的信息,这些信息被封装在称为数据报(Datagram)的UDP包中。
DatagramSocket类用于收发UDP数据报。
为发送数据,要将数据封装到DatagramPacket中,使用DatagramSocket发送该包。
为接收数据,要从DatagramSocket中接收一个DatagramPakcet对象,然后解析该包的内容。
UDP与TCP的比较
(1)UDP没有两台主机间唯一连接的概念。使用UDP时,每个数据报中都给出了完整的地址信息,因此无需要建立发送方和接收方的连接。对于TCP协议,由于它是一个面向连接的协议,在Socket之间进行数据传输之前必然要建立连接,所以在TCP中多了一个连接建立的时间。
(2)一个DatagramSocket可以从多个独立主机接收数据,也可向多个主机传输相同的数据,此Socket并不专用于一个连接。
(3)TCP
Socket将网络视为流,通过与Socket相关的输入输出流来收发数据。而UDP所处理的总是单个数据报包,封装在一个数据报中的数据都以一个包的形式整体收发,一个包不需与其它包相关。流中的数据是有序的,而数据报包与数据报包之间是无序的。
(4)使用UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内。而TCP没有这方面的限制,一旦连接建立起来,双方的socket就可以按统一的格式传输大量的数据。
2
DatagramPacket类和DatagramSocket类
2.1DatagramPacket类
该类表示一个数据报包。该数据报包用来实现一个无连接包的传送服务。它是进行数据报通信的基本单位。包含了IP地址、端口号和需要传输的数据等。在发送和接收数据报时,要创建DatagramPakcet类对象作为数据的载体。
1.构造函数
(1)public DatagramPacket(byte ibuf[],int
ilength)
功能:创建一个用于接收数据报的DatagramPacket类对象。
参数:
ibuf:存储数据报的缓冲区。在数据报通信中,发送和接收一个数据报,都需提供一个缓冲区,用来安置发送和接收到的数据报。
ilength:接收数据报的长度,必须小于等于ibuf.length。
(2)public DatagramPacket(byte ibuf[],int ilength,InetAddress
iaddr,int iport)
功能:创建一个用于发送数据报的DatagramPacket类对象。
参数:
ibuf:存储数据报的缓冲区。
ilength:发送数据报的长度,必须小于等于ibuf.length。
iaddr:数据报要发送到的目的IP地址。
iport:数据报要发送到的目的地址的端口号。
2.常用方法
(1)public synchronized InetAddress
getAddress()功能:返回存放在接收或发送的数据报中的IP址。
(2)public synchronized int
getPort()功能:返回存放在接收或发送的数据报中的端口号。
(3)public synchronized byte[] getData()功能:返回存放在数据报中的数据。
(4)public synchronized int getLength()功能:返回数据报中数据的长度。
(5)public synchronized void setAddress(InetAddress
iaddr)功能:设置发送数据报的目的地址为iaddr。
(6)public synchronized void setPort(int
iport)功能:设置发送数据报的目的地址的端口号为iport。
(7)public synchronized void setData(byte
ibuf[])功能:设置数据报中的数据内容为字节数组ibuf的数据。
(8)public synchronized void setLength(int
ilength)功能:设置数据报中的数据的长度为ilength。
2.2
DatagramSocket类
该类表示用来发送和接收数据报包的套接字(Socket)。要收发DatagramPacket,必须打开一个数据报套接字(Datagram
Socket)。在Java中,数据报套接字通过DatagramSocket类来创建和访问。
1.构造函数
(1)public DatagramSocket() throws SocketException
功能:创建用于数据报通信的socket对象,由本地主机自动指定的一个可用的端口号。
(2)public DatagramSocket(int port) throws SocketException
功能:创建用于数据报通信的socket对象,并且把它绑定到本地主机指定的端口port。
(3)public DatagramSocket(int port,InetAddress addr) throws
SocketException
功能:创建用于数据报通信的socket对象,并绑定到指定的本地地址addr的指定端口port。端口号port必须为0和65535之间的一个。
2.常用方法
(1)public void send(DatagramPacket p) throws
IOException功能:从此socket发送数据报包。
(2)public synchronized void receive(DatagramPacket p) throws
IOException功能:从网络接收一个UDP数据报包。存储在DatagramPacket类对象p中。该数据报包还包含发送方的IP
地址和发送方主机的端口号。
(3)public InetAddress
getLocalAddress()功能:获取该socket绑定的本地地址。
(4)public int getLocalPort()功能:获取该socket绑定的本地主机的端口号。
(5)public void close()功能:释放该数据报socket所占用的端口。
【例 1】
获取本地主机的时间,通过UDP实现。
(1)服务器端程序TimeServer_UDP.java
import
java.net.*;
import java.io.*;
import java.util.*;
public class TimeServer_UDP{
public static void main(String args[]) throws
IOException{
DatagramSocket socket = new
DatagramSocket(3000);//Socket对象.端口3000
System.out.println("等待接收数据......");
while(true){
byte buf[] =
new byte[256];
DatagramPacket
packet = new DatagramPacket(buf,buf.length);
socket.receive(packet);
String date =
new Date().toString();
buf =
date.getBytes();
InetAddress
addr = packet.getAddress();
int port =
packet.getPort();
packet = new
DatagramPacket(buf,buf.length,addr,port);
socket.send(packet);
System.out.println("已接收来自"
+ addr.toString() + ":"+port+"的数据");
}
}
}
(2)客户端程序TimeClient_UDP.java
import
java.net.*;
import java.io.*;
public class TimeClient_UDP{
public static void main(String args[]) throws
IOException{
String host =
"localhost";
byte msg[] = new
byte[256];//存放收发数据
InetAddress addr =
InetAddress.getByName(host);//获取主机地址
System.out.println("侦听主机为:"+addr);
DatagramPacket packet = new
DatagramPacket(msg,msg.length,addr,3000);//创建要收发的数据报
DatagramSocket socket = new
DatagramSocket();//创建用于通信的Socket对象
socket.send(packet);//发送数据报
packet = new
DatagramPacket(msg,msg.length);//创建接收的数据报
socket.receive(packet);//接收数据报,存放到packet中
String time = new
String(packet.getData());//获取数据报中的数据内容
System.out.println("侦听主机:"+host+"的当前日期为:"+time);
socket.close();//释放Socket
}
}
服务器端
客户端
3
基于UDP协议的Socket编程
3.1基于UDP协议的Socket编程的基本步骤
开发基于UDP协议的网络应用程序时,需分别编写客户端和服务器端两个应用程序,这两个程序的基本架构相似。基本步骤如下:
(1)建立一个DatagramSocket对象;
(2)创建用于接收或发送数据的DatagramPacket对象;
(3)利用DatagramSocket类方法receive()或send()接收或发送数据报包。
3.2
应用举例
【例 2】
编写一个基于UDP协议的Socket网络点对点聊天程序,可实现客户端和服务器端信息的互发。
1.编写服务器端程序Serverapp_UDP.java
2.编写客户端程序Clientapp_UDP.java
1.服务器端程序具体开发步骤
(1)建立一个DatagramSocket对象;
(2)进入收发数据报包的循环,直到客户端数据传送完毕。循环过程如下:
1)创建一个用于接收数据的DatagramPakcet对象;
2)利用DatagramSocket类方法receive()等待接收客户端的数据报包;
3)处理客户端数据报包内容;
4)创建一个用于发送响应信息数据的DatagramPakcet对象;
5)利用DatagramSocket类方法send()向客户端发送包含响应信息的数据报包;
(3)客户端数据传送完毕,释放Socket。
import
java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
public class Serverapp_UDP extends WindowAdapter implements
ActionListener,KeyListener{
TextField str_send;
Label label;
TextArea msg;
Button send,exit;
Panel
p1; String hostname;
DatagramSocket
receiveSocket,sendSocket;
DatagramPacket
receivePacket,sendPacket; public void display(){
Frame f=new
Frame("在线聊天——服务器");
f.setSize(400,350);
f.setLocation(400,400);
f.setBackground(Color.green);
p1=new
Panel();
f.add(p1,"South");
msg=new
TextArea();
msg.setSize(100,250);
msg.setBackground(Color.white);
msg.setEditable(false);
f.add(msg);
label=new
Label("发送消息");
p1.add(label);
str_send=new
TextField(20);
p1.add(str_send);
str_send.addKeyListener(this);
send=new
Button("发送");
p1.add(send);
send.addActionListener(this);
exit=new
Button("退出");
p1.add(exit);
exit.addActionListener(this);
f.addWindowListener(this);
f.setVisible(true);
try{
sendSocket=new DatagramSocket(5000);
}catch(Exception e){
msg.append(e+"n");
} } //接受信息
public void
receiveMessage(){
try {
receiveSocket=new
DatagramSocket(3333);
while(true){
byte[]
buf=new byte[500];
receivePacket=new DatagramPacket(buf,buf.length);
receiveSocket.receive(receivePacket);
if(receivePacket.getLength()==0){
System.out.println("消息为空");
continue;
}
ByteArrayInputStream bin=new
ByteArrayInputStream(receivePacket.getData());
BufferedReader reader=new BufferedReader(new
InputStreamReader(bin));