Java UDP通信

一、DatagramPacket类:

如果把DatagramSocket比作创建的港口码头,那么DatagramPacket就是发送和接收数据的集装箱。
构造函数:一个用来接收数据,一个用来发送数据
public DatagramPacket(byte[] buf,int length) //接收数据
构造 DatagramPacket 用来接收长度为 ilength 的包。
public DatagramPacket(byte[] buf,int length,InetAddress address,int port)
构造数据报文包用来把长度为 ilength 的包传送到指定宿主的指定的端口号。
getAddress()
返回接收或发送此数据报文的机器的 IP 地址。
getData()
返回接收的数据或发送出的数据。
getLength()
返回发送出的或接收到的数据的长度。
getPort()
返回接收或发送该数据报文的远程主机端口号。

二、DatagramSocket类

此类表示用来发送和接收数据报包的套接字。 数据报套接字是包投递服务的发送或接收点。
DatagramSocket(int port)     创建数据报套接字并将其绑定到本地主机上的指定端口。
DatagramSocket(int port, InetAddress laddr)     创建数据报套接字,将其绑定到指定的本地地址。

receive(DatagramPacket p)
从此套接字接收数据报包。
void send(DatagramPacket p)
从此套接字发送数据报包。
bind(SocketAddress addr)
将此 DatagramSocket 绑定到特定的地址和端口。
void close()
关闭此数据报套接字。
void connect(InetAddress address, int port)
将套接字连接到此套接字的远程地址。
void connect(SocketAddress addr)
将此套接字连接到远程套接字地址(IP 地址 + 端口号)。
void disconnect()
断开套接字的连接。
getInetAddress()
返回此套接字连接的地址。
InetAddress getLocalAddress()
获取套接字绑定的本地地址。

三、InetAddress类
InetAddress用于表示计算机IP地址的一个类,而在日常应用中的IP地址用"192.168.0.1",
"WWW.it315.org"等字符串格式表示的。
getByName方法
getHostAddress方法

四、UDP通信示例代码:

发送端代码UdpSend.java:

import java.net.*;

public class UdpSend {
public static void main(String[] args) throws Exception{
DatagramSocket ds=new DatagramSocket();
String strInfo="Hello Taohx";
ds.send(new DatagramPacket(strInfo.getBytes(),strInfo.length(),
InetAddress.getByName("59.64.157.93"),3000));
ds.close();
}
}

接收代码UdpRecv.java:

import java.net.*;
public class UdpRecv {
public static void main(String[] args) throws Exception{
DatagramSocket ds=new DatagramSocket(3000);
byte [] buf=new byte[1024];
DatagramPacket dp=new DatagramPacket(buf,1024);
ds.receive(dp);
String strInfo= new String(dp.getData(),0,dp.getLength())+
" from "+dp.getAddress().getHostAddress()+":"+dp.getPort();
System.out.println(strInfo);
ds.close();

}
}

注意:若发送中文信息时,因为中文字符占两个字节,故发送代码 ds.send要修改如下才可正确地发送中文信息,

ds.send(new DatagramPacket(strInfo.getBytes(),strInfo.getBytes().length,
InetAddress.getByName("59.64.157.93"),3000));

***********************************************************************************************************

在Java中操纵UDP

使用位于JDK中Java.net包下的DatagramSocket和DatagramPacket类,可以非常方便地控制用户数据报文。

在描述它们之前,必须了解位于同一个位置的InetAddress类。InetAddress实现了Java.io. Serializable接口,不允许继承。它用于描述和包装一个Internet IP地址,通过三个方法返回InetAddress实例:

getLocalhost():返回封装本地地址的实例。

getAllByName(String host):返回封装Host地址的InetAddress实例数组。

getByName(String host):返回一个封装Host地址的实例。其中,Host可以是域名或者是一个合法的IP地址。

DatagramSocket类用于创建接收和发送UDP的Socket实例。和Socket类依赖SocketImpl类一样,DatagramSocket类的实现也依靠专门为它设计的DatagramScoketImplFactory类。DatagramSocket类有3个构建器:

DatagramSocket():创建实例。这是个比较特殊的用法,通常用于客户端编程,它并没有特定监听的端口,仅仅使用一个临时的。

