JAVA基础学习之UDP网络编程

来源:互联网 发布:mac用破解版软件风险 编辑:程序博客网 时间:2024/05/16 07:45

UDP(UserDatagramProtocol)用户数据报包协议

UDP和TCP位于同一层-传输层,但它对于数据包的顺序错误或重发没有TCP来的可靠。因此,UDP不被应用于那些使用虚电路的面向直接服务.
UDP是一种面向无连接的通信协议。UDP向应用程序提供了一种发送封装的原始IP数据包的方法,并且发送时无需建立连接,不保证可靠数据的传输

特点:面向无连接的数据传输,不可靠的,但效率高。
UDP一次发送的数据不能超过64KB.

UDP编程所需要的类:
DatagramSocket 此类表示用来发送和接收数据报包的套接字
DatagramPacket 此类表示数据报包
DatagramPacket(byte[]buf, intlength, InetAddressaddress, intport)
参数:buf -包数据
length -包长度
address -目的地址
port -目的端口号

写法步骤:
服务器端:创建监听套接字,接受数据包,发送数据包,关闭套接字
客户端: 创建套接字,发送数据包,接受数据包,关闭套接字


DatagramSocket类
DatagramSocket(int port)
创建数据报套接字并将其绑定到本地主机上的指定端口。
DatagramSocket(int port, InetAddress laddr)
创建数据报套接字,将其绑定到指定的本地地址。



DatagramPacket类

构造方法摘要

DatagramPacket(byte[] buf, int length)

构造 DatagramPacket,用来接收长度为 length 的数据包。

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

构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。

DatagramPacket(byte[] buf, int offset, int length)

构造 DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量。

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

构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。

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

构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。

DatagramPacket(byte[] buf, int length, SocketAddress address)

构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。


一般方法

 InetAddress     getAddress()

返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。

byte[]  getData()

返回数据缓冲区。

int getLength()

返回将要发送或接收到的数据的长度。

int getOffset()

返回将要发送或接收到的数据的偏移量。

int getPort()

返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。

SocketAddress   getSocketAddress()

获取要将此包发送到的或发出此数据报的远程主机的 SocketAddress(通常为 IP 地址 + 端口号)。

void    setAddress(InetAddress iaddr)

设置要将此数据报发往的那台机器的 IP 地址。

void    setData(byte[] buf)

为此包设置数据缓冲区。

void    setData(byte[] buf, int offset, int length)

为此包设置数据缓冲区。

void    setLength(int length)

为此包设置长度。

void    setPort(int iport)

设置要将此数据报发往的远程主机上的端口号。

void    setSocketAddress(SocketAddress address)

设置要将此数据报发往的远程主机的 SocketAddress(通常为 IP 地址 + 端口号)。

示例1:
本例子使用WindowBuilder图形界面进行编写。实现简单的一对一聊天
界面效果:
这里写图片描述

java代码

