Java数据报套接字

这篇博文是本文学习《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 <[email protected]>
 * 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 <[email protected]>
 * 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 <[email protected]> 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;
    }
}

文中所有源代码链接


  1. 返回到目录 ?
时间: 2024-11-16 04:31:10

Java数据报套接字的相关文章

Java-API DatagramSocket 数据报套接字 类

1.数据报套接字 类 DatagramSocket 类,用来发送和接收数据报包的套接字. DatagramSocket 构造方法 构造方法格式 说明 public DatagramSocket() 构造数据报套接字,绑定到本地任何确定的可用端口 public DatagramSocket(int port) 绑定到指定端口的套接字 public DatagramSocket(int port, InetAddress laddr) 绑定到指定的端口.指定的地址 1.1.发送数据 使用 Datag

Linux进程间通信 -- 数据报套接字 socket()、bind()、sendto()、recvfrom()、close()

前一篇文章,Linux进程间通信——使用流套接字介绍了一些有关socket(套接字)的一些基本内容,并讲解了流套接字的使用,这篇文章将会给大家讲讲,数据报套接字的使用. 一.简单回顾——什么是数据报套接字 socket,即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行.也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信.也因为这样,套接字明确地将客户端和服务器区分开来. 相对于流套接字,数据报套接字的

一起talk C栗子吧(第一百五十九回:C语言实例--基于AF_INET域的数据报套接字通信)

各位看官们,大家好,上一回中咱们说的是基于AF_INET域的流套接字通信的例子,这一回咱们说的例子是:基于AF_INET域的数据报套接字通信 .闲话休提,言归正转.让我们一起talk C栗子吧! 看官们,我们在上一回中一起制作了我们的第三道佳肴是:基于AF_INET域的流套接字通信.今天,我将和大家一起制作第四道佳肴:基于AF_INET域的数据报套接字通信. 制作第四道佳肴的菜谱:数据报套接字过程. 制作第四道佳肴的食材:数据报套接字的接口,套接字属性,套接字地址信息. 看官们,以上的内容,我们

一起talk C栗子吧(第一百五十七回:C语言实例--基于AF_UNIX域的数据报套接字通信)

各位看官们,大家好,上一回中咱们说的是基于AF_UNIX域的流套接字通信的例子,这一回咱们说的例子是:基于AF_UNIX域的数据报套接字通信 .闲话休提,言归正转.让我们一起talk C栗子吧! 看官们,我们在上一回中一起制作了我们的第一道佳肴是:基于AF_UNIX域的流套接字通信.今天,我将和大家一起制作第二道佳肴:基于AF_UNIX域的数据报套接字通信. 制作第二道佳肴的菜谱:数据报套接字过程. 制作第二道佳肴的食材:数据报套接字的接口,套接字属性,套接字地址信息. 看官们,以上的内容,我们

IPC——数据报套接字通信

Linux进程间通信——使用数据报套接字 前一篇文章,Linux进程间通信——使用流套接字介绍了一些有关socket(套接字)的一些基本内容,并讲解了流套接字的使用,这篇文章将会给大家讲讲,数据报套接字的使用. 一.简单回顾——什么是数据报套接字 socket,即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行.也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信.也因为这样,套接字明确地将客户端和服务器

1.3.1 数据报套接字编程

1.3  实现超链接 在网络应用过程中,特别是在Web程序中,超级链接用得非常普遍.其实使用VC技术,也可以实现超级链接功能.在本节的内容中,将介绍使用Visual C++ 6.0开发一个实现超级链接功能的应用程序.在开始之前,首先简单介绍与之相关的基础知识. 1.3.1  数据报套接字编程 流式套接字主要用于TCP协议,接下来将要学的数据报套接字主要用于UDP协议.数据报套接字(Datagram Socket)提供双向的通信,但没有可靠/有序/不重复的保证,所以UDP传送数据可能会收到无次序.

流式套接字(SOCK_STREAM),数据报套接字 (SOCK_DGRAM) 的比较

1.流式套接字 使用这种套接字时,数据在客户端是顺序发送的,并且到达的顺序是一致的.比如你在客户端先发送1,再发送2,那么在服务器端的接收顺序是先接收到1,再接收到2,流式套接字是可靠的,是面向连接的: 2.数据报套接字 这种套接字是无连接的,数据是打包成数据包发送的,到达的顺序不一定与发送的顺序是一致的,并且数据不一定是可达的,并且接收到的数据还可能出错. 既然这样那为什么还要使用这种套接字呢?因为现每个使用udp的程序都有自己的对数据进行确认的协议.如TFTP协议规定了每收到一个消息比如,

socket 错误之:OSError: [WinError 10057] 由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。

出错的代码 #server端 import socket import struct sk=socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen() conn,addr=sk.accept() str_len1=struct.unpack('i',conn.recv(4))[0] print(sk.recv(str_len1)) str_len2=struct.unpack('i',conn.recv(4))[0] print(sk.recv

Java中套接字的理解

什么是socket,以及两端ServerSocket和Socket是真正的干什么用的,也终于理解到其实普通的socket也就仅仅是普通管道,两边通过这个管道互相传和接受信息. 建立Socket连接 建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket,另一个运行于服务器端,称为ServerSocket. 套接字之间的连接过程分为三个步骤: (1)服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请