udp学习笔记

java.util.DatagramSocket接收和发送udp数据报
java.util.DatagramPacket表示UDP数据报


//发送数据
public void send(DatagramPacket p) throws IOException
//接收数据
public synchronized void receive(DatagramPacket p) throws IOException


---------------------------客户端----------------------------------
/**
* udp客户端
*
* @version
*
* @Description:
*
* @author <a href="mailto:zhenhuayue@sina.com">Retacn</a>
*
* @since 2014-6-24
*
*/
public class EchoClient {
// udp服务器地址
private String remoteHost = "localhost";
private int remotePort = 8000;
private DatagramSocket socket;


/**
* 构造器
*
* @throws SocketException
*/
public EchoClient() throws SocketException {
socket = new DatagramSocket();
}


public void talk() {
try {
InetAddress remoteIP = InetAddress.getByName


(remoteHost);


BufferedReader localReader = new


BufferedReader(new InputStreamReader(System.in));
String msg = null;
while ((msg = localReader.readLine()) !=


null) {
byte[] outputData = msg.getBytes();


DatagramPacket outPacket = new


DatagramPacket(outputData, outputData.length, remoteIP, remotePort);
socket.send(outPacket);// 向服端发送


数据


DatagramPacket inputPacket = new


DatagramPacket(new byte[512], 512);
socket.receive(inputPacket);
System.out.println(new String


(inputPacket.getData(), 0, inputPacket.getLength()));
if (msg.equals("bye")) {
break;
}
}


} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
socket.close();
}


}


/**
* @param args
* @throws SocketException
*/
public static void main(String[] args) throws SocketException


{
new EchoClient().talk();
}
}








------------------------------服务端-----------------------------
/**
* udp服务端
*
* @version
*
* @Description:
*
* @author <a href="mailto:zhenhuayue@sina.com">Retacn</a>
*
* @since 2014-6-24
*
*/
public class EchoServer {
private int port = 8000;
private DatagramSocket socket;


/**
* 构造器
*
* @throws SocketException
*/
public EchoServer() throws SocketException {
socket = new DatagramSocket();
System.out.println("server is started!");
}


public String echo(String msg) {
return "echo" + msg;
}


public void serviced() {
while (true) {
try {
// 接收来自任意一个客户端的数据报
DatagramPacket packet = new


DatagramPacket(new byte[512], 512);
socket.receive(packet);


String msg = new String


(packet.getData(), packet.getLength());
System.out.println(packet.getAddress


() + ":" + packet.getPort() + ">" + msg);
// 给客户端一个回复
packet.setData(echo(msg).getBytes());
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}


}


}


/**
* @param args
* @throws SocketException
*/
public static void main(String[] args) throws SocketException


{
new EchoServer().serviced();
}
}






datagramPacket类
构造方法分为:
接收数据的:
public DatagramPacket(byte buf[],//要发送的数据
int length)//要发送的字节数
public DatagramPacket(byte buf[],//要发送的数据
int offset,//要发送的数据在data中的位置
int length)//要发送的字节数
发送数据的:
public DatagramPacket(byte buf[], //要发送的数据
int offset,//要发送的数据在data中的位置
int length,//要发送的字节数
InetAddress address,//目的地址
int port)//目的端口号


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


SocketException


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


SocketException




------------------数据报的大小(指的是数据部分)----------------
ipv4数据报的最大长度为65507字节
ipv6数据的最大长度为65536字节
原则上不大于8k


udp头包括: ip头 udp头和数据






----------------------读取和设置DatagramentPacket------------------
属性:
byte[] buf; //数据缓冲区
int offset; //数据报在缓冲区的位置
int length; //数据报长度
int bufLength;
InetAddress address; //数据报的目标地址
int port; //UDP端口
可以通过get/set方法获取和设置属性






-----------------------数据格式的转换-------------------
/**
* long型数组转换为字节数组
*
* @param data
* @return
* @throws IOException
*/
public static byte[] longToByte(long[] data) throws


IOException {
ByteArrayOutputStream bos = new


ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
for (int i = 0; i < data.length; i++) {
dos.writeLong(data[i]);
}
dos.close();
return bos.toByteArray();
}


/**
* 字节数组转换为long型数组
*
* @param data
* @return
* @throws IOException
*/
public long[] byteToLong(byte[] data) throws IOException {
long[] result = new long[data.length / 8];
ByteArrayInputStream bis = new ByteArrayInputStream


(data);
DataInputStream dis = new DataInputStream(bis);
for (int i = 0; i < data.length; i++) {
result[i] = dis.readLong();
}
return result;
}


