【网络编程2】Java数据报套接字

来源:互联网 发布:如何创建域名 编辑:程序博客网 时间:2024/06/06 00:34

这篇博文是本文学习《Java网络程序设计》书中第5章数据报套接字的学习总结。初学者网友学习这篇Java数据报套接字文章,如果难于理解文章前面理论部分,可以先运行后面的程序,边看运行后面的程序边理解前面的原理,这对初学者是最好的方法。所有源代码都在文章后面我的github链接代码中。
——惠州学院13网络工程 吴成兵 20160609

目录 1

  • 目录 1
  • 一 数据报套接字概述
  • 二 DatagramPacket
    • 21 创建DatagramPacket对象
      • 211 创建的DatagramPacket对象用于接收数据
      • 212 创建的DatagramPacket对象用于发送数据
    • 22 DatagramPacket常用方法
  • 三 DatagramSocket
    • 31 创建DatagramSocket对象
    • 32 DatagramSocket常用方法
  • 四 DatagramSocket编程示例
    • 41 利用DatagramSocket查询端口占用情况
    • 42 利用数据报通信的CS程序
      • 421 数据接收端
      • 422 数据发送端
    • 43 用UDP实现的聊天程序

一 数据报套接字概述

  流套接字的每个连接均要花费一定的时间,为了减少这种开销,网络API提供了第二种套接字——数据报套接字(DatagramSocket),又称自寻址套接字
数据报套接字通信流程
  数据报套接字基于的协议是UDP协议,采用的是一种尽力而为(Best-Effort)的传送数据的方式,它只是把数据的目的地记录在数据报包(DatagramPacket)中,然后就直接放在网络上,系统不保证数据是否能安全送到,或者什么时候可以送到,也就是说它并不保证传送的质量。UDP在每一个自寻址包中包含了错误检测信息,在每个自寻址包到达目的地之后UDP进行简单的错误检查,如果检查失败,UDP将抛弃这个自寻址包,也不会从发送者那里重新请求替代者。这与通过邮局发送信件相似,发信人在发信这前不需要与收信人建立连接,同样也不能保证信件能到达发信人那里。

  可见,UDP的优点是效率高,并且比较灵活,一般用于质量和实时性要求不是很高的情况,比如实时音频和视频应用中。

  DatagramSocket本身只是码头,不维护状态,不能产生I/O流,它的唯一作用就是接收和发送数据报包,这个数据报包在Java中是使用DatagramPacket对象实现的。

  数据报套接字编程中主要使用下面三个类:DatagramPacket 、DatagramSocket和MulticastSocket。

  • DatagramPacket对象描绘了自寻址包的地址信息;
  • DatagramSocket表示客户端程序与服务器程序自寻址套接字;
  • MulticastSocket 描绘了能进行多点传送的自寻址套接字。

二 DatagramPacket

2.1 创建DatagramPacket对象

2.1.1 创建的DatagramPacket对象用于接收数据

  以一个空数组来创建DatagramPacket对象,该对象的作用是接收DatagramSocket中的数据。

  • public DatagramPacket(byte buf[], int length) :接收到的数据从buf[0]开始存放,直到整个数据包接收完毕或者将length的字节写入buf为止。
  • public DatagramPacket(byte buf[], int offset, int length):接收到的数据从buf[offset]开始存放,如果数据包长度超出了length,则会触发IllegalArgument-Exception。

不过这是RuntimeException,不需要用户代码捕获。
示范代码如下:

byte[] buffer = new byte[8912];DatagramPacket datap = new DatagramPacket(buffer, buffer.length);

2.1.2 创建的DatagramPacket对象用于发送数据

  以一个包含数据的数组 buf[]来创建DatagramPacket对象,该对象作为DatagramSocket发送数据的载体,并指定该DatagramPacket目的IP地址和端口号。

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

示范代码是要发送一串字符串:

String string = new String("java networking");byte[] data=string.getBytes();int port=1024;InetAddress inetA=null;try {    inetA = InetAddress.getByName("127.0.0.1");    DatagramPacket datap = new DatagramPacket(data,data.length,inetA,port);} catch (UnknownHostException e) {}

2.2 DatagramPacket常用方法

获取DatagramPacket对象属性的方法如下:

  • public synchronized InetAddress getAddress()
  • public synchronized int getPort()
  • public synchronized byte[] getData()
  • public synchronized int getLength()
  • public synchronized int getOffset()