package com.ql.Receivers;import java.awt.EventQueue;import javax.swing.JFrame;import javax.swing.JPanel;import java.awt.BorderLayout;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.InetSocketAddress;import java.net.SocketAddress;import java.net.SocketException;import java.net.UnknownHostException;import javax.swing.JLabel;import javax.swing.JTextField;import javax.swing.JButton;import javax.swing.JScrollPane;import javax.swing.JTextArea;public class Receiver implements ActionListener {    private JFrame frame;    private JTextField txtStr;    private JTextArea txtArea;    private JButton btnSend, btnEnter;    private JTextField txtYouAddress;    private DatagramSocket receiverSocket, sendSocket;//发送和接收套接字    private JTextField txtMyAddress;    private String youAddress = "127.0.0.2", myAddress = "127.0.0.1";//己方地址和对方地址    /**     * Launch the application.     */    public static void main(String[] args) {        EventQueue.invokeLater(new Runnable() {            public void run() {                try {                    Receiver window = new Receiver();                    window.frame.setVisible(true);                } catch (Exception e) {                    e.printStackTrace();                }            }        });    }    /**     * Create the application.     */    public Receiver() {        initialize();    }    /**     * Initialize the contents of the frame.     */    private void initialize() {        frame = new JFrame();        frame.setBounds(100, 100, 530, 370);        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        frame.getContentPane().setLayout(new BorderLayout(0, 0));        JPanel panel = new JPanel();        frame.getContentPane().add(panel);        panel.setLayout(null);        JPanel panel_1 = new JPanel();        panel_1.setBounds(0, 33, 514, 256);        panel.add(panel_1);        panel_1.setLayout(new BorderLayout(0, 0));        JScrollPane scrollPane = new JScrollPane();        panel_1.add(scrollPane);        txtArea = new JTextArea();        scrollPane.setViewportView(txtArea);        JPanel panel_2 = new JPanel();        panel_2.setBounds(0, 288, 514, 44);        panel.add(panel_2);        panel_2.setLayout(null);        JLabel lblNewLabel = new JLabel("\u8BF7\u8F93\u5165\uFF1A");        lblNewLabel.setBounds(10, 18, 54, 15);        panel_2.add(lblNewLabel);        txtStr = new JTextField();        txtStr.setBounds(74, 15, 329, 21);        panel_2.add(txtStr);        txtStr.setColumns(10);        btnSend = new JButton("\u53D1\u9001");        btnSend.setBounds(413, 12, 93, 27);        btnSend.addActionListener(this);        panel_2.add(btnSend);        JPanel panel_3 = new JPanel();        panel_3.setBounds(0, 0, 514, 31);        panel.add(panel_3);        panel_3.setLayout(null);        JLabel lblNewLabel_1 = new JLabel("\u5BF9\u65B9IP\uFF1A");        lblNewLabel_1.setBounds(10, 10, 54, 15);        panel_3.add(lblNewLabel_1);        txtYouAddress = new JTextField();        txtYouAddress.setText("127.0.0.1");        txtYouAddress.setBounds(65, 7, 152, 21);        panel_3.add(txtYouAddress);        txtYouAddress.setColumns(10);        JLabel lblNewLabel_2 = new JLabel("\u60A8\u7684IP\uFF1A");        lblNewLabel_2.setBounds(234, 10, 54, 15);        panel_3.add(lblNewLabel_2);        txtMyAddress = new JTextField();        txtMyAddress.setText("127.0.0.1");        txtMyAddress.setBounds(282, 7, 152, 21);        panel_3.add(txtMyAddress);        txtMyAddress.setColumns(10);        btnEnter = new JButton("\u786E\u5B9A");        btnEnter.setBounds(440, 6, 64, 23);        btnEnter.addActionListener(this);        panel_3.add(btnEnter);    }    @Override    public void actionPerformed(ActionEvent e) {        // 按钮处理        if (e.getSource() == btnSend) { //发送            String string = txtStr.getText().trim();            send(string);        }        if(e.getSource() == btnEnter){ //连接            if(!"".equals(txtMyAddress.getText().trim()) && !"".equals(txtYouAddress.getText().trim())){                myAddress = txtMyAddress.getText().trim();                youAddress = txtYouAddress.getText().trim();                connect(myAddress); //封装的连接方法            }else{                txtArea.setText(txtArea.getText() + "发送目的地址或自己地址不能为空!");            }        }    }    /**     * 封装的连接方法     * @param address 己方地址,用于接收数据包     */    public void connect(String address) {        SocketAddress mAddress = new InetSocketAddress(address, 8888);        try {            receiverSocket = new DatagramSocket(mAddress);//实例化接收套接字            sendSocket = new DatagramSocket();//实例化发送套接字        } catch (SocketException e) {            e.printStackTrace();        }        new ReceiverThread(receiverSocket).start();//将接收放入线程中,不断接收数据    }    /**     * 用于接收消息的线程     * @author qinlang     *     */    class ReceiverThread extends Thread {        DatagramSocket socket;        public ReceiverThread(DatagramSocket socket){            this.socket = socket;        }        @Override        public void run() {            while(true){                DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);//定义并实例化接收对象                try {                    socket.receive(packet);//接收数据包                } catch (IOException e) {                    e.printStackTrace();                }                txtArea.setText(txtArea.getText() + "\n" + new String(packet.getData(), 0, packet.getLength()));//显示数据包内容            }        }    }    /**     * 用于发送数据包的方法     * @param str     */    public void send(String str){        InetAddress address = null;//定义发送至的地址        try {            address = InetAddress.getByName(youAddress);//实例化地址        } catch (UnknownHostException e1) {            e1.printStackTrace();        }        DatagramPacket packet = new DatagramPacket(str.getBytes(), str.getBytes().length, address, 8888);//定义并实例化数据包        try {            sendSocket.send(packet);//发送数据包            txtArea.setText(txtArea.getText() + "\n已发送...");        } catch (IOException e) {            e.printStackTrace();        }    }}

示例2:
本例子使用WindowBuilder图形界面进行编写。实现简单的多对多聊天

界面效果:
这里写图片描述

java代码:
客户端

package com.ql.talkM2M;import java.awt.EventQueue;import javax.swing.JFrame;import javax.swing.JPanel;import java.awt.BorderLayout;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.InetSocketAddress;import java.net.SocketException;import javax.swing.JLabel;import javax.swing.JTextField;import javax.swing.JButton;import javax.swing.JScrollPane;import javax.swing.JTextArea;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;public class Sender implements ActionListener{    private JFrame frame;    private JTextField txtName;    private JTextField txtServerAddress;    private JTextField txtStr;    private JButton btnConnect, btnSend;    private JTextArea textArea;    private DatagramSocket socket;//套接字    private DatagramPacket packet;//数据包    private String name; //昵称    private Thread thread;//用于接收的线程    /**     * Launch the application.     */    public static void main(String[] args) {        EventQueue.invokeLater(new Runnable() {            public void run() {                try {                    Sender window = new Sender();                    window.frame.setVisible(true);                } catch (Exception e) {                    e.printStackTrace();                }            }        });    }    /**     * Create the application.     */    public Sender() {        initialize();    }    /**     * Initialize the contents of the frame.     */    private void initialize() {        frame = new JFrame();        frame.addWindowListener(new WindowAdapter() {//程序关闭通知所有在线的用户            @Override            public void windowClosing(WindowEvent e) {                if(thread != null){                    send("退出了...");                }            }        });        frame.setBounds(100, 100, 536, 354);        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        JPanel panel = new JPanel();        frame.getContentPane().add(panel, BorderLayout.CENTER);        panel.setLayout(null);        JPanel panel_1 = new JPanel();        panel_1.setBounds(0, 0, 520, 37);        panel.add(panel_1);        panel_1.setLayout(null);        JLabel lblNewLabel = new JLabel("\u6635\u79F0\uFF1A");        lblNewLabel.setBounds(263, 10, 44, 15);        panel_1.add(lblNewLabel);        txtName = new JTextField();        txtName.setBounds(305, 7, 131, 21);        panel_1.add(txtName);        txtName.setColumns(10);        btnConnect = new JButton("连接");        btnConnect.setBounds(446, 6, 64, 23);        btnConnect.addActionListener(this);        panel_1.add(btnConnect);        JLabel lblNewLabel_1 = new JLabel("\u670D\u52A1\u5668IP\uFF1A");        lblNewLabel_1.setBounds(10, 10, 64, 15);        panel_1.add(lblNewLabel_1);        txtServerAddress = new JTextField();        txtServerAddress.setText("127.0.0.1");        txtServerAddress.setBounds(74, 7, 179, 21);        panel_1.add(txtServerAddress);        txtServerAddress.setColumns(10);        JPanel panel_2 = new JPanel();        panel_2.setBounds(0, 279, 520, 37);        panel.add(panel_2);        panel_2.setLayout(null);        JLabel lblNewLabel_2 = new JLabel("\u8BF7\u8F93\u5165\uFF1A");        lblNewLabel_2.setBounds(10, 12, 54, 15);        panel_2.add(lblNewLabel_2);        txtStr = new JTextField();        txtStr.setBounds(57, 9, 362, 21);        panel_2.add(txtStr);        txtStr.setColumns(10);        btnSend = new JButton("\u53D1\u9001");        btnSend.setEnabled(false);        btnSend.setBounds(427, 8, 83, 23);        btnSend.addActionListener(this);        panel_2.add(btnSend);        JPanel panel_3 = new JPanel();        panel_3.setBounds(0, 37, 520, 242);        panel.add(panel_3);        panel_3.setLayout(new BorderLayout(0, 0));        JScrollPane scrollPane = new JScrollPane();        panel_3.add(scrollPane);        textArea = new JTextArea();        scrollPane.setViewportView(textArea);    }    @Override    public void actionPerformed(ActionEvent e) {        //按钮按键处理        if(e.getSource() == btnConnect){ //连接            if("连接".equals(btnConnect.getText())){//按钮上的文字显示“连接”,表示没有连接,点击连接                if("".equals(txtServerAddress.getText()) || "".equals(txtName.getText().trim())){                    textArea.setText(textArea.getText() + "请输入服务器IP或昵称!\n");                }else{                    connect();//封装的连接方法                    btnConnect.setText("断开");//连接按钮改变文字                    thread = new Thread(new IN(socket));//将接收放入线程中,不断接收数据包                    thread.start();                    btnSend.setEnabled(true);//发送按钮使能                }            }else if("断开".equals(btnConnect.getText())){//按钮上的文字显示“断开”,表示正在连接,点击断开                if(e.getSource() == btnConnect){                    send("退出了...");//断开了连接,表明已下线                    if(thread != null){                        thread.stop();//销毁线程                        btnSend.setEnabled(false);//发送使否                    }                    btnConnect.setText("连接");//连接按钮改变文字                }            }        }        if(e.getSource() == btnSend){ //发送            String string = txtStr.getText().trim();            send(string);//封装的发送方法        }    }    /**     * 连接方法     */    public void connect(){        try {            socket = new DatagramSocket();//实例化套接字            name = txtName.getText().trim();            String str = name + ":上线了...";            String address = "";            address = txtServerAddress.getText().trim();//得到服务器ip            //连接上服务器,发送一次数据,通知服务器上的所有用户            packet = new DatagramPacket(str.getBytes(),str.getBytes().length, new InetSocketAddress(address, 8888));            socket.send(packet);            textArea.setText(textArea.getText() + "\n已连接...");        } catch (SocketException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }    }    /**     * 为接收开启单独的线程,不断接收数据     * @author qinlang     *     */    class IN implements Runnable {        private DatagramSocket socket;        public IN(DatagramSocket socket) {            this.socket = socket;        }        @Override        public void run() {            byte[] b = new byte[1024];//一次读取数据大小            DatagramPacket packet2 = new DatagramPacket(b, b.length);// 用来接收的包裹            String value = "";            while (true) {                try {                    socket.receive(packet2);//接收数据                    value = new String(b, 0, packet2.getLength());//将数据分割读取                    textArea.setText(textArea.getText() + "\n" + value);                } catch (IOException e) {                    break;                }            }        }    }    /**     * 发送数据的方法     * @param data     */    public void send(String data){        try {            packet.setData((name + ":" + data).getBytes());//设置数据(发送的数据和昵称),转换成字节流            socket.send(packet);//发送数据            textArea.setText(textArea.getText() + "\n您:" + data);        } catch (IOException e) {            e.printStackTrace();        }    }}

服务器

package com.ql.talkM2M;import java.io.BufferedReader;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.SocketAddress;import java.net.SocketException;import java.util.ArrayList;import java.util.List;/** * 服务器端,可看做只转发数据的服务器,也可以当客户端发送数据 * @author qinlang * */public class Server {    /**     * @param args     */    public static void main(String[] args) {        DatagramSocket socket = null;        DatagramPacket packet = null;        List<SocketAddress> addresses;//存放所有用户地址的集合        try {            socket = new DatagramSocket(8888);            packet = new DatagramPacket(new byte[1024], 1024);            socket.receive(packet);//接收一次自己的数据        } catch (SocketException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        addresses = new ArrayList<SocketAddress>();        addresses.add(packet.getSocketAddress()); //将自己的地址加入集合        new Thread(new INReceiver(socket, addresses)).start();//启动接收线程        //发送线程没有启动,因为只做转发功能,服务器不需要发送自己的数据    }}/** * 发送数据线程,此方法用于服务器对连接在服务器上的用户发数据 * @author qinlang * */class OUTReceiver implements Runnable {    BufferedReader br;    DatagramPacket packet;// 数据 地址    DatagramSocket socket;    List<SocketAddress> addresses;    public OUTReceiver(BufferedReader br, DatagramPacket packet,            DatagramSocket socket, List<SocketAddress> addresses) {        this.br = br;        this.packet = packet;        this.socket = socket;        this.addresses = addresses;    }    @Override    public void run() {        while (true) {//死循环,不断发送数据            try {                String data = br.readLine();//按行读取数据                packet.setData((Thread.currentThread().getName() + data).getBytes());                // 同一个数据,把所有在线的人都发一遍                for (SocketAddress socketAddress : addresses) {                    packet.setSocketAddress(socketAddress);                    socket.send(packet);                }                // 建议你不要把服务器关掉            } catch (IOException e) {                e.printStackTrace();            }        }    }}/** * 接收数据线程 * @author qinlang * */class INReceiver implements Runnable {    private DatagramSocket socket;    private List<SocketAddress> addresses;    public INReceiver(DatagramSocket socket, List<SocketAddress> addresses) {        this.socket = socket;        this.addresses = addresses;    }    @Override    public void run() {        byte[] b = new byte[1024];        DatagramPacket packet = new DatagramPacket(b, b.length);// 用来接收的包裹        String value = "";        while (true) {            try {                socket.receive(packet);//接收数据                SocketAddress myadd = packet.getSocketAddress();// 得到发数据过来的地址                // 同一个数据,把所有在线的人都发一遍                for (SocketAddress socketAddress : addresses) {                    if (socketAddress.equals(myadd))//如果是自身,则跳过                        continue;                    packet.setSocketAddress(socketAddress);                    socket.send(packet);                }                // 第一次来的时候,把这个地址添加到地址集合里面去                if (!addresses.contains(myadd))                    addresses.add(myadd);                if (value.equalsIgnoreCase("esc"))//如果退出了,把地址从集合中移除                    addresses.remove(myadd);            } catch (IOException e) {                e.printStackTrace();            }        }    }}
0 0
原创粉丝点击