Java网络编程_基于UDP协议的网络编程

UDP协议是一种不可靠的网络协议,它在通信实例的两端各建立一个Socket,但在两个Socket之间并没有虚拟链路,这两个Socket只是发送、接收数据报的对象。Java提供了DatagramSocket对象作为基于UDP协议的Socket,使用DatagramPacket代表DatagramSocket发送、接收的数据包。

UDP协议是面向非连接的协议,没有建立连接的过程,因此它的通信效率很高;也正是因为如此,它的可靠性不如TCP协议。

UDP协议和TCP协议的简单比较:

TCP协议:可靠,传输大小无限制,但是需要连接建立时间,差错控制开销大。
UDP协议:不可靠,差错控制开销小,传输大小限制在64KB以下,不需要建立连接。


DatagramSocket与DatagramPacket

Java使用DatagramSocket代表UDP协议的Socket,DatagramSocket唯一作用就是接收和发送数据报。Java使用DatagramPacket来代表数据报,DatagramSocket接收和发送的数据都是通过DatagramPacket对象完成的。

DatagramSocket构造器
DatagramSocetk()创建一个DatagramSocket实例,并将对象绑定到本机默认IP地址、本机所有可用端口中随机选择的某个端口。
DatagramSocket(int port)创建一个DataGramSocket实例,并将该对象绑定到本机默认IP地址、指定端口
DatagramSocket(int port,InetAddress Iaddr)创建一个DatagramSocket实例,并将对象绑定到指定IP地址、指定端口

得到DatagramSocket实例之后,可以通过以下方法来接收和发送数据:

DatagramSocket方法
receive(DatagramPacket p)从该DatagramSocket中接收数据报
send(DatagramPacket p)以该DatagramSocket对象向外发送数据报

DatagramPacket构造器及方法

DatagramPacket的构造器
DatagramPacket(byte[] buf,int length)以一个空数组来创建DatagramPacket对象,该对象的作用是接收DatagramSocket中的数据
DatagramPacket(byte[] buf,int length,InetAddress addr,int port)以一个包含数据的数组来创建DatagramPacket对象,创建该DatagramPacket对象时还指定了IP地址和端口——这就是决定了该数据报的目的地
DatagramPacket(byte[] buf,int offset,int length)以一个空数组来创建DatagramPacket对象,并指定接收到的数据放入buf数组中时从offset开始,最多放length个字节
DatagramPacket(byte[] buf,int offset,int length,InetAddress addr,int port)创建一个用于发送的DatagramPacket对象,指定发送buf数组中从offset开始,总共length个字节

在接收数据之前,应该采用DatagramPacket的第一个或第三个构造器生成一个DatagramPacket对象,给出接收数据的字节数组及其长度。然后调用DatagramSocket的receive()方法等待数据报的到来,receive()将一直等待(该方法会阻塞调用该方法的线程),直到受到一个数据报为止。

//创建一个接收数据的DatagramPacket对象
DatagramPacket packet = new DatagramPacket(buf, 256);
//接收数据报
socket.receive(packet);

在发送数据之前,调用第二个或第四个构造器创建DatagramPacket对象,此时的字节数组里存放了想发送的数据。除此之外,还要给出完整的目的地址,包括IP地址和端口号。发送数据是通过DatagramSocket的send()方法实现的,send()方法根据数据包的目的地址来寻径以传送数据报。

//创建一个发送数据的DatagramPacket对象
DatagramPacket packet = new Datagrampacket(buf, length, address, port);
//发送数据报
socket.send(packet);

当服务器端接收到一个DatagramPacket对象后,如果想向该数据包的发送者“反馈”一些信息,但由于UDP协议是面向非连接的,所以接收者并不知道每个数据报由谁发送过来,但程序可以调用DatagramPacket的如下三个方法来获取发送者的IP地址和端口。

方法
InetAddress getAddress()当程序准备发送此数据报时,该方法返回此数据报的目标机器的IP地址;当程序接收到一个数据报时,该方法返回该数据报的发送主机的IP地址
int getPort()当程序准备发送此数据报时,该方法返回此数据报的目标机器的端口;当程序刚接收到一个数据报时,该方法返回该数据报的发送主机的端口
SocketAddress getSocketAddress()当程序准备发送此数据报时该方法返回此数据报的目标SocketAddress;当程序刚接收到一个数据报时,该方法返回该数据报的发送主机的SocketAddress

实例代码:

public class UDPServer {

    public static final int PORT = 30000;
    //定义每个数据报的大小最大为4KB
    private static final int DATA_LEN = 4096;
    //定义接收网络数据的字节数组
    byte[] inBuff = new byte[DATA_LEN];
    //以指定字节数组创建准备接收数据的DatagramPacket对象
    private DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length);
    //定义一个用于发送的DatagramPacket对象
    private DatagramPacket outPacket;
    //定义一个字符串数组,服务器端发送该数组的元素
    String[] books = new String[]{"qqq", "www", "eee", "rrr"};
    public void init() throws IOException{

        try(
            //创建DatagramSocket对象
            DatagramSocket socket = new DatagramSocket(PORT)){

            //采用循环接收数据
            for(int i = 0; i < 1000; i++){

                //读取Socket中的数据,读到的数据放入inPacket封装的数组里
                socket.receive(inPacket);
                //判断inPacket.getData()和inBuff是否是同一个数组
                System.out.println(inBuff == inPacket.getData());
                //将接收到的内容转换为字符串后输出
                System.out.println(new String(inBuff, 0, inPacket.getLength()));
                //从字符串数组中取出一个元素作为发送数据
                byte[] sendData = books[i % 4].getBytes();
                //以指定的字节数组作为发送数据,以刚接收到的DatagramPacket的源SocketAddress作为
                //目标SocketAddress创建DatagramPacket
                outPacket = new DatagramPacket(sendData, sendData.length, inPacket.getSocketAddress());
                //发送数据
                socket.send(outPacket);
            }
        }
    }
    public static void main(String[] args) throws IOException{

        new UDPServer().init();
    }
}   
public class UDPClient {

    //定义发送数据报的目的地
    public static final int DEST_PORT = 30000;
    public static final String DEST_IP = "127.0.0.1";
    //定义每个数据报的大小最大为4KB
    private static final int DATA_LEN = 4096;
    //定义接收网络数据的字节数组
    byte[] inBuff = new byte[DATA_LEN];
    //以指定的字节数组创建准备接收数据的DatagramPacket对象
    private DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length);
    //定义一个用于发送的DatagramPacket对象
    private DatagramPacket outPacket = null;
    public void init() throws IOException{

        try(
            //创建一个客户端DatagramSocket,使用随机端口
            DatagramSocket socket = new DatagramSocket()){

            //初始化发送时用的DatagramSocket,它包含一个长度为0的字节数组
            outPacket = new DatagramPacket(new byte[0], 0, InetAddress.getByName(DEST_IP), DEST_PORT);
            //创建键盘输入流
            Scanner scan = new Scanner(System.in);
            //不断地读取键盘输入
            while(scan.hasNextLine()){

                //将键盘输入的一行字符串转换为字节数组
                byte[] buff = scan.nextLine().getBytes();
                //设置发送用的DatagramPacket中的字节数据
                outPacket.setData(buff);
                //发送数据报
                socket.send(outPacket);
                //读取Socket中的数据,读取到的数据放在inPacket所封装的字节数组中
                socket.receive(inPacket);
                System.out.println(new String(inBuff, 0, inPacket.getLength()));
            }
        }
    }
    public static void main(String[] args) throws IOException{

        new UDPClient().init();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值