JAVA基础学习之UDP网络编程
2015-10-06 20:07
615 查看
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,用来接收长度为 length 的数据包。
构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
构造 DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量。
构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。
构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。
构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
一般方法
返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
返回数据缓冲区。
返回将要发送或接收到的数据的长度。
返回将要发送或接收到的数据的偏移量。
返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。
获取要将此包发送到的或发出此数据报的远程主机的 SocketAddress(通常为 IP 地址 + 端口号)。
设置要将此数据报发往的那台机器的 IP 地址。
为此包设置数据缓冲区。
为此包设置数据缓冲区。
为此包设置长度。
设置要将此数据报发往的远程主机上的端口号。
设置要将此数据报发往的远程主机的 SocketAddress(通常为 IP 地址 + 端口号)。
示例1:
本例子使用WindowBuilder图形界面进行编写。实现简单的一对一聊天
界面效果:
java代码
示例2:
本例子使用WindowBuilder图形界面进行编写。实现简单的多对多聊天
界面效果:
java代码:
客户端
服务器
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(); } } } }
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树
- [原创]java局域网聊天系统