zoukankan      html  css  js  c++  java
  • 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();//运行服务器
        }
    }

      运行结果:

  • 相关阅读:
    函数式宏定义与普通函数
    linux之sort用法
    HDU 4390 Number Sequence 容斥原理
    HDU 4407 Sum 容斥原理
    HDU 4059 The Boss on Mars 容斥原理
    UVA12653 Buses
    UVA 12651 Triangles
    UVA 10892
    HDU 4292 Food
    HDU 4288 Coder
  • 原文地址:https://www.cnblogs.com/zhishengyong/p/3794749.html
Copyright © 2011-2022 走看看