DatagramSocket(int port):创建实例,并固定监听Port端口的报文。

DatagramSocket(int port, InetAddress localAddr):这是个非常有用的构建器,当一台机器拥有多于一个IP地址的时候,由它创建的实例仅仅接收来自LocalAddr的报文。

值得注意的是,在创建DatagramSocket类实例时,如果端口已经被使用,会产生一个SocketException的异常抛出,并导致程序非法终止,这个异常应该注意捕获。DatagramSocket类最主要的方法有4个:

Receive(DatagramPacket d):接收数据报文到d中。receive方法产生一个“阻塞”。

Send(DatagramPacket d):发送报文d到目的地。

SetSoTimeout(int timeout):设置超时时间,单位为毫秒。

Close():关闭DatagramSocket。在应用程序退出的时候,通常会主动释放资源,关闭Socket,但是由于异常地退出可能造成资源无法回收。所以,应该在程序完成时,主动使用此方法关闭Socket,或在捕获到异常抛出后关闭Socket。

“阻塞”是一个专业名词,它会产生一个内部循环,使程序暂停在这个地方,直到一个条件触发。

DatagramPacket类用于处理报文,它将Byte数组、目标地址、目标端口等数据包装成报文或者将报文拆卸成Byte数组。应用程序在产生数据包是应该注意,TCP/IP规定数据报文大小最多包含65507个,通常主机接收548个字节,但大多数平台能够支持8192字节大小的报文。DatagramPacket类的构建器共有4个:

DatagramPacket(byte[] buf, int length, InetAddress addr, int port):从Buf数组中,取出Length长的数据创建数据包对象,目标是Addr地址,Port端口。

DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):从Buf数组中,取出Offset开始的、Length长的数据创建数据包对象,目标是Addr地址,Port端口。

DatagramPacket(byte[] buf, int offset, int length):将数据包中从Offset开始、Length长的数据装进Buf数组。

DatagramPacket(byte[] buf, int length):将数据包中Length长的数据装进Buf数组。

DatagramPacket类最重要的方法就是getData()了,它从实例中取得报文的Byte数组编码。

简单的实例说明

{接收数据的服务器}

byte[] buf = new byte[1000];

DatagramSocket ds = new DatagramSocket(12345);

//开始监视12345端口

DatagramPacket ip = new DatagramPacket(buf, buf.length);

//创建接收数据报的实例

while (true)

{

ds.receive(ip);

//阻塞,直到收到数据报后将数据装入IP中

System.out.println(new String(buf));

}

{发送数据的客户端}

InetAddress target = InetAddress.getByName(“www.xxx.com“);

//得到目标机器的地址实例

DatagramSocket ds = new DatagramSocket(9999);

//从9999端口发送数据报

String hello = “Hello, I am come in!”;

//要发送的数据

byte[] buf = hello.getBytes();

//将数据转换成Byte类型

op = new DatagramPacket(buf, buf.length, target, 12345);

//将BUF缓冲区中的数据打包

ds.send(op);

//发送数据

ds.close();

//关闭连接

------

Java中的DatagramPacket与DatagramSocket的初步
1.基本概念:

a.DatagramPacket与DatagramSocket位于java.net包中

b.DatagramPacket表示存放数据的数据报,DatagramSocket表示接受或发送数据报的套接字

c.由这两个类所有构成的网络链接是基于UDP协议,是一种不可靠的协议。

之所以不可靠是因为发送方不负责数据是否发送成功,接收方收到数据时也不会   向发送方反馈成功消息,容易导致信息的丢失。

但是这种协议却是快捷的,比如CS(半条命)游戏就是基于UDP协议,否则我们估计   要玩一次游戏后就会把机子砸了,所以我们可以看到游戏有时会失帧。

2.使用方法:

要建立基于UDP协议的链接,我们应该先建立套接字<DatagramSocket>(即发送站或接收站),之后通过套接字发送或接受数据<DatagramPacket>。

我们先了解一下DatagramSocket和DatagramPacket类

DatagramSocket类:

构造方法:

DatagramSocket():

表示创建一个默认的套接字,并绑定到本地地址和一个随机的端口号

DatagramSocket(int port):

与上面不同的是,绑定到特定端口号,其它不变