设置DatagramPacket对象属性的方法如下:

  • public synchronized void setAddress(InetAddress iaddr)
  • public synchronized void setPort(int iport)
  • public synchronized void setData(byte[] buf, int offset, int length)
  • public synchronized void setData(byte[] buf)
  • public synchronized void setLength(int length)
  • public synchronized void setSocketAddress(SocketAddress address)

部分方法说明:

  • getAddress()是返回该DatagramPacket的IP地址。如果DatagramPacket是发送出来的数据报包,这个方法则返回目的主机的IP地址。如果DatagramPacket是接收到的数据报包,这个方法则返回远程主机的IP地址。
  • getSocketAddress()是返回要将此包发送到的或此数据报包的远程主机的SocketAddress(通常是IP地址+端口号)。
  • setAddress()是设置该DatagramPacket要发送往的目的主机的IP地址。
  • setSocketAddress()是设置要将此包发送到的或此数据报包的远程主机的SocketAddress(通常是IP地址+端口号)。

三 DatagramSocket

3.1 创建DatagramSocket对象

  • public DatagramSocket() throws SocketException :系统随机产生一个端口,创建一个数据报套接字。这种构造方法没有指定端口号,可以用在发送端,如果构造不成功则触发SocketException异常。
  • public DatagramSocket(int port) throws SocketException :用一个指定端口号port创建一个数据报套接字。如果指定端口已被占用或者是试图连接低于1024的端口但又没具备权限。
  • public DatagramSocket(int port, InetAddress laddr) throws SocketException :创建一个数据报套接字,并将该对象绑定到指定的IP地址和端口。

通常用指定端口的方式创建发送端数据报套接字。一旦得到了DatagramSocket对象之后,就可以通过如下两个方法来接收和发送数据:

  • public void send(DatagramPacket p) throws IOException :从该DatagramSocket中接收数据报包。
  • public synchronized void receive(DatagramPacket p) throws IOException :使用该DatagramSocket对象向外发送数据报包。

从上面两个方法可以看出,使用DatagramSocket发送数据报包时,DatagramSocket并不知道将该数据报包发送到哪里,而是由DatagramPacket自身决定数据报包的目的地,所有的端口、目的地址和数据,需要由DatagramPacket来指定。就像码头并不知道每个集装箱发送到哪里,码头只是将这些集装箱发送出去,而集装箱本身包含了该集装箱的目的地。

  DatagramSocket在接收数据之前,应该采用public DatagramPacket(byte buf[], int length)或public DatagramPacket(byte buf[], int offset, int length)构造方法创建一个DatagramPakcet对象,给出接收数据的字节及其长度。然后调用DatagramSocket的方法receive()等待数据报包的到来,receive()将一直等待(也就是说会阻塞调用该方法的线程),直到收到一个数据报包为止,如下代码所示:

//创建要接收数据的DatagramPacket对象DatagramPacket packet =new DatagramPacket(buf,buf.length);//接收数据receiveSocket.receive(packet);

  发送数据之前,调用public DatagramPacket(byte buf[], int length, InetAddress address, int port)或public DatagramPacket(byte buf[], int offset, int length, InetAddress address, int port)构造方法创建DatagramPacket对象,此时的字节数组存放了要发送的数据。除此之外,还要给出完整的目的地址,包括IP地址和端口号。发送数据是通过DatagramSocket的方法send()实现的,send()根据数据报包的目的地址来寻径以传递数据报包,如下代码所示:

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

3.2 DatagramSocket常用方法

  • public InetAddress getInetAddress()
  • public int getPort()
  • public InetAddress getLocalAddress()
  • public int getLocalPort()
  • public SocketAddress getRemoteSocketAddress()
  • public SocketAddress getLocalSocketAddress()
  • public void send(DatagramPacket p) throws IOException
  • public synchronized void receive(DatagramPacket p) throws IOException

四 DatagramSocket编程示例

4.1 利用DatagramSocket查询端口占用情况

这里写图片描述

