• JAVA UDP网络编程学习笔记


    一、UDP网络编程概述

      采用TCP协议通信时,客户端的Socket必须先与服务器建立连接,连接建立成功后,服务器端也会持有客户端连接的Socket,客户端的Socket与服务器端的Socket是对应的,它们构成了两个端点之间的虚拟通信链路。与TCP通信不同,UDP是面向无连接的、不可靠的基于数据包的传输协议。即应用进程(或程序)在使用UDP协议之前,不必先建立连接。自然,发送数据结束时也没有连接需要释放。因此,减少了开销和发送数据之前的延时。UDP也采用端口来区分进程。

      在java中,java.net.DatagramSocket负责接收和发送UDP数据报文,java.net.DatagramPacket表示UDP数据报。每个DatagramSocket与一个数据报套接字(包括本地主机的IP地址和本地UDP端口)绑定,每个DatagramSocket可以把UDP数据报发送给任意一个远程DatagramSocket,也可以接收来自任意一个远程DatagramSocket的数据报。在UDP数据报中包含了目的地址信息,DatagramSocket可以根据该信息把数据报发送的目的地。

      UDP协议是无连接的协议。客户端的DatagramSocket与服务端DatagramSocket不存在一一对应关系,两者无需建立连接,就能交换数据报。每个DatagramSocket对象都会与一个本地端口绑定,在此端口监听发送过来的数据报。在服务器程序中,一般由程序显示地为DatagramSocket指定本地端口。在客户程序中,一般由操作系统为DatagramSocket分配本地端口,这种端口也称为匿名端口。

    二、关于DatagramSocket类和DatagramPacket类

      DatagramSocket类的构造方法如下:

      DatagramSocket()throws SocketException

      作用:构造数据报套接字并将其绑定到本地主机上任何可用的端口。套接字将被绑定到INADDR_ANY地址,IP地址由内核来选择。

      DatagramSocket(int port)throws SocketException

      作用:创建数据报套接字并将其绑定到本地主机上的指定端口。套接字将被绑定到INADDR_ANY地址,IP地址由内核来选择。

      DatagramSocket类的常用方法如表所示:

    DatagramSocket类的常用方法
    void send(DatagramPacket p) throws IOException 发送一个UDP数据包。一个UDP数据包就是一个DatagramPacket对象
    void receive(DatagramPacket p) throws IOException 接收一个UDP数据包。一个UDP数据包就是一个DatagramPacket对象
    void connect(InetAddress address,int port) 将该UDPSocket变成连接型的UDPSocket
    void disconnect() 将该UDPSocket变成一个非连接型的UDPSocket
    void close() 关闭UDPSocket连接

      其中,UDPSocket分为“连接型”与“非连接型”两种。默认UDPSocket是“非连接型”的,这个连接不是指向TCP那样进行三步握手,而只是将对方信息与自己关联在一起。

      DatagramPacket类的对象代表了一个UDP数据报包。通过UDP发送数据时,先要根据发送的数据生成一个DatagramPacket对象,然后通过DatagramSocket对象的send()方法发送这个对象。接收时,先要根据要接收数据的缓冲区生成一个Datagrampacket对象,然后通过DatagramPacket对象的receive()方法接收这个对象的数据内容。

      DatagramPacket类的构造方法分为两类:

      一类是创建DatagramPacket对象用来接收数据报包;

      另一类是创建DatagramPacket对象用来发送数据报包。

      它们的区别是,用于发送数据报包的构造方法需要设置数据报包达到的目的地址,若是“连接型”UDP,则不需要设定目的地址,而用于接收数据报包的构造方法无须设定地址。

      用于接收数据报包的构造方法如下:

      DatagramPacket(byte[] buf,int 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的长度。

      DatagramPacket(byte[] buf,int offset,int length,InetAddress address,int port)

      作用:构造数据报包发送的对象,用来将长度为length且偏移量为offset的包发送到指定主机上的指定端口号。length参数要小于等于buf的长度。

    DatagramPacket类的常用方法如下表:

    DatagramPacket类的常用方法
    byte[] getData() 返回DatagramPacket对象中包含的数据
    int getLength() 返回发送/接收数据的长度
    int getOffset() 返回发送/接收数据在byte[]中的偏移
    InetAddress getAddress() 返回对方的IP地址。用InetAddress对象表示
    int getPort() 返回对方的端口号
    void setData(byte[] buf,int offset,int length) 设置该对象中包含的数据
    void setAddress(inetaddress iaddr) 设置该对象中包含的IP地址
    void setPort() 设置该对象中包含的端口号

      通过UDP发送/接收数据步骤:

      发送数据,先要根据发送的数据生成一个DatagramPacket对象,并指定发送长度和接收数据的IP地址和端口号,然后通过DatagramSocket对象的send()方法发送这个对象。

      接收数据,根据要接受收数据的缓冲区及大小生成一个DatagramPacket对象,然后通过DatagramSocket对象的receive()方法接收这个对象的数据内容。

    三、UDP网络编程练习

      练习代码:

    package com.ItHeima.WeekAct;
    
    /**服务器**/
    
    
    import java.net.*;
    
    public class ChatterClient extends Thread {
    
        private DatagramSocket socket;
        private InetAddress address;
        private byte[] buf = new byte[1000];
        private DatagramPacket packet = new DatagramPacket(buf, buf.length);//创建要发送的数据包
        private int id;//客户端id
    
        public ChatterClient(int id) {
            this.id = id;
            try {
                socket = new DatagramSocket();//创建UDP套接字
                address = InetAddress.getByName(null);//取得本地地址
            } catch (SocketException e) {
                System.out.println("can not open socket");
                e.printStackTrace();
                System.exit(1);
            } catch (UnknownHostException e) {
                System.out.println("Can not find host");
                System.exit(1);
            }
            System.out.println("ChatterClient starting");
            start();//之后调用run()
        }
        
        public void run(){
            try {
                for(int i = 0 ; i < 25 ; i++){
                    String outMsg = "服务器你好,这是我客户端发过来的数据,请接收!" + id + ",消息" + i;//要发送到服务器的数据
                    socket.send(new DatagramPacket(outMsg.getBytes(),outMsg.getBytes().length, address, ChatterServer.INPORT));//打包数据并将其发送到指定地址+端口的服务端
                    socket.receive(packet);//接收服务端返回的数据包
                    String msg=new String(packet.getData(),packet.getOffset(),packet.getLength());//获取服务器返回的信息
                    String rcvd = "客户端--" + id +", 收到来自服务器的信息" + packet.getAddress() + "," + packet.getPort() + ":" + msg;//组合返回信息
                    System.out.println(rcvd);//输出到控制台
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(1);//出错退出
            }
        }
        
        public static void main(String[] args) {
            for(int i = 0 ; i < 10 ; i ++ ){
                new ChatterClient(i);
            }
        }
    }
    package com.ItHeima.WeekAct;
    
    /**服务器**/
    
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.SocketException;
    
    
    
    public class ChatterServer {
    
        public static final int INPORT = 1711;//服务器端口
        private byte[] buf = new byte[1000];
        private DatagramPacket packet = new DatagramPacket(buf, buf.length);//创建数据包
        private DatagramSocket socket;//UDP套接字
        public ChatterServer(){
            try{
                socket = new DatagramSocket(INPORT);//启动套接字
                System.out.println("Server started");
                while(true){
                    socket.receive(packet);//接收数据包并将当前线程挂起
                    String msg=new String(packet.getData(),packet.getOffset(),packet.getLength());//获取客户端发送的信息
                    String rcvd ="服务器--收到来自客户端的信息:"+ msg+ ", from adddress:" + packet.getAddress() + ",port:" + packet.getPort();//解析数据包
                    System.err.println(rcvd);//打印数据信息
                    String returnMasg = "服务器返回信息:你好客户端,这是你发过来的数据:" + msg+",我将它原样返回";
                    DatagramPacket echo = new DatagramPacket(returnMasg.getBytes(), returnMasg.getBytes().length,packet.getAddress(), packet.getPort());//将接收到包重新包装称UDP数据包准备原封不动的返回给客户端
                    socket.send(echo);//反馈数据包
                }
            }catch (SocketException e) {
                System.out.println("Can`t open socket");
                System.exit(1);
            }catch (IOException e) {
                System.out.println("Communication error");
                e.printStackTrace();
            }
        }
        public static void main(String[] args) {
            new ChatterServer();//运行服务器
        }
    }

      运行结果:

  • 相关阅读:
    在应用程序中利用Jena API处理OWL本体
    Encoded vs Literal, RPC vs Document
    DWR、XMLHTTP、XMLRPC和Flex
    北京的第一场雪
    让IE浏览器提示下载或直接打开word文档
    色拉英语第一集第一幕:记得说“请”
    色拉英语第一集第三幕:凯文在家吗?
    30天敏捷结果(30):提升敏捷结果
    生活:兔年春节家庭寻宝习俗
    敏捷个人:2011/1/26聊天记录(沟通、优势)
  • 原文地址:https://www.cnblogs.com/zhishengyong/p/3794749.html
Copyright © 2020-2023  润新知