这几天需要实现一个底层基于UDP的协议,该协议底层使用UDP传输但是具有拥塞控制、超时重发、数据确认等功能又比TCP简单 (RUDP,Reliable UDP)。在实现协议底层的UDP服务时准备使用Java的NIO,在网上查资料都是以TCP为例讲的,于是自己研究了一下基于UDP的NIO。
NIO的思路是基于多路选择的,即由原来的每个连接都由一个线程来等待消息,改为每个连接都在选择器上注册,由选择器来等待。当然NIO引入了很多新的概念,如Channel,Buffer、Charset、Selector等,使得编程更简洁、更面向对象化。
下面贴出用NIO API改造成UDP示例代码,注意其中使用Charset来编码解码的过程(当然Charset还支持很多其他编码不仅局限于默认编码)以及Buffer的使用。
package
sinpo.usagedemo;
import
java.net.DatagramSocket;
import
java.net.InetSocketAddress;
import
java.net.SocketAddress;
import
java.nio.ByteBuffer;
import
java.nio.CharBuffer;
import
java.nio.channels.DatagramChannel;
import
java.nio.channels.SelectionKey;
import
java.nio.channels.Selector;
import
java.nio.charset.Charset;
import
java.util.Iterator;
import
java.util.Set;
/**
*
@author
徐辛波(sinpo.xu@hotmail.com) Oct 19, 2008
*/
public class
UDPServer
extends
Thread
{
public
void
run
() {
Selector selector =
null
;
try
{
DatagramChannel channel = DatagramChannel.open
()
;
DatagramSocket socket = channel.socket
()
;
channel.configureBlocking
(
false
)
;
socket.bind
(
new
InetSocketAddress
(
5057
))
;
selector = Selector.open
()
;
channel.register
(
selector, SelectionKey.OP_READ
)
;
}
catch
(
Exception e
) {
e.printStackTrace
()
;
}
ByteBuffer byteBuffer = ByteBuffer.allocate
(
65536
)
;
while
(
true
) {
try
{
int
eventsCount = selector.select
()
;
if
(
eventsCount >
0
) {
Set selectedKeys = selector.selectedKeys
()
;
Iterator iterator = selectedKeys.iterator
()
;
while
(
iterator.hasNext
()) {
SelectionKey sk =
(
SelectionKey
)
iterator.next
()
;
iterator.remove
()
;
if
(
sk.isReadable
()) {
DatagramChannel datagramChannel =
(
DatagramChannel
)
sk
.channel
()
;
SocketAddress sa = datagramChannel
.receive
(
byteBuffer
)
;
byteBuffer.flip
()
;
// 测试:通过将收到的ByteBuffer首先通过缺省的编码解码成CharBuffer 再输出
CharBuffer charBuffer = Charset.defaultCharset
()
.decode
(
byteBuffer
)
;
System.out.println
(
"receive message:"
+ charBuffer.toString
())
;
byteBuffer.clear
()
;
String echo =
"This is the reply message from 服务器。"
;
ByteBuffer buffer = Charset.defaultCharset
()
.encode
(
echo
)
;
datagramChannel.write
(
buffer
)
;
}
}
}
}
catch
(
Exception e
) {
e.printStackTrace
()
;
}
}
}
public static
void
main
(
String
[]
args
) {
new
UDPServer
()
.start
()
;
}
}
|
Client
package
sinpo.usagedemo;
import
java.net.InetSocketAddress;
import
java.net.SocketAddress;
import
java.nio.ByteBuffer;
import
java.nio.channels.DatagramChannel;
import
java.nio.channels.SelectionKey;
import
java.nio.channels.Selector;
import
java.nio.charset.Charset;
import
java.util.Iterator;
import
java.util.Set;
/**
*
@author
徐辛波(sinpo.xu@hotmail.com)
* Oct 19, 2008
*/
public class
UDPClient
extends
Thread
{
public
void
run
() {
DatagramChannel channel =
null
;
Selector selector =
null
;
try
{
channel = DatagramChannel.open
()
;
channel.configureBlocking
(
false
)
;
SocketAddress sa =
new
InetSocketAddress
(
"localhost"
,
5057
)
;
channel.connect
(
sa
)
;
}
catch
(
Exception e
) {
e.printStackTrace
()
;
}
try
{
selector = Selector.open
()
;
channel.register
(
selector, SelectionKey.OP_READ
)
;
channel.write
(
Charset.defaultCharset
()
.encode
(
"Tell me your time"
))
;
}
catch
(
Exception e
) {
e.printStackTrace
()
;
}
ByteBuffer byteBuffer = ByteBuffer.allocate
(
100
)
;
while
(
true
) {
try
{
int
eventsCount = selector.select
()
;
if
(
eventsCount >
0
) {
Set selectedKeys = selector.selectedKeys
()
;
Iterator iterator = selectedKeys.iterator
()
;
while
(
iterator.hasNext
()) {
SelectionKey sk =
(
SelectionKey
)
iterator.next
()
;
iterator.remove
()
;
if
(
sk.isReadable
()) {
DatagramChannel datagramChannel =
(
DatagramChannel
)
sk
.channel
()
;
datagramChannel.read
(
byteBuffer
)
;
byteBuffer.flip
()
;
//TODO 将报文转化为RUDP消息并调用RUDP协议处理器来处理
System.out.println
(
Charset.defaultCharset
()
.decode
(
byteBuffer
)
.toString
())
;
byteBuffer.clear
()
;
datagramChannel.write
(
Charset.defaultCharset
()
.encode
(
"Tell me your time"
))
;
}
}
}
}
catch
(
Exception e
) {
e.printStackTrace
()
;
}
}
}
}
|