问题描述
- java 关于NIO实现UDP数据传输问题 ,急谢谢,C币不足请不要介意
- 各位大侠好,小弟想问一下问题,搞了一两天没有搞明白的。因为要实现一个UDP传输服务端,于是在网上找了很多资料然后就写了一个。但是写好之后发现有两个很严重的问题,希望各位大哥给点意见或者思路去解决。
问题一:启动服务端,同时也启动客户端,客户端传输数据服务器正常接收,但是断开客户端后,再启动客户端,服务器就收不到任何客户端发送的消息,好像是服务器关闭了UDP一样,但是重启服务器后(重新打开UDP)客户端既可以发送信息过来。
问题二:多个客户端。第一个客户端连接上后,第二个客户端怎么也链接不上了。即使关闭了第一个客户端也一样。
如下是代码:
package com.gateway.socket;import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;
import java.util.Set;import org.apache.log4j.Logger;
public class UDPEchoServerSelector extends Thread {
private static final Logger log = Logger.getLogger(ServerSocket.class);//private InetSocketAddress inetSocketAddress;// socket处理类private UDPSocketHandler handler = new UDPSocketHandler();// 注册的接受服务private SocketReceiver receiver = null;/** * 初始化socket * * @param receiver * @param hostname * @param port * @throws IOException * @throws UnknownHostException */public UDPEchoServerSelector(SocketReceiver receiver String hostname int port) { if (hostname.isEmpty()) { inetSocketAddress = new InetSocketAddress(port); } else { inetSocketAddress = new InetSocketAddress(hostname port); } this.receiver = receiver;}@Overridepublic void run() { try { Selector selector = Selector.open(); // 创建选择器,可以处理多路通道。 DatagramChannel serverSocketChannel = DatagramChannel.open(); // 打开通道 serverSocketChannel.configureBlocking(false); // 非阻塞 serverSocketChannel.socket().bind(inetSocketAddress); /* * 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_READ事件注册该事件后, * 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。 */ serverSocketChannel.register(selector SelectionKey.OP_READ new ClientData()); log.info(""Server: socket server started.""); /* * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理 */ while (true) { // 轮询 // 当注册的事件到达时,方法返回;否则该方法会一直阻塞 int nKeys = selector.select(); if (nKeys == 0) { continue; } // 得到选择键列表 Set Keys = selector.selectedKeys(); Iterator it = Keys.iterator(); while (it.hasNext()) { SelectionKey key = null; key = (SelectionKey) it.next(); // 键为位掩码 it.remove(); // 客户端请求连接事件 if (key.isValid() && key.isWritable()) { log.info(""Server: SelectionKey is acceptable.""); handler.handleWrite(key); } if (key.isReadable()) {// 获得了可读的事件 log.info(""Server: SelectionKey is readable.""); handler.receiveMsg(key receiver); } } Keys.clear(); } } catch (IOException e) { e.printStackTrace(); }}public static class ClientData { public SocketAddress clientAddress; public ByteBuffer buffer = ByteBuffer.allocate(255);}
}
package com.gateway.socket;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import org.apache.log4j.Logger;
import com.gateway.common.DeviceDataTools;
import com.gateway.common.data.HexUtils;
import com.gateway.socket.UDPEchoServerSelector.ClientData;/**
- 处理socket类
- @author Andy
- */
public class UDPSocketHandler {private static Logger log = Logger.getLogger(UDPSocketHandler.class);
/**
- 链接请求
- @throws IOException
*/
public void handleWrite(SelectionKey key) {
try {
DatagramChannel channel = (DatagramChannel) key.channel();
ClientData clntDat = (ClientData) key.attachment();
clntDat.buffer.flip(); // 从起始位置开始发送
int bytesSent;bytesSent = channel.send(clntDat.buffer clntDat.clientAddress);if (bytesSent != 0) { key.interestOps(SelectionKey.OP_READ); // 关注客户端发送数据}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
- 读请求
- @throws IOException
*/
public void receiveMsg(SelectionKey key SocketReceiver receiver) {ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.clear();
DatagramChannel socketChannel = (DatagramChannel) key.channel();//非阻塞
try {
socketChannel.configureBlocking(false);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}System.out.println(""channel code:"" + socketChannel.hashCode());
try {
while (true) {InetSocketAddress client = (InetSocketAddress) socketChannel .receive(byteBuffer); byteBuffer.flip(); // byteBuffer中传过来的是10进制的bytep[] byte[] dst = new byte[byteBuffer.limit()]; byteBuffer.get(dst); // 将10进制的byte[]转化成16进制字符串 String data = HexUtils.converBytesToHex(dst); System.out.println(data); log.info(""Server: data1 = "" + data); byteBuffer.clear(); receiver.udpreceive(socketChannel data client); break;}
} catch (java.io.IOException e) {
//e.printStackTrace();
//this.closeChannel(key socketChannel);
} catch (Exception e) {
//e.printStackTrace();
//this.closeChannel(key socketChannel);
}
}
/**
- @param socketChannel*/private void closeChannel(SelectionKey key DatagramChannel socketChannel) {try { while (socketChannel.isOpen()) { key.cancel(); socketChannel.close(); }} catch (IOException e1) { e1.printStackTrace();}}
/**
- 根据socketKey从内存中获取channel,通过channel向client端发送消息
- @param socketKey
- 内存在channel对应的key
- @param data
- 发送的数据
- @return
- @throws IOException
*/
public static boolean send(String socketKey String data)
throws IOException {DatagramChannel socketChannel = SocketChannelMapper
.getUDPChannel(socketKey);if (socketChannel == null || !socketChannel.isOpen()) {
return false;
}
InetSocketAddress client = SocketChannelMapper
.getUDPInetSocketAddress(socketKey + ""address"");
boolean f = socketChannel.isConnected();
ByteBuffer byteBuffer = ByteBuffer.wrap(DeviceDataTools.hex2Byte(data));
if (f) {
socketChannel.write(byteBuffer);
} else {
socketChannel.connect(new InetSocketAddress(client.getAddress()
client.getPort()));
socketChannel.send(byteBuffer client);
}
return true;
}
/**
- 根据socketKey从内存中获取channel,通过channel向client端发送消息
- @param socketKey
- 内存在channel对应的key
- @param data
- 发送的数据
- @return
- @throws IOException
*/
public static boolean send(DatagramChannel socketChannel String data
InetSocketAddress client) throws IOException {if (socketChannel == null) {
return false;
}
System.out.println(""#########################ADDRESS""
+ client.getAddress());
System.out.println(""#########################PORT"" + client.getPort());boolean f = socketChannel.isConnected();
ByteBuffer byteBuffer = ByteBuffer.wrap(DeviceDataTools.hexStr2ByteArray(data));
if (f) {
socketChannel.write(byteBuffer);
} else {
socketChannel.connect(new InetSocketAddress(client.getAddress()
client.getPort()));
socketChannel.send(byteBuffer client);
}return true;
}
}
解决方案
建议楼主建一个服务器,多个客户端测试一下
解决方案二:
@模范青蛙,请问一下代码有没有问题?我目前就是将本机作为服务器来测试的