---------------------重用DatagramPacket------------------------
/**
* datagramPacket重用
*
* 用来多次发送或接收数据
*
* @version
*
* @Description:
*
* @author <a href="mailto:zhenhuayue@sina.com">Retacn</a>
*
* @since 2014-6-24
*
*/
public class DatagramTester {
private int port = 8000;
private DatagramSocket sendSocket;
private DatagramSocket receiveSocket;
private static final int MAX_LENGTH = 3584;


/**
* 构造器
*
* @throws SocketException
*/
public DatagramTester() throws SocketException {
sendSocket = new DatagramSocket();
receiveSocket = new DatagramSocket(port);
// 开启收发线程
receiver.start();
sender.start();


}


/**
* long型数组转换为字节数组
*
* @param data
* @return
* @throws IOException
*/
public static byte[] longToByte(long[] data) throws


IOException {
ByteArrayOutputStream bos = new


ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
for (int i = 0; i < data.length; i++) {
dos.writeLong(data[i]);
}
dos.close();
return bos.toByteArray();
}


/**
* 字节数组转换为long型数组
*
* @param data
* @return
* @throws IOException
*/
public long[] byteToLong(byte[] data) throws IOException {
long[] result = new long[data.length / 8];
ByteArrayInputStream bis = new ByteArrayInputStream


(data);
DataInputStream dis = new DataInputStream(bis);
for (int i = 0; i < data.length; i++) {
result[i] = dis.readLong();
}
return result;
}


/**
* 发送数据
*
* @param data
* @throws IOException
*/
public void send(byte[] data) throws IOException {
DatagramPacket packet = new DatagramPacket(data, 0,


512, InetAddress.getByName("localhost"), port);


// 已发送的字节数
int bytesSent = 0;
// 发送次数
int count = 0;
while (bytesSent < data.length) {
sendSocket.send(packet);
System.out.println("<sendSocket>第" + (+


+count) + "次,发送了" + packet.getLength() + "个字节");
// 记当发送字节数
bytesSent += packet.getLength();
// 未发送字节
int remain = data.length - bytesSent;
// 记算下次发送数据的长度
int length = (remain > 512) ? 512 : remain;
// 改变socket属性
packet.setData(data, bytesSent, length);
}
}


/**
* 接收数据
*
* @return
* @throws IOException
*/
public byte[] receive() throws IOException {
byte[] data = new byte[MAX_LENGTH];
// 数据包
DatagramPacket packet = new DatagramPacket(data, 0,


MAX_LENGTH);


// 已接收的字节数
int bytesReceived = 0;
// 接收次数
int count = 0;


// 开始时间
long beginTime = System.currentTimeMillis();
// 如果接收完全部数据,或是超过了5分钟,就结束
while (bytesReceived < data.length &&


((System.currentTimeMillis() - beginTime) < 5000 * 5)) {
receiveSocket.receive(packet);
System.out.println("<ReceiveSoket>第" + (+


+count) + "次接收到" + packet.getLength() + "个字节");


// 记录已接收的字节数
bytesReceived += packet.getLength();
// 修改packet数据包格式
packet.setData(data, bytesReceived,


MAX_LENGTH - bytesReceived);
}


return data;
}


/* 发送线程 */
public Thread sender = new Thread() {


public void run() {
long[] longArray = new long[MAX_LENGTH];
for (int i = 0; i < longArray.length; i++) {
longArray[i] = i + 1;
}
try {
send(longToByte(longArray));
} catch (IOException e) {
e.printStackTrace();
}
};
};


/**
* 接收者线程
*/
public Thread receiver = new Thread() {


public void run() {
try {
long[] longArray = byteToLong


(receive());
// 打印接收到的数据
for (int i = 0; i < longArray.length;


i++) {
if (i % 100 == 0) {
System.out.println();
}
}
} catch (IOException e) {
e.printStackTrace();
}
};
};


@SuppressWarnings("unused")
public static void main(String[] args) throws SocketException


{
DatagramTester tester = new DatagramTester();
}
}






------------------------DatagramSocket-------------------
收发数据报


构造器有以下几种格式:
DatagramSocket() //与匿名端口绑定
DatagramSocket(int port)
DatagramSocket(DatagramSocketImpl impl)
DatagramSocket(SocketAddress bindaddr) //指定ip和端口号
DatagramSocket(int port, InetAddress laddr)//同上


常用方法


getLocalAddress()
getRemoteSocketAddress()
getLocalSocketAddress()
getLocalPort()


