非阻塞服务器建立问题
这是一份关于建立非阻塞服务器的完整代码,有好几个地方不明白!!!希望高人指教。。。。以前用的都是阻塞式socket通信,第一次接触非阻塞通信,对于缓冲区,以及读写控制等都不是很懂!对于疑惑的地方我在以下代码中标出!求解答。。。(各种函数,类我都在jdk文档中看过解释,所以请勿直接贴解释,比较笨,希望得到通俗易懂的理解)
[code=Java]
package thread2;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.net.*;
import java.util.*;
public class EchoServer{
private Selector selector = null;
private ServerSocketChannel serverSocketChannel = null;
private int port = 8000;
private Charset charset=Charset.forName( "GBK ");
public EchoServer()throws IOException{
selector = Selector.open();
serverSocketChannel= ServerSocketChannel.open();
serverSocketChannel.socket().setReuseAddress(true);
serverSocketChannel.socket().bind(new InetSocketAddress(port));
System.out.println( "服务器启动 ");
}
public void accept(){
for(;;){
try{
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println( "接收到客户连接,来自: " +
socketChannel.socket().getInetAddress() +
": " + socketChannel.socket().getPort());
socketChannel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
synchronized(gate){ //问题1:此处同步的运用不是很懂!关gate什么事???后面也有用到
selector.wakeup(); //问题2:wakeup()在此处的作用
socketChannel.register(selector,
SelectionKey.OP_READ |
SelectionKey.OP_WRITE, buffer); //问题3:buffer说是附加的项目,有什么用?
}
}catch(IOException e){e.printStackTrace();}
}
}
private Object gate=new Object();
public void service() throws IOException{
for(;;){
synchronized(gate){}
int n = selector.select();
if(n==0)continue;
Set readyKeys = selector.selectedKeys();
Iterator it = readyKeys.iterator();
while (it.hasNext()){
SelectionKey key=null;
try{
key = (SelectionKey) it.next();
it.remove();
if (key.isReadable()) { //问题4:selector的监控机制我有研究过!基本知识懂。但不明白如何控制数据的通信!比如我要通过此服务器像客户端发送数据该调用什么函数?或是进行什么操作?这个困扰了很久。。。务必解答呀~~~
receive(key);
}
if (key.isWritable()) {
send(key);
}
}catch(IOException e){
e.printStackTrace();
try{
if(key!=null){
key.cancel();
key.channel().close();
}
}catch(Exception ex){e.printStackTrace();}
}
}//#while
}//#while
}
public void send(SelectionKey key)throws IOException{
ByteBuffer buffer=(ByteBuffer)key.attachment(); //问题5:这个函数里面对bytebuffer的各种操作,flip(),compact()等。。。看来解释,还是没搞懂实际作用= =。
SocketChannel socketChannel=(SocketChannel)key.channel();
buffer.flip(); //把极限设为位置
String data=decode(buffer);
if(data.indexOf( "\n ")==-1)return;
String outputData=data.substring(0,data.indexOf( "\n ")+1);
System.out.print(outputData);
ByteBuffer outputBuffer=encode( "echo: "+outputData);
while(outputBuffer.hasRemaining())
socketChannel.write(outputBuffer);
ByteBuffer temp=encode(outputData);
buffer.position(temp.limit());
buffer.compact();
if(outputData.equals( "bye\r\n ")){
key.cancel();
socketChannel.close();
System.out.println( "关闭与客户的连接 ");
}
}
public void receive(SelectionKey key)throws IOException{
ByteBuffer buffer=(ByteBuffer)key.attachment();
SocketChannel socketChannel=(SocketChannel)key.channel();
ByteBuffer readBuff= ByteBuffer.allocate(32);
socketChannel.read(readBuff);
readBuff.flip();
buffer.limit(buffer.capacity());
buffer.put(readBuff);
}
public String decode(ByteBuffer buffer){ //解码
CharBuffer charBuffer= charset.decode(buffer);
return charBuffer.toString();
}
public ByteBuffer encode(String str){ //编码
return charset.encode(str);
}
public static void main(String args[])throws Exception{
final EchoServer server = new EchoServer();
Thread accept=new Thread(){
public void run(){
server.accept();
}
};
accept.start();
server.service();
}
}
[/code]
路过的大神,求解= =。这两天研究这个非阻塞通信,纠结死了。请耐心解答,感激不尽!
[解决办法]
非阻塞通信要细讲起来太多(做过C的scoket通信的可能会更容易理解)
就LZ的问题回答吧
//问题1:此处同步的运用不是很懂!关gate什么事???后面也有用到
gate在这里只是一个锁的作用,这种用法在多线程里很常见,就是一个线程A要执行一段代码,就先要获得gate对象的锁,如果gate的锁已经有线程B在使用,那么线程A就会等待只到线程B把锁交出来,所以gate在这里就是协调accept和service的同步(因为accept方法是用一个线程调用,service方法是主线程调用)
//问题2:wakeup()在此处的作用
就是如果有某个线程因为调用selct而正在堵塞中,那么wakeup就是让堵塞的线程立即返回,即select处理立刻结束,这样就可以遍历选择器的key去判断哪些通道有数据,可以采取什么操作等等
//问题3:buffer说是附加的项目,有什么用?
就是给通道一个缓存的附加项目,这样可以方便从通道中获得一个附加项目来操作,否则就要自己根据不同的通道去创建和查找通道的缓存,如果通道太多,管理就比较混乱(因为哪个缓存对应哪个通道要自己管理),所以还是让管道自己带着一个附加项目的缓存管理起来方便
//问题4:selector的监控机制我有研究过!基本知识懂。但不明白如何控制数据的通信!比如我要通过此服务器像客户端发送数据该调用什么函数?或是进行什么操作?这个困扰了很久。。。务必解答呀~~~
LZ你的receive方法和send方法就是接收和发送处理,key就是问题3处注册的通道的键(通过此键可以找回通信通道),socketChannel的read就是收信(就是从通道中读取数据到缓存),write就是送信(就把数据写到通道)
//问题5:这个函数里面对bytebuffer的各种操作,flip(),compact()等。。。看来解释,还是没搞懂实际作用= =。
缓存是线性操作位置(position)到限制(limit)这段区间,那么假如我们保存数据,比如从0位置开始一直保存到64位置,限制是100,因为继续读或者继续写的话,是从65位置开始的,而此时如果我们想读取0-64位置的信息,该怎么办?那么就要调用flip方法,这样就会把限制(limit)设置到当前64的位置(position),并把位置(position)设置到0,这样我们就可以读取从0位置开始到64位置限制的信息了
而compact就是压缩缓存,什么意思呢?简单的说就是把当前位置到限制位置的信息移动到0位置开始,然后把当前位置设置为limit-positin+1(也就是移动的长度+1),然后把限制(limit)设置为最大。还是用上面的例子来说明,当前位置是65,限制是100,那么调用compact就会把65-100位置的信息移动到从0位置开始,即把65位置的信息保存到0位置,66位置的信息保存到1位置,依次类推,这样就变成从65-100位置的信息移动到了0-35位置,然后再把当前位置设置为36,把限制设置为最大(比如最大是1024,那么limit就设置到1024)
[解决办法]
2、问题四中,我问错方向了。。。我想知道key.readable()和key.writeable(),其中某些通道也就是key的这两个方法的结果为什么突然就变成true,而实现后续的读写操作?是什么操纵的?我可以调用吗?因为我要实现向特定客户端发送信息的功能!如果这个不可控,那我如何实现= =。应该不是直接调用write函数吧。。。
一般不注册写事件,想什么时候写就什么时候写,想写什么就写什么。
[解决办法]