您的位置:首页 > 理论基础 > 计算机网络

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(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 网络编程 udp