接收和发送数据报
//发送
public void send(DatagramPacket p) throws IOException
public synchronized void receive(DatagramPacket p) throws IOException


-----------------------管理连接------------------------------
//只与指定的远程主机和UDP端口号收发数据
public void connect(InetAddress address, int port)
//终止当前连接
public void disconnect()
//只有建立连接时才会返回,远程端口号
getPort()
//同上,返回远程ip
getInetAddress()
//同上
getRemoteSocketAddress()




------------------------关闭DatagramSocket---------------
close()


SocketOptions的几个选项


public final static int SO_TIMEOUT = 0x1006;//等待超时时间
public final static int SO_RCVBUF = 0x1002;//接收数据缓冲区的大小
public final static int SO_SNDBUF = 0x1001;//发送数据缓冲区的大小
是否允许重用datagramSocket所绑定的地址
public final static int SO_REUSEADDR = 0x04;
//是否允许对网络广播地址收发数据报
public final static int SO_BROADCAST = 0x0020;


//可以使用以下方法对选项进行设置
receiveSocket.setSoTimeout(timeout);
//源代码中是通过以下式实现
setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout))
其他同理




---------------------ip服务类型选项-------------------
//设置服务类型
public synchronized void setTrafficClass(int tc) throws


SocketException
//读取服务类型
public synchronized void setTrafficClass(int tc) throws


SocketException


四种服务类型
0x02(二进制倒数第二位为1) 成本低
0x04(倒数第三位为1) 高可靠性
0x08(倒数第四位为1) 高吞吐量
0x10(倒数第五位为1) 最小延迟


----------------------DatagramChannel类-----------------------
非阻塞方式发送和接收数据包,是selectableChannel的子类,服务器通过单个
线程和多个客户通信


//创建datagramChannel,示例代码如下:
DatagramChannel channel = DatagramChannel.open();
DatagramSocket socket = channel.socket();
SocketAddress address = new InetSocketAddress(8000);
// 绑定一个本地地址
socket.bind(address);


------------------------管理连接---------------------------------
一般 不对datagramChannel进行连接


-----------------------收发数据报---------------------------------
//发送数据报
channel.send(src, target)
//把缓冲区的位置重设为零
src.rewind();


注:send方法不会分为多个数据报发送,要么全发,要么一个不发




//接收数据报
public abstract SocketAddress receive(ByteBuffer dst) throws


IOException


示例代码如下:
/**
* 发送数据
*
* @version
*
* @Description:
*
* @author <a href="mailto:zhenhuayue@sina.com">Retacn</a>
*
* @since 2014-6-25
*
*/
public class SendChannel {


/**
* @param args
* @throws IOException
* @throws InterruptedException
*/
public static void main(String[] args) throws IOException,


InterruptedException {
DatagramChannel channel = DatagramChannel.open();


DatagramSocket socket = channel.socket();
SocketAddress localAddress = new InetSocketAddress


(7000);
SocketAddress remoteAddress = new InetSocketAddress


(InetAddress.getByName("localhost"), 8000);
// 绑定本地地址
socket.bind(localAddress);
while (true) {
ByteBuffer buffer = ByteBuffer.allocate


(1024);
buffer.clear();
System.out.println("缓冲区的剩余字节为:" +


buffer.remaining());
int n = channel.send(buffer, remoteAddress);


System.out.println("发送的字节数为:" + n);
TimeUnit.MICROSECONDS.sleep(500);
}


}
}






