高分求救:如何用 ServerSocketChannel & SocketChannel做的一个接收客户端的数据包?(在线)
1、客户端程序可以连上来,但发送一次之后,发送第2次,服务器就出问题了,主要是在run()方法中的while (selector.select() > 0)这句上,本来selector.select()应该是阻塞的,但第2次发送后,就不阻塞了,立刻返回,而且为false。请问这是为什么?
2、连第二个客户端的时候,程序也没有检测到,key.isAcceptable()始终为false,是怎么回事?
代码如下:
public void init(int portNumber) throws Exception{
//分配一个ServerSockerChannel
serverChannel = ServerSocketChannel.open();
//从ServerSocketChannel里获得一个对应的Socket
serverSocket = serverChannel.socket();
//生成一个Selector
selector = Selector.open();
//把socket绑定到端口上
serverSocket.bind(new InetSocketAddress(portNumber));
//serverChannel为非bolck
serverChannel.configureBlocking(false);
//通过Selector注册ServerSocketChannel
//serverChannel.register(selector,SelectionKey.OP_ACCEPT);
serverChannel.register(selector,SelectionKey.OP_ACCEPT);
}
protected void registerChannel(Selector selector,SelectableChannel channel,int ops) throws Exception{
if (channel == null){
return;
}
channel.configureBlocking(false);
channel.register(selector,ops);
}
protected void processData(SelectionKey key) throws Exception{
SocketChannel socketChannel = (SocketChannel)key.channel();
USSDMessage message = (USSDMessage)messageFactory.createMessage( " ") ;
USSDEvent event = null;
int count;
buffer.clear();
//读取所有的数据
while((count = socketChannel.read(buffer)) > 0){
System.out.println( "count: "+count);
buffer.flip();
while(buffer.hasRemaining()){
message.buildMessageFromBuffer(buffer);
if(message != null){
int intCmdId = message.getCmdId();
if(intCmdId == Constant.USSD_MESSAGE_COMMAND_SURVEILLANCE_REQ)
{
event = new USSDEvent(buffer);
this.applicationContext.publishEvent(event);
}
message = this.resetUSSDMessage(message);
buffer.clear();
ByteBuffer newBuffer = buffer.slice() ;
message.appendToBuffer(newBuffer);
System.out.println( "buffer write to socketChannel begin! ");
// message.buildMessageFromBuffer(buffer);
socketChannel.write(buffer); //在socket里写数据
System.out.println( "buffer write to socketChannel end! ");
}
}
buffer.clear(); //清空buffer
//count = -1;
//break;
}
if(count < 0){
//count <0,说明已经读取完毕
socketChannel.close();
}
}
private void processData(SocketChannel socketChannel) throws Exception
{
USSDMessage message = (USSDMessage)messageFactory.createMessage( " ") ;
USSDEvent event = null;
int count;
buffer.clear();
//读取所有的数据
while((count = socketChannel.read(buffer) ) > 0){
// socketChannel.read(buffer);
buffer.flip();
while(buffer.hasRemaining()){
message.buildMessageFromBuffer(buffer);
if(message != null){
int intCmdId = message.getCmdId();
if(intCmdId == Constant.USSD_MESSAGE_COMMAND_SURVEILLANCE_REQ){
event = new USSDEvent(buffer);
this.applicationContext.publishEvent(event);
}
message = this.resetUSSDMessage(message);
buffer.clear();
ByteBuffer newBuffer = buffer.slice() ;
message.appendToBuffer(newBuffer);
socketChannel.write(buffer); //在socket里写数据
}
}
buffer.clear(); //清空buffer
//count = -1;
//break;
}
//if(count < 0){
////count <0,说明已经读取完毕
//socketChannel.close();
//}
}
public void startWork() throws Exception{
while(true){
int n = selector.select();//获得IO准备就绪的channel数量
System.out.println( "selector.select(): "+n);
if(n == 0){
continue;
}
//用一个iterator 返回Selector的selectedkeys
Iterator it = selector.selectedKeys().iterator();
System.out.println( "selector.selectedKeys: "+selector.selectedKeys().size());
//处理每一个SelectionKey
while(it.hasNext()){
SelectionKey key = (SelectionKey)it.next();
//判断是否有新的连接到达
if(key.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel)key.channel();
SocketChannel channel = server.accept();
channel.configureBlocking(false);
registerChannel(selector,channel,SelectionKey.OP_READ);
//doWork(channel);
processData(channel);
}
//判断是否有数据在此channel里需要读取
if(key.isReadable()){
processData(key);
}
it.remove();
}
}
}
调用:
public void run() {
try {
init(this.serverPort);
startWork();
} catch (Exception e) {
e.printStackTrace();
}
}
请高人指点!!!!!
[解决办法]
package nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Iterator;
import java.util.Set;
public class SocketNIOServer
{
private ByteBuffer _buffer = ByteBuffer.allocate(8);
private IntBuffer _intBuffer = _buffer.asIntBuffer();
private SocketChannel _clientChannel = null;
private ServerSocketChannel _serverChannel = null;
public void start()
{
try
{
openChannel();
waitForConnection();
}
catch(IOException e)
{
System.err.println(e.toString());
}
}
private void openChannel() throws IOException
{
_serverChannel = ServerSocketChannel.open();
// 非阻塞的加法服务器 首先在openChannel方法中加入语句
_serverChannel.configureBlocking(false);// 设置成为非阻塞模式
_serverChannel.socket().bind(new InetSocketAddress(10000));
System.out.println( "服务器通道已经打开 ");
}
// private void waitForConnection() throws IOException
// {
// while(true)
// {
// _clientChannel = _serverChannel.accept();
// if(_clientChannel != null)
// {
// System.out.println( "新的连接加入 ");
// processRequest();
// _clientChannel.close();
// }
// }
// }
// 重写WaitForConnection方法的代码如下,使用非阻塞方式
private void waitForConnection() throws IOException
{
Selector acceptSelector = SelectorProvider.provider().openSelector();
/*
* 在服务器套接字上注册selector并设置为接受accept方法的通知。
* 这就告诉Selector,套接字想要在accept操作发生时被放在ready表上,因此,允许多元非阻塞I/O发生。
*/
SelectionKey acceptKey = _serverChannel.register(acceptSelector,SelectionKey.OP_ACCEPT);
int keysAdded = 0;
/* select方法在任何上面注册了的操作发生时返回 */
while((keysAdded = acceptSelector.select()) > 0)
{
// 某客户已经准备好可以进行I/O操作了,获取其ready键集合
Set readyKeys = acceptSelector.selectedKeys();
Iterator i = readyKeys.iterator();
// 遍历ready键集合,并处理加法请求
while(i.hasNext())
{
SelectionKey sk = (SelectionKey)i.next();
i.remove();
ServerSocketChannel nextReady = (ServerSocketChannel)sk.channel();
// 接受加法请求并处理它
Socket _clientSocket = nextReady.accept().socket();
processRequest();
_clientSocket.close();
}
}
}
private void processRequest() throws IOException
{
_buffer.clear();
_clientChannel.read(_buffer);
int result = _intBuffer.get(0) + _intBuffer.get(1);
_buffer.flip();
_buffer.clear();
_intBuffer.put(0,result);
_clientChannel.write(_buffer);
}
public static void main(String[] args)
{
new SocketNIOServer().start();
}
}