论坛首页 Java版

请问Java网络编程如何在不使用多线程的情况下实现异步返回?

浏览 1221 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
最后更新时间:2007-09-29 关键字: 网络编程
我指的是在不使用多线程的情况下进行并发处理

具体的情况是,在不使用多线程的情况下,服务器侦听某个端口,在有连接进来的时候会调用某个函数对此连接进行处理,但是由于处理的过程可能会比较长,为了不让后面连接的用户等待,需要此函数能异步返回,而不是阻塞在这个函数。

之所以希望不使用多线程,是因为考虑到同时连接的用户数会比较多,如果用多线程的话,线程创建,销毁和切换的开销会太大。虽然可以使用线程池,但是这样只是省去了创建和销毁线程的开销,线程切换的开销还是会存在,而且线程的数目也不好确定。

.NET中的Socket类有个方法是BeginReceive,这个方法中可以定义一个回调函数,在调用BeginReceive之后,回调函数会被调用,而BeginReceive可以立即返回,因此在不使用多线程的情况下可以实现并发处理。

我要的就是这种效果,请问在Java中怎么实现?

我找了些资料,里面提到用ServerSocketChannel,即NIO可以实现非阻塞,但是我实验了一下,好像不行。

代码如下:

package snake.test.channel;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

class Server {
	

	public static void main(String[] args) {
		Reactor re = null;
		try {
			re = new Reactor(6656);
			re.run();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}
}

class Reactor implements Runnable {
	final Selector selector;
	final ServerSocketChannel serverSocket;

	public int num = 0;
	Reactor(int port) throws IOException {
		selector = Selector.open();
		serverSocket = ServerSocketChannel.open();
		serverSocket.socket().bind(new InetSocketAddress(port));
		serverSocket.configureBlocking(false);
		SelectionKey sk = serverSocket.register(selector,
				SelectionKey.OP_ACCEPT);
		sk.attach(new Acceptor());
	}

	public void run() { // normally in a new Thread
		try {
			while (!Thread.interrupted()) {
				selector.select();
				Set selected = selector.selectedKeys();
				Iterator it = selected.iterator();
				while (it.hasNext()) {
					dispatch((SelectionKey) (it.next()));
				}
				selected.clear();
			}
		} catch (IOException ex) { /* ... */
		}
	}

	void dispatch(SelectionKey k) {
		Runnable r = (Runnable) (k.attachment());
		if (r != null)
			r.run();
	}

	class Acceptor implements Runnable { // inner
		public void run() {
			try {
				num++;
				SocketChannel c = serverSocket.accept();
				if (c != null)
					new Handler(selector, c, num);
			} catch (IOException ex) { /* ... */
			}
		}
	}

	

}

package snake.test.channel;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;

final class Handler implements Runnable {
	final SocketChannel socket;
	final SelectionKey sk;
	ByteBuffer input = ByteBuffer.allocate(1024);
	ByteBuffer output = ByteBuffer.allocate(1024);
	static final int READING = 0, SENDING = 1;
	int state = READING;
	
	private int num = 0;

	Handler(Selector sel, SocketChannel c,int n) throws IOException {
		System.out.println("Client Startup");
		socket = c;
		num = n;
		c.configureBlocking(false);
		// Optionally try first read now
		sk = socket.register(sel, 0);
		sk.attach(this);
		sk.interestOps(SelectionKey.OP_READ);
		sel.wakeup();
	}

	boolean inputIsComplete() {
		
		return true; /* ... */
	}

	boolean outputIsComplete() {
		return true; /* ... */
	}