/**
*
* 接收数据报
*
* @version
*
* @Description:
*
* @author <a href="mailto:zhenhuayue@sina.com">Retacn</a>
*
* @since 2014-6-25
*
*/
public class ReceiveChannel {


/**
* @param args
* @throws IOException
* @throws InterruptedException
*/
public static void main(String[] args) throws IOException,


InterruptedException {
//缓冲字节大小
final int ENOUGH_SIZE = 1024;
final int SMALL_SIZE = 4;
// 是否是阻塞模式
boolean isBlocked = true;
int size = ENOUGH_SIZE;


if (args.length > 0) {
// 读取命令行参数
int opt = Integer.parseInt(args[0]);
switch (opt) {
case 1:
isBlocked = true;
size = ENOUGH_SIZE;
break;
case 2:
isBlocked = true;
size = SMALL_SIZE;
break;
case 3:
isBlocked = false;
size = ENOUGH_SIZE;
break;
case 4:
isBlocked = false;
size = SMALL_SIZE;
break;
}


}


DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(isBlocked);
// 用于接收数据的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(size);
DatagramSocket socket = channel.socket();
SocketAddress localAddress = new InetSocketAddress


(8000);
socket.bind(localAddress);


while (true) {
System.out.println("开始接收数据");
SocketAddress remoteAddress =


channel.receive(buffer);
if (null == remoteAddress) {
System.out.println("没有接收到数据


报");
} else {
buffer.flip();
System.out.println("接收到的数据大小


为:" + buffer.remaining());
}
,TimeUnit.MICROSECONDS.sleep(500);
}


}
}






--------------------------read/write方式收发数据包----------------
write发送数据报,有以下几种形式
//依次发送bytebuffer中每一个byteBuffer的数据
public abstract int write(ByteBuffer src) throws IOException
public final long write(ByteBuffer[] srcs) throws IOException
public abstract long write(ByteBuffer[] srcs,//
int offset,//
int length)throws IOException;//指定buffer的个数




write()与send()的区别:
write()需要先调用connect()与远程进立连接
非阻塞模式下,write()不能保证所剩作数据作为一个数据报发送
要想发送所有剩余数据,可以采用以下方法
while(buffer.hasRemaining()&&channel.write(buffer)!=1){
}






read()接收数据报
//返回实际接收数据的字节数,只接收一个数据报,保存在byteBufferkh
public abstract int read(ByteBuffer dst) throws IOException;
public final long read(ByteBuffer[] dsts) throws IOException
public abstract long read(ByteBuffer[] dsts, int offset, int


length)
throws IOException;


read()和receive()一样,如果缓冲区小接收数据,多余的数据将被丢弃


------------------------------组播--------------------------------
网络数据传播的三种方式:
单播:
广播:
组播:


java.net.MulticastSocket具有组播的功能,是datagramSocket的子类
如果要接收组播数据报,需要建立MulticastSocket,添加到组播组
发送组播数据报不用添加到组播组,需要先调用setTimeToLive()方法


//构造器
//绑定匿名接口,(只发送)
public MulticastSocket() throws IOException
//以下两种可用于接收
public MulticastSocket(int port) throws IOException
//如果参数设为空,后期需要使用bind()重新绑定
public MulticastSocket(SocketAddress bindaddr) throws IOException






与组播组通信有四种方法:
加入组播组
public void joinGroup(InetAddress mcastaddr) throws IOException
public void joinGroup(SocketAddress mcastaddr,//指定组播地址
NetworkInterface netIf)//指定网络接口
throws IOException
向组中成员发送数据报
public void send(DatagramPacket p, byte ttl)throws IOException
接收组播数据报
receive()//接收和发送数据报,象是调用的datagramSocket中的方法
离开组播组
public void leaveGroup(InetAddress mcastaddr) throws IOException
public void leaveGroup(SocketAddress mcastaddr, NetworkInterface


netIf)throws IOException


组播地址位于224.0.0.0-239.255.255.255




multicastSocket的属性
//读取和设置网络接口
public InetAddress getInterface() throws SocketException
public void setInterface(InetAddress inf) throws SocketException
//networkInterface有唯一的域名
public NetworkInterface getNetworkInterface() throws SocketException
public void setNetworkInterface(NetworkInterface netIf)throws


SocketException




//读取和设置ttl属性
public int getTimeToLive() throws IOException
//ttl属性决定了数据报允放通过的路由器的数目
public void setTimeToLive(int ttl) throws IOException




//读取和设置组播数据报回送模式
public boolean getLoopbackMode() throws SocketException
//是否接收自身发送的数据报,true为不接收
public void setLoopbackMode(boolean disable) throws SocketException




组播socket的范例


/**
* 组播发送
*
* @version
*
* @Description:
*
* @author <a href="mailto:zhenhuayue@sina.com">Retacn</a>
*
* @since 2014-6-26
*
*/
public class MutlcastSocketSender {


/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// 组播地址及端口
InetAddress group = InetAddress.getByName


("192.168.255");
int port = 4000;
MulticastSocket msSocket = null;


try {
msSocket = new MulticastSocket(port);
msSocket.joinGroup(group);
while (true) {
String message = "hello" + new Date


();
byte[] buffer = message.getBytes();
DatagramPacket packet = new


DatagramPacket(buffer, buffer.length, group, port);
// 发送组播数据报
msSocket.send(packet);
System.out.println("发送数据报给" +


group + ":" + port);


TimeUnit.MICROSECONDS.sleep(1000);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (null != msSocket) {
try {
msSocket.leaveGroup(group);
msSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}


}
}


-------------------------------------------------







































  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值