第12章 UDP
来源:互联网 发布:可以在家干的工作 知乎 编辑:程序博客网 时间:2024/06/01 12:14
第12章 UDP
12.1 UDP协议
Java中的UDP的实现分为两个类:DatagramPacket和DatagramSocket。
DatagramPacket类将数据字节填充到UDP包中,这称为数据报(datagram),由你来解包接收的数据报。
DatagramSocket可以收发UDP数据报。
为发送数据,要将数据放到DatagramPacket中,使用DatagramSocket来发送这个包。
要接收数据,可以从DatagramSocket中接收一个DatagramPacket对象,然后检查该包的内容。
12.2 UDP客户端
示例12-1:一个daytime协议客户端
<span style="font-size:18px;">import java.io.*;import java.net.*;public class DaytimeUDPClient { private final static int PORT = 13; private static final String HOSTNAME = "time.nist.gov"; public static void main(String[] args) { try (DatagramSocket socket = new DatagramSocket(0)) { socket.setSoTimeout(10000); InetAddress host = InetAddress.getByName(HOSTNAME); DatagramPacket request = new DatagramPacket(new byte[1], 1, host , PORT); DatagramPacket response = new DatagramPacket(new byte[1024], 1024); socket.send(request); socket.receive(response); String result = new String(response.getData(), 0, response.getLength(), "US-ASCII"); System.out.println(result); } catch (IOException ex) { ex.printStackTrace(); } } }</span>
12.3 UDP服务器
<span style="font-size:18px;">import java.net.*;import java.util.Date;import java.util.logging.*;import java.io.*;public class DaytimeUDPServer { private final static int PORT = 13; private final static Logger audit = Logger.getLogger("requests"); private final static Logger errors = Logger.getLogger("errors"); public static void main(String[] args) { try (DatagramSocket socket = new DatagramSocket(PORT)) { while (true) { try { DatagramPacket request = new DatagramPacket(new byte[1024], 1024); socket.receive(request); String daytime = new Date().toString(); byte[] data = daytime.getBytes("US-ASCII"); DatagramPacket response = new DatagramPacket(data, data.length, request.getAddress(), request.getPort()); socket.send(response); audit.info(daytime + " " + request.getAddress()); } catch (IOException | RuntimeException ex) { errors.log(Level.SEVERE, ex.getMessage(), ex); } } } catch (IOException ex) { errors.log(Level.SEVERE, ex.getMessage(), ex); } } }</span>
12.4 DatagramPacket类
构造函数
接收数据报的构造函数
这两个构造函数可以创建新的DatagramPacket对象从网络接收数据:
public DatagramPacket(byte[] buffer, int length)
当Socket接收一个数据报时,它将数据报的数据部分存储在buffer,从buffer[0]开始,一直到包完全存储,或者直到向buffer写入了length字节。
public DatagramPacket(byte[] buffer, int offset, int length)
第二个构造函数,将从buffer[offet]开始存储。
不要创建超过8192(8k)字节数据的DatagramPacket对象。
发送数据报的构造函数
下面4个构造函数会创建新的DatagramPacket对象,用来通过网络发送数据:
public DatagramPacket(byte[] data, int length, InetAddress destination, int port)
public DatagramPacket(byte[] data, int offset, int length, InetAddress destination, int port)
public DatagramPacket(byte[] data, int length, SocketAddress destination)
public DatagramPacket(byte[] data, int offset, int length,SocketAddress destination)
get方法
有6个获取数据报不同部分地方法,主要用于接收网络的数据报。
public InetAddress getAddress()
public int getPort()
public SocketAddress getSocketAddress()
包含远程主机的IP地址和端口。
public byte[] getData()
返回数据报中的数据
将数据转化为字符串:
String s = new String(dp.getData(),"UTF-8");
将数据转化为其他java数据
InputStream in = new ByteArrayInputStream(packet.getData(),packet.getOffset(),packet.getLength());
DataInputStream din = new DataInputStream(in);
public int getLength()
返回数据报中数据的字节数。
public int getOffset()
返回该数组中的一个位置,即开始填充数据报数据的那个位置。
set方法
public void setData(byte[] data)
可以重复地发送相同的DatagramPacket对象,每次只改变数据。
public void setData(byte[] data, int offset, int length)
public void setAddress(InetAddress remote)
会修改数据报发往的地址。这允许你将同一个数据报发送给多个不同的接收方。
public void setPort(int port)
public void setAddress(SocketAddress remote)
会改变数据报包要发往的地址和端口。
public void setLength(int length)
改变内部缓冲区中包含实际数据报数据的字节数,而不包括未填充数据的空间。
12.5 DatagramSocket类
客户端和服务器使用DatagramSocket是一样的,区别在于使用匿名端口还是已知端口。这与TCP中不同。
构造函数
所有构造函数都只处理本地地址和端口。远程地址和端口存储在DatagramPacket中,而不是DatagramScoket。
一个DatagramSocket可以从多个远程主机和端口收发数据报。
public DatagramSocket() throws SocketException
创建一个绑定到匿名端口的Socket.
public DatagramSocket(int port) throws SocketException
创建一个在指定端口监听入站数据报的Socket。
public DatagramSocket(int port, InetAddress interface) throws SocketException
public DatagramSocket(SocketAddress interface) throws SocketException
protected DatagramSocket(DatagramSocketImpl impl ) throws SocketException
发送和接收数据报
DatagramSocket类的首要任务是发送和接收UDP数据报。一个Socket可以既发送又接收数据报。可以同时对多台主机收发数据。
public void send(DatagramPacket dp) throws IOException
public void receive(DatagramPacket d) throws IOException
public void close()
调用DatagramSocket对象的close()方法将释放该Socket占用的端口。
public int getLocalPort()
返回Socket正在监听的本地端口。
public InetAddress getLocalAddress()
返回Socket绑定到的本地地址。
public SocketAddress getLocalSocketAddress()
返回包装了Socket绑定到的本地接口和端口
管理连接
public void connect(InetAddress host, int port)
指定DatagramSocket只对指定远程主机和指定远程端口收发数据包。
public void disconnect()
disconnect()方法中断已连接DatagramSocket的“连接”,从而可以再次收发任何主机和端口的包。
public int getPort()
返回连接的远程端口。
public InetAddress getInetAddress()
返回连接的远程主机的地址
public InetAddress getRemoteSocketAddress()
返回连接的远程主机的地址
Socket选项
Java支持6个UDP Socket选项:
*SO_TiMEOUT
SO_TIMEOUT是receive()在抛出异常前等待入站数据报的时间,以毫秒计。
public void getSoTimeout(int timeout) throws SocketException
public int getSoTimeout() throws IOException
*SO_RCVBUF
DatagramSocket的SO_RCVBUF选项与Socket的SO_RCVBUF选项紧密相关。
public void setReceiveBufferSize(int size) throws SocketException
public int getReceiveBufferSize() throws SocketException
*SO_SNDBUF
获取和设置建议用于网络传输的发送缓冲区大小:
public void setSendBufferSize(int size) throws SocketException
public int getSendBufferSize() throws SocketException
*SO_REUSEADDR
SO_REUSEADDR可以控制是否允许多个数据报Socket同时绑定到相同的端口和地址。
public void setReuseAddress(boolean on) throws SocketException
public boolean getReuseAddress() throws SocketException
*SO_BROADCAST
SO_BROADCAST选项控制是否允许一个Socket向广播地址收发包。
public void setBroadcast(boolean on) throws SocketException
public boolean getBroadcast() throws SocketException
*IP_TOS
业务流类型。
12.6 一些有用的应用
12.7 DatagramChannel
DatagramChannel类用于非阻塞UDP应用程序。
使用DatagramChannel
打开一个Socket
DatagramChannel channel = DatagramChannel.open();
SocketAddress address = new InetSocketAddress(3141);
DatagramSocket socket = channel.socket();
socket.bind(address);
Java7中简化:
SocketAddress address = new InetSocketAddress(3141);
channel.bind(address);
接收
public SocketAddress receive(ByteBuffer dst) throws IOException
如果通道是阻塞的,这个方法砸读取到包之前不会返回。
如果通道是非阻塞的,没有包可以读取的情况下这个方法会立即返回null。
<span style="font-size:18px;">import java.io.*;import java.net.*;import java.nio.*;import java.nio.channels.*;public class UDPDiscardServerWithChannels { public final static int PORT = 9; public final static int MAX_PACKET_SIZE = 65507; public static void main(String[] args) { try { DatagramChannel channel = DatagramChannel.open(); DatagramSocket socket = channel.socket(); SocketAddress address = new InetSocketAddress(PORT); socket.bind(address); ByteBuffer buffer = ByteBuffer.allocateDirect(MAX_PACKET_SIZE); while (true) { SocketAddress client = channel.receive(buffer); buffer.flip(); System.out.print(client + " says "); while (buffer.hasRemaining()) System.out.write(buffer.get()); System.out.println(); buffer.clear(); } } catch (IOException ex) { System.err.println(ex); } }}</span>
public int send(ByteBuffer src,SocketAddress target) throws IOExcetion
<span style="font-size:18px;">import java.io.*;import java.net.*;import java.nio.*;import java.nio.channels.*;public class UDPEchoServerWithChannels { public final static int PORT = 7; public final static int MAX_PACKET_SIZE = 65507; public static void main(String[] args) { try { DatagramChannel channel = DatagramChannel.open(); DatagramSocket socket = channel.socket(); SocketAddress address = new InetSocketAddress(PORT); socket.bind(address); ByteBuffer buffer = ByteBuffer.allocateDirect(MAX_PACKET_SIZE); while (true) { SocketAddress client = channel.receive(buffer); buffer.flip(); channel.send(buffer, client); buffer.clear(); } } catch (IOException ex) { System.err.println(ex); } }}</span>
打开一个数据报通道,可以使用Connect()方法将它连接到一个特定的远程地址:
SocketAddress remote = new InetSocketAddress("time.nist.gov",37);
channel.connect(remote);
读取
有3个read()方法,只用于已连接的通道。
public int read(ByteBuffer dst) throws IOException
public long read(ByteBuffer[] dsts) throws IOException
public long read(ByteBuffer[] dsts,int offset,int length) throws IOException
写入
有3个write()方法,只用于已连接的通道。
public int write(ByteBuffer src) throws IOException
public long write(ByteBuffer[] dsts) throws IOException
public long write(ByteBuffer[] dsts,int offset,int length) throws IOException
选择器和非阻塞I/O也可以用于UDP:
基于通道的UDP echo客户端:
import java.io.*;import java.net.*;import java.nio.*;import java.nio.channels.*;import java.util.*;public class UDPEchoClientWithChannels { public final static int PORT = 7; private final static int LIMIT = 100; public static void main(String[] args) { SocketAddress remote; try { remote = new InetSocketAddress(args[0], PORT); } catch (RuntimeException ex) { System.err.println("Usage: java UDPEchoClientWithChannels host"); return; } try (DatagramChannel channel = DatagramChannel.open()) { channel.configureBlocking(false); channel.connect(remote); Selector selector = Selector.open(); channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE); ByteBuffer buffer = ByteBuffer.allocate(4); int n = 0; int numbersRead = 0; while (true) { if (numbersRead == LIMIT) break; // wait one minute for a connection selector.select(60000); Set<SelectionKey> readyKeys = selector.selectedKeys(); if (readyKeys.isEmpty() && n == LIMIT) { // All packets have been written and it doesn't look like any // more are will arrive from the network break; } else { Iterator<SelectionKey> iterator = readyKeys.iterator(); while (iterator.hasNext()) { SelectionKey key = (SelectionKey) iterator.next(); iterator.remove(); if (key.isReadable()) { buffer.clear(); channel.read(buffer); buffer.flip(); int echo = buffer.getInt(); System.out.println("Read: " + echo); numbersRead++; } if (key.isWritable()) { buffer.clear(); buffer.putInt(n); buffer.flip(); channel.write(buffer); System.out.println("Wrote: " + n); n++; if (n == LIMIT) { // All packets have been written; switch to read-only mode key.interestOps(SelectionKey.OP_READ); } } } } } System.out.println("Echoed " + numbersRead + " out of " + LIMIT + " sent"); System.out.println("Success rate: " + 100.0 * numbersRead / LIMIT + "%"); } catch (IOException ex) { System.err.println(ex); } }}
关闭
public void close() throws IOException
Socket选项//Java 7
SO_SNDBUF StandardSocketOptions.SO_SNDBUF Integer 用于发送数据报包的缓冲区大小
SO_RCVBUF StandardSocketOptions.SO_RCVBUF Integer 用于接收数据报包的缓冲区大小
SO_REUSEADDR ... Boolean 启用/禁用地址重用
SO_BROADCAST ... Boolean 启用/禁用广播消息
IP_TOS ... Integer 业务流类型
IP_MULTICAST_IF ... NetworkInterface 用于组播的本地网络接口
IP_MULTICAST_TTL Integer 用于组播数据报的生存时间值
IP_MULTICAST_LOOP Boolean 启用/禁用组播数据报的回送
0 0
- 第12章 UDP
- 第24章 UDP基本原理
- 第8章:UDP套接口
- 第2章 传输层:TCP、UDP和SCTP
- 第8章基本UDP套接口编程
- TCP/IP 第11章 UDP用户数据报协议
- 《TCP/IP详解》学习笔记-第11章 UDP
- [TCP/IP详解:协议]第11章UDP学习记录
- 【读书笔记】TCP/IP网络编程 第6章UDP数据传输
- 第8章 基本UDP套接字编程
- UNPv13:#第2章#传输层:TCP、UDP和SCTP
- udp 编码的connect 函数 ——unix 网络编程 第8章
- TCP/IP详解读书笔记(第11章 UDP:用户数据报协议)
- Unix网络编程代码 第8章 基本UDP套接字编程
- unix网络编程第2章:传输层:TCP,UDP和SCTP
- MINA2.0用户手册中文版--第二章 第五节 UDP客户端实例
- 【TCP/IP详解】第11章 UDP:用户数据报协议
- MINA2.0用户手册中文版--第二章 第五节 UDP客户端实例
- HDU 5213 (莫队算法)
- 作业-4
- TextView can not be cast to ViewGroup
- 数据结构之链表学习笔记
- C++上机实验4-求1000以内所有偶数的和
- 第12章 UDP
- Shell:脚本调试
- 我的第一篇博客
- 百度地图离线开发
- rocketmq cluster下concurrently重试机制实现
- 241. Different Ways to Add Parentheses
- Error creating bean with name 'sessionFactory' defined in class path resource [applicationContext.xm
- 关于openvas的酸甜苦辣
- 英语