package _5_2DatagramSocket编程示例._5_2_1利用DatagramSocket查询端口占用情况;import java.net.DatagramSocket;import java.net.SocketException;/** *  * Copyright ? 2016 Authors. All rights reserved. * * FileName: .java * @author : Wu_Being <1040003585@qq.com> * Date/Time: 2016-6-10/上午12:36:45 * Description: 查询端口的占用情 ? */public class UDPScan {    /**     * @param args     */    public static void main(String[] args) {        //2014~65535        for (int port = 1024; port < 65536; port++) {            try {                DatagramSocket server = new DatagramSocket(port);                server.close();            } catch (SocketException e) {                System.out.println("there is a server in port:" + port + ".");            }        }    }}

4.2 利用数据报通信的C/S程序

这里写图片描述
这里写图片描述
这里写图片描述

4.2.1 数据接收端

package _5_2DatagramSocket编程示例._5_2_2利用数据报通信的CS程序;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.SocketException;/** *  * Copyright ? 2016 Authors. All rights reserved. * * FileName: .java * @author : Wu_Being <1040003585@qq.com> * Date/Time: 2016-6-10/下午10:36:26 * Description: */public class UDPReceiver {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        try {            DatagramSocket receiveSocket =new DatagramSocket(5000);            byte buf[]=new byte[1000];            DatagramPacket receivepPacket=new DatagramPacket(buf,buf.length);            System.out.println("starting to receive packet...");            while(true){                receiveSocket.receive(receivepPacket);                String receiveData=new String(receivepPacket.getData(),0,receivepPacket.getLength());                String name=receivepPacket.getAddress().getHostName();                int port=receivepPacket.getPort();                System.out.print("来自主机:"+name);                System.out.print(",的端口:"+port);                System.out.println("的数据:"+receiveData);            }        } catch (SocketException e) {            System.out.println("不能打开数据报Socket,或数据报Socket无法与指定端口连接");            System.exit(1);        } catch (IOException e) {            System.out.println("网络通信出现问题,问题在于:"+e.toString());        }        }}

4.2.2 数据发送端

package _5_2DatagramSocket编程示例._5_2_2利用数据报通信的CS程序;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.net.SocketException;import java.util.Scanner;/** *  * Copyright ? 2016 Authors. All rights reserved. *  * FileName: .java *  * @author : Wu_Being <1040003585@qq.com> Date/Time: 2016-6-10/下午10:36:20 *         Description: */public class UDPSender {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        while (true) {            Scanner scanner = new Scanner(System.in);            try {                System.out.print("请输入要发送的数据:");                String string = scanner.nextLine();                DatagramSocket sendSocket = new DatagramSocket();// 端口号随机                // byte[] databyte=new byte[100]                byte[] databyte = string.getBytes();                DatagramPacket sentPacket = new DatagramPacket(databyte,                        databyte.length, InetAddress.getByName("127.0.0.1"),                        5000);                sendSocket.send(sentPacket);                System.out.println("send the data:" + string);            } catch (SocketException e) {                System.out.println("不能打开数据报Socket,或数据报Socket无法与指定端口连接");            } catch (IOException e) {                System.out.println("网络通信出现问题,问题在于:" + e.toString());            }        }    }}

4.3 用UDP实现的聊天程序

这里写图片描述

package _5_2DatagramSocket编程示例._5_2_3用UDP实现的聊天程序;import java.awt.BorderLayout;import java.awt.Container;import java.awt.FlowLayout;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.net.SocketException;import java.net.UnknownHostException;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.Date;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JLabel;import javax.swing.JPanel;import javax.swing.JScrollPane;import javax.swing.JTextArea;import javax.swing.JTextField;public class UDPChat implements ActionListener, Runnable {    JTextArea showArea;    JLabel lbl1, lbl2, lbl3;    JTextField msgTextField, sendPortTextField, receivePorttTextField, IPAddressTextField;    JFrame mainFrame;    JButton sendButton, startButton;//,resetbButton;    JScrollPane JSPane;    JPanel panel1, panel2;    Container container;    Thread thread = null;    DatagramSocket sendSocket, receiveSocket;    DatagramPacket sendPacket, receivePacket;    private InetAddress sendIP;    private int sendPort, receivePort;// 存储发送端口和接收端口    private byte inbuf[], outbuf[];    public static final int BUFSIZE = 1024;    public UDPChat() {        mainFrame = new JFrame("聊天——UDP协议");        container = mainFrame.getContentPane();        showArea = new JTextArea();        showArea.setEditable(false);        showArea.setLineWrap(true);        lbl1 = new JLabel("接收端口号:");        lbl2 = new JLabel("发送端口号:");        lbl3 = new JLabel("对方的地址:");        sendPortTextField = new JTextField();        sendPortTextField.setColumns(4);        receivePorttTextField = new JTextField();        receivePorttTextField.setColumns(4);        IPAddressTextField = new JTextField();        IPAddressTextField.setColumns(8);        startButton = new JButton("开始");        startButton.addActionListener(this);/**///      resetbButton=new JButton("重置");//      resetbButton.addActionListener(this);/**/        panel1 = new JPanel();        panel1.setLayout(new FlowLayout());        panel1.add(lbl1);        panel1.add(receivePorttTextField);        panel1.add(lbl2);        panel1.add(sendPortTextField);        panel1.add(lbl3);        panel1.add(IPAddressTextField);        panel1.add(startButton);//      panel1.add(resetbButton);        JSPane = new JScrollPane(showArea);        msgTextField = new JTextField();        msgTextField.setColumns(40);        msgTextField.setEditable(false);        msgTextField.addActionListener(this);/**/        sendButton = new JButton("发送");        sendButton.setEnabled(false);        sendButton.addActionListener(this);/**/        panel2 = new JPanel();        panel2.setLayout(new FlowLayout());        panel2.add(msgTextField);        panel2.add(sendButton);        container.add(panel1, BorderLayout.NORTH);        container.add(JSPane, BorderLayout.CENTER);        container.add(panel2, BorderLayout.SOUTH);        mainFrame.setSize(600, 400);        mainFrame.setVisible(true);        mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    }    @Override    public void actionPerformed(ActionEvent e) {        try {            if (e.getSource() == startButton) {/* 按下了开始按键 */                inbuf = new byte[BUFSIZE];                receivePort = Integer.parseInt(receivePorttTextField.getText());                sendPort = Integer.parseInt(sendPortTextField.getText());                sendIP = InetAddress.getByName(IPAddressTextField.getText());                //创建发送和接收DatagramSocket和DatagramPacket                sendSocket = new DatagramSocket();                receiveSocket = new DatagramSocket(receivePort);                receivePacket = new DatagramPacket(inbuf, BUFSIZE);                thread = new Thread(this);                thread.setPriority(Thread.MIN_PRIORITY);                thread.start();                startButton.setEnabled(false);                sendButton.setEnabled(true);                msgTextField.setEditable(true);//          } else if (e.getSource() == resetbButton) {//              //              sendPortTextField.setText(null);//              receivePorttTextField.setText(null);//              IPAddressTextField.setText(null);//              msgTextField.setText(null);//              showArea.setText(null);//              //              startButton.setEnabled(true);//              sendButton.setEnabled(false);//              msgTextField.setEditable(false);//              //              thread.interrupt();//              if(receivePacket!=null) {//                  receiveSocket.close();//              }            } else {/* 按下了发送按键或回车键 */                outbuf = msgTextField.getText().getBytes();                // 组装要发送的的数据包                sendPacket = new DatagramPacket(outbuf, outbuf.length, sendIP, sendPort);                // 发送数据                sendSocket.send(sendPacket);                showArea.append("我说(" + msgTextField.getText() + ")"    + getDateTime() + "\n");                msgTextField.setText(null);            }        } catch (UnknownHostException e1) {            showArea.append("getByName无法连接到指定地址!!!" + getDateTime() + "\n");        } catch (SocketException e1) {            showArea.append("DatagramSocket无法打开指定端口!!!" + getDateTime() + "\n");        } catch (IOException e1) {            showArea.append("send数据数据失败!!!" + getDateTime() + "\n");        } catch (NumberFormatException e1) {            showArea.append("设置端口数据格式不对!!!" + getDateTime() + "\n");        }    }    @Override    public void run() {        String msgstr;        while (true) {            try {// 注意try的位置                receiveSocket.receive(receivePacket);                msgstr = new String(receivePacket.getData(), 0, receivePacket.getLength());                showArea.append("对方说(" + msgstr + ")" + getDateTime() + "\n");            } catch (Exception e) {                showArea.append("receive接收数据出错!!!" + getDateTime() + "\n");            }        }    }    public static void main(String[] args) {        new UDPChat();    }    /**     * Java代码中获得当前时间     *      * @return 当前时期时间     */    private String getDateTime() {        Date date = new Date();        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        String time = format.format(date);        return time;    }}

文中所有源代码链接

Wu_Being博客声明:本人博客欢迎转载,请标明博客原文和原链接!谢谢!
《Java数据报套接字》:
http://blog.csdn.net/u014134180/article/details/51636009

Wu_Being 吴兵博客接受赞助费二维码

如果你看完这篇博文,觉得对你有帮助,并且愿意付赞助费,那么我会更有动力写下去。


  1. 返回到目录
1 0
原创粉丝点击