各位大侠好,小弟想问一下问题,搞了一两天没有搞明白的。因为要实现一个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;
}
@Override
public 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;
}
}