DatagramSocket(int port, InetAddress iad):

表示创建一个套接字,绑定到特定的端口号及指定地址

DatagramSocket(SocketAddress sad);

表示创建一个套接字,绑定到特定的套接字地址

小提示:所谓的本地地址我们可以这样来理解,我们通过“控制面板-本地连接-支持”中可以看到一个IP地址,这就是本地的IP地址,也暂且理解为本地地址。

基本方法:

close():

关闭套接字

recevie(DatagramPacket dp):

接受数据报

send(DatagramPacket dp):

发送数据报

这里触及了数据报,下面介绍数据报:

DatagramPacket类:

构造方法:(只列出主要的)

接受类型:

DatagramPacket(byte[] buf, int length):

用来接受长度为length的buf数据(即数据存于字节数组buf中)

发送类型:

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

将length长的buf数据发送到指定的地址的端口号处

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

将length长的buf数据发送到指定的套接字地址处

有上面可知,我们发送数据时构造的数据报应该是发送类型的,而接受数据报时应该是接受类型的

开始书写代码

代码意图:

1.一个接收方,一个发送方

2.当接收方收到发送方发送的消息后,打印发送的消息及发送方的地址和端口号,之后向发送反馈一条信息“接受方:我收到了!”

3.发送方打印出接收方反馈的消息

详细思路见代码:

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class Sender {
public static void main(String[] args) {
try {
// 创建发送方的套接字,IP默认为本地,端口号随机
DatagramSocket sendSocket = new DatagramSocket();

// 确定要发送的消息:
String mes = "你好!接收方!";

// 由于数据报的数据是以字符数组传的形式存储的,所以传转数据
byte[] buf = mes.getBytes();

// 确定发送方的IP地址及端口号,地址为本地机器地址
int port = 8888;
InetAddress ip = InetAddress.getLocalHost();

// 创建发送类型的数据报:
DatagramPacket sendPacket = new DatagramPacket(buf, buf.length, ip,
port);

// 通过套接字发送数据:
sendSocket.send(sendPacket);

// 确定接受反馈数据的缓冲存储器,即存储数据的字节数组
byte[] getBuf = new byte[1024];

// 创建接受类型的数据报
DatagramPacket getPacket = new DatagramPacket(getBuf, getBuf.length);

// 通过套接字接受数据
sendSocket.receive(getPacket);

// 解析反馈的消息,并打印
String backMes = new String(getBuf, 0, getPacket.getLength());
System.out.println("接受方返回的消息:" + backMes);

// 关闭套接字
sendSocket.close();
} catch (Exception e) {
e.printStackTrace();
}

}
}

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketAddress;

public class Receive {
public static void main(String[] args) {
try {
// 确定接受方的IP和端口号,IP地址为本地机器地址
InetAddress ip = InetAddress.getLocalHost();
int port = 8888;

// 创建接收方的套接字,并制定端口号和IP地址
DatagramSocket getSocket = new DatagramSocket(port, ip);

// 确定数据报接受的数据的数组大小
byte[] buf = new byte[1024];

// 创建接受类型的数据报,数据将存储在buf中
DatagramPacket getPacket = new DatagramPacket(buf, buf.length);

// 通过套接字接收数据
getSocket.receive(getPacket);

// 解析发送方传递的消息,并打印
String getMes = new String(buf, 0, getPacket.getLength());
System.out.println("对方发送的消息:" + getMes);

// 通过数据报得到发送方的IP和端口号,并打印
InetAddress sendIP = getPacket.getAddress();
int sendPort = getPacket.getPort();
System.out.println("对方的IP地址是:" + sendIP.getHostAddress());
System.out.println("对方的端口号是:" + sendPort);

// 通过数据报得到发送方的套接字地址
SocketAddress sendAddress = getPacket.getSocketAddress();

// 确定要反馈发送方的消息内容,并转换为字节数组
String feedback = "接收方说:我收到了!";
byte[] backBuf = feedback.getBytes();

// 创建发送类型的数据报
DatagramPacket sendPacket = new DatagramPacket(backBuf,
backBuf.length, sendAddress);

// 通过套接字发送数据
getSocket.send(sendPacket);

// 关闭套接字
getSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

测试已通过

先启动接收方,然后启动发送方即可