网络编程03:基于UDP的Socket通信
标签: 网络编程
UDP编程
UDP协议(用户数据报协议)是无连接、不可靠的、无序的。UDP协议以数据报作为数据传输的载体。
进行数据传输时,首先需要将要传输的数据定义成数据报(Datagram),在数据报中指明数据所要达到的Socket(主机地址和端口号),然后再将数据报发送出去。涉及到以下两个操作类:
- DatagramPacket:表示数据报包
- DatagramSocket:进行端到端通信的类
DatagramSocket类
和DatagramPacket类
实现了基于 UDP 协议网络程序。
UDP数据报通过数据报套接字DatagramSocket
发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。
DatagramPacket对
象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号。
UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接。
流程如下:
- DatagramSocket与DatagramPacket
- 建立发送端,接收端
- 建立数据包
- 调用Socket的发送、接收方法
- 关闭Socket
- 发送端与接收端是两个独立的运行程序
DatagramPacket
public final class DatagramPacket extends Object
该类表示数据报包。
数据报包用于实现无连接分组传送服务。 仅基于该数据包中包含的信息,每个消息从一台机器路由到另一台机器。 从一台机器发送到另一台机器的多个分组可能会有不同的路由,并且可能以任何顺序到达。 包传送不能保证。
构造方法
其构造方法接受一个缓冲字节数组byte[] buf
, 可以指定主机地址(InetAddress)和端口号,也可以指定字节数组的长度和偏移量
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具有偏移ioffset指定主机上到指定的端口号。DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)
构造用于发送长度的分组数据报包 length具有偏移 ioffset指定主机上到指定的端口号。
常用方法
InetAddress getAddress()
返回该数据报发送或接收数据报的计算机的IP地址。byte[] getData()
返回数据缓冲区。int getPort()
返回发送数据报的远程主机上的端口号,或从中接收数据报的端口号。SocketAddress getSocketAddress()
获取该数据包发送到或正在从其发送的远程主机的SocketAddress(通常为IP地址+端口号)。void setAddress(InetAddress iaddr)
设置该数据报发送到的机器的IP地址。void setData(byte[] buf, int offset, int length)
设置此数据包的数据缓冲区。void setPort(int iport)
设置发送此数据报的远程主机上的端口号。void setSocketAddress(SocketAddress address)
设置该数据报发送到的远程主机的SocketAddress(通常是IP地址+端口号)。
DatagramSocket
public class DatagramSocket extends Object implements Closeable
此类表示用于发送和接收数据报数据包的套接字。数据报套接字是分组传送服务的发送或接收点。 在数据报套接字上发送或接收的每个数据包都被单独寻址和路由。 从一个机器发送到另一个机器的多个分组可以不同地路由,并且可以以任何顺序到达。
构造方法
protected DatagramSocket()
构造数据报套接字并将其绑定到本地主机上的任何可用端口。protected DatagramSocket(DatagramSocketImpl impl)
使用指定的DatagramSocketImpl创建一个未绑定的数据报套接字。protected DatagramSocket(int port)
构造数据报套接字并将其绑定到本地主机上的指定端口。protected DatagramSocket(int port, InetAddress laddr)
创建一个数据报套接字,绑定到指定的本地地址。protected DatagramSocket(SocketAddress bindaddr)
创建一个数据报套接字,绑定到指定的本地套接字地址。
常用方法
void receive(DatagramPacket p)
从此套接字接收数据报包。void send(DatagramPacket p)
从此套接字发送数据报包。void close()
关闭此数据报套接字。InetAddress getInetAddress()
返回此套接字连接到的地址。int getPort()
返回此套接字连接到的端口号。
基于UDP的socket通信模型
服务器端实现步骤:
- 创建DatagramSocket,指定端口号
- 创建DatagramPacket数据报包,用于接收数据
- 接收客户端发送的数据信息
- 读取数据
客户端实现步骤
- 定义发送信息
- 创建DatagramPacket,包含将要发送的信息
- 创建DatagramSocket
- 发送数据
例子
发送和接收
发送端:
package charNet;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class TestUDPSend {
public static void send() {
DatagramSocket datagramSocket = null;
try {
datagramSocket = new DatagramSocket();
byte[] b = "我是被发送的数据".getBytes();
//创建一个数据报:每一个数据报不能大于64k,都记录着数据信息,发送端的IP、端口号,
// 以及要发送到的接收端的IP以及端口号
DatagramPacket datagramPacket = new DatagramPacket(b,0,b.length,
InetAddress.getByName("localhost"),9999);
datagramSocket.send(datagramPacket);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (datagramSocket != null) {
datagramSocket.close();
}
}
}
public static void main(String[] args) {
send();
}
}
接收端:
package charNet;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class TestUDPReceive {
public static void receive() {
DatagramSocket datagramSocket = null;
try {
datagramSocket = new DatagramSocket(9999);
byte[] b = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(b,0,b.length);
datagramSocket.receive(datagramPacket);
String str = new String(datagramPacket.getData(),0,datagramPacket.getLength());
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
} finally {
datagramSocket.close();
}
}
public static void main(String[] args) {
receive();
}
}
UDP登录和相应
服务器:
package charNet;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//服务器端,实现基于UDP的用户登录
public class UDPServer {
public static void server() {
DatagramSocket datagramSocket = null;
try {
//创建服务器DatagramSocket,指定端口
datagramSocket = new DatagramSocket(9999);
//创建数据报,用于接收客户端发送的数据,将数据保存在字节数组中
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data,data.length);
System.out.println("服务器端已经启动,等待中...");
//接收客户端发送的数据
datagramSocket.receive(packet); //此方法在接收到数据报之前会一直阻塞
//读取数据
String info = new String(data,0,packet.getLength());
System.out.println("我是服务器,客户端说" + info);
//给客户端相应
//定义客户端的地址,端口号,数据
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] recdata = "欢迎".getBytes();
//创建数据报,包含响应信息
DatagramPacket packet2 = new DatagramPacket(recdata,recdata.length,address,port);
datagramSocket.send(packet2);
} catch (IOException e) {
e.printStackTrace();
} finally {
datagramSocket.close();
}
}
public static void main(String[] args) {
server();
}
}
客户机:
package charNet;
import java.io.IOException;
import java.io.InputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* Created by japson on 9/17/2017.
*/
public class UDPClient {
public static void main(String[] args) {
DatagramSocket datagramSocket = null;
try {
//创建DatagramSocket对象
datagramSocket = new DatagramSocket();
//定义服务器的地址,端口号,数据
InetAddress address = InetAddress.getByName("localhost");
int port = 9999;
byte[] data = "用户名:USER1;密码:123".getBytes();
//创建数据报,包含发送的数据信息
DatagramPacket packet = new DatagramPacket(data,0,data.length,address,port);
//向服务器端发送数据
datagramSocket.send(packet);
//接收服务器端发送数据报
byte[] data1 = new byte[1024];
DatagramPacket packet1 = new DatagramPacket(data1,data1.length,address,port);
datagramSocket.receive(packet1);
String reply = new String(packet1.getData(),0,packet.getLength());
System.out.println("我是客户端,服务器说:" + reply);
} catch (IOException e) {
e.printStackTrace();
} finally {
datagramSocket.close();
}
}
}