	void process() {
		//System.out.println("Sleep");
		try {
			Thread.sleep(100000000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	// class Handler continued
	public void run() {
		try {
			System.out.println("Run   " + num);
			if (state == READING)
				read();
			else if (state == SENDING)
				send();
		} catch (IOException ex) { /* ... */
		}
	}

	void read() throws IOException {
		
		socket.read(input);
		if (inputIsComplete()) {
			process();
			state = SENDING;
			// Normally also do first write now
			sk.interestOps(SelectionKey.OP_WRITE);
		}
	}

	void send() throws IOException {
		socket.write(output);
		if (outputIsComplete())
			sk.cancel();
	}
}







Handler类中的process函数中调用了Thread.Sleep让程序睡眠一段长时间,如果可以异步返回的话,则多个用户可以并发处理,但是不成功
请问是否少了什么东西?
或者有什么别的技术?还是说一定使用多线程技术?
谢谢!
   
最后更新时间:2007-09-29
nio是符合你的要求,建议你还是找找相关资料吧。下面是一个小例子:
package com.ray.demo.nio1;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class SimpleEchoServer {

	private ByteBuffer writeBuffer = ByteBuffer.allocate(1024);

	public void acceptConnections(int port) throws Exception {
		Selector selector = Selector.open();
		ServerSocketChannel server = ServerSocketChannel.open();
		server.configureBlocking(false);
		InetSocketAddress isa = new InetSocketAddress(port);
		server.socket().bind(isa);
		server.register(selector, SelectionKey.OP_ACCEPT);
		while (selector.select() > 0) {
			Set readyKeys = selector.selectedKeys();
			Iterator i = readyKeys.iterator();
			while (i.hasNext()) {
				SelectionKey sk = (SelectionKey) i.next();
				if (sk.isAcceptable()) {
					acceptClient(sk, selector);
				}
				if (sk.isReadable()) {
					dealWithClientRead(sk, selector);
				}
				i.remove();
			}
		}
	}

	private void acceptClient(SelectionKey sk, Selector selector) throws IOException {
		ServerSocketChannel nextReady = (ServerSocketChannel) sk.channel();
		SocketChannel sc = nextReady.accept();
		sc.configureBlocking(false);
		sc.register(selector, SelectionKey.OP_READ);
	}

	private void dealWithClientRead(SelectionKey sk, Selector selector) throws IOException {
		SocketChannel sockChannel = (SocketChannel) sk.channel();
		ByteBuffer readBuffer = ByteBuffer.allocate(1024);
		readBuffer.clear();
		while (sockChannel.read(readBuffer) > 0)
			;
		readBuffer.flip();
		sockChannel.register(selector, SelectionKey.OP_WRITE, readBuffer);
		while(readBuffer.hasRemaining()) {
			sockChannel.write(readBuffer);
		}
	}

	public static void main(String[] args) throws Exception {
		SimpleEchoServer server = new SimpleEchoServer();
		server.acceptConnections(1234);
	}
}

不过话又说回来,如果你的处理过程是比较花时间的话,例如读数据库等,建议还是使用多线程吧。
   
0 请登录后投票
最后更新时间:2007-09-29
liangguanhui 写道
nio是符合你的要求,建议你还是找找相关资料吧。下面是一个小例子:
	private void dealWithClientRead(SelectionKey sk, Selector selector) throws IOException {
		SocketChannel sockChannel = (SocketChannel) sk.channel();
		ByteBuffer readBuffer = ByteBuffer.allocate(1024);
		readBuffer.clear();
		while (sockChannel.read(readBuffer) > 0)
			;
		readBuffer.flip();
		sockChannel.register(selector, SelectionKey.OP_WRITE, readBuffer);
		while(readBuffer.hasRemaining()) {
			sockChannel.write(readBuffer);
		}
	}

	

不过话又说回来,如果你的处理过程是比较花时间的话,例如读数据库等,建议还是使用多线程吧。



这种做法跟我上面贴的代码基本上是一样的
如果前一个用户在dealWithClientRead中阻塞的话,后面的用户请求是得不到处理的,我试验的做法是在里面加一个Thread.Sleep,让其睡眠一段长时间,结果后面发起连接的用户的请求都得不到处理,要等到前面的连接处理完才能得到处理

其实我要实现的处理过程并不是太花时间,主要的问题是同时连接的用户的数量会比较大,这样的话用多线程我担心系统开销太大,所以希望能有异步返回的做法,如果上面的nio的做法的话,貌似如果有一个用户阻塞的话,就会导致其他用户的请求得不到处理,这样就不是并发了

不知道我对nio的理解有没有错误,但是我自己写了一些类似你贴的代码的试验程序都做不到并发处理

谢谢!
   
0 请登录后投票
最后更新时间:2007-09-30
看看Cindy,javaeye搜索下就有。
   
0 请登录后投票
最后更新时间:2007-11-27
ssnake 写道
liangguanhui 写道
nio是符合你的要求,建议你还是找找相关资料吧。下面是一个小例子:
	private void dealWithClientRead(SelectionKey sk, Selector selector) throws IOException {
		SocketChannel sockChannel = (SocketChannel) sk.channel();
		ByteBuffer readBuffer = ByteBuffer.allocate(1024);
		readBuffer.clear();
		while (sockChannel.read(readBuffer) > 0)
			;
		readBuffer.flip();
		sockChannel.register(selector, SelectionKey.OP_WRITE, readBuffer);
		while(readBuffer.hasRemaining()) {
			sockChannel.write(readBuffer);
		}
	}

	

不过话又说回来,如果你的处理过程是比较花时间的话,例如读数据库等,建议还是使用多线程吧。



这种做法跟我上面贴的代码基本上是一样的
如果前一个用户在dealWithClientRead中阻塞的话,后面的用户请求是得不到处理的,我试验的做法是在里面加一个Thread.Sleep,让其睡眠一段长时间,结果后面发起连接的用户的请求都得不到处理,要等到前面的连接处理完才能得到处理

其实我要实现的处理过程并不是太花时间,主要的问题是同时连接的用户的数量会比较大,这样的话用多线程我担心系统开销太大,所以希望能有异步返回的做法,如果上面的nio的做法的话,貌似如果有一个用户阻塞的话,就会导致其他用户的请求得不到处理,这样就不是并发了

不知道我对nio的理解有没有错误,但是我自己写了一些类似你贴的代码的试验程序都做不到并发处理

谢谢!

NIO 只是在进行IO操作的时候 不  block calling thread.
业务处理可以用thread pool
   
0 请登录后投票
最后更新时间:2008-09-18
socket.getInputstream().avaible()

loop for : avaible()>0
   
0 请登录后投票
论坛首页 Java版

跳转论坛:
JavaEye推荐