java socket nio 多线程_Java Socket编程(五)NIO

一、服务器端的处理模式

1.迭代服务器

服务器只有处理完了当前用户的请求后,才会处理下一请求,因此是

迭代式的,在同一线程内处理。

ServerSocket serverSocket = new ServerSocket(port);

while(true) {

Socket clientSocket = serverSocket.accept();

...

}

2.一客户一线程

对每个连接的客户端都新建一个线程来处理它的请求。这种处理方式

的缺点很明显,当创建出来的同时运行的线程过多时,操作系统大量

时间都耗费在线程的切换和状态维护上,而非线程内的请求处理。

ServerSocket serverSocket = new ServerSocket(port);

while(true) {

Socket clientSocket = serverSocket.accept();

new HandlerThread(clientSocket).start();

...

}

3.线程池

使用固定数目的线程来监听请求,或者使用JDK的Executor线程池。相比

前面两种方法,这种处理方式在并发与性能之间达到了一个平衡,但是

它也是有自身的缺点,具体见后文的描述。

final ServerSocket serverSocket = new ServerSocket(port);

for (int i = 0; i < poolSize; i++) {

new Thread() {

public void run() {

while (true) {

Socket clientSocket = serverSocket.accept();

...

}

}

}.start();

}

4.缺点

类似上述这些基于阻塞式Socket和多线程的处理方式都有很大的缺点:

1.线程池的大小限制了系统可以同时服务的客户端总数。

2.如果想要保证某些连接优先获得服务,线程就很难做到。

3.如果这些线程需要读写共享的资源的话,还需要锁或其他互斥机制来同步。

二、NIO中的Select机制

通过NIO,我们可以很方便地在Java中实现Select机制。Select机制比上述的

处理方式好在哪呢?

首先,在Select机制中,我们不需要许多的线程来应对无数客户端的请求。我们

通过一次select()调用可以得到一组客户端,而这些客户端都是准备好读写的,

因此在后续的读写操作都是非阻塞的。所以这一组准备就绪的客户端读写请求就

都交给一个Selector类来处理了,完全的单线程,没有了上面的那些缺点!

此外,定长的Buffer缓冲类替代了流的概念,所以NIO里没有了输入输出流的身影,

而是将数据保存到Buffer对象中后,直接写入到Channel中。读取时,也是直接从

Channel中读取出Buffer。

public class TCPNonblockingClientTest {

public static void main(String[] args) throws Exception {

SocketChannel channel = SocketChannel.open();

channel.configureBlocking(false);

if (!channel.connect(new InetSocketAddress("localhost", 1234))) {

while (!channel.finishConnect())

System.out.print(".");

System.out.println();

}

String message = "helloselector";

ByteBuffer writeBuf = ByteBuffer.wrap(message.getBytes());

ByteBuffer readBuf = ByteBuffer.allocate(message.length());

int totalBytesRcvd = 0;

int bytesRcvd;

while (totalBytesRcvd < message.length()) {

if (writeBuf.hasRemaining())

channel.write(writeBuf);

if ((bytesRcvd = channel.read(readBuf)) == -1)

throw new SocketException("Connection closed");

totalBytesRcvd += bytesRcvd;

System.out.print(".");

}

System.out.println("Received: " + new String(readBuf.array(), 0, totalBytesRcvd));

channel.close();

}

}

public class TCPSelectorServerTest {

public static void main(String[] args) throws Exception {

Selector selector = Selector.open();

int[] ports = { 1234, 5678 };

for (int port : ports) {

ServerSocketChannel listenChannel = ServerSocketChannel.open();

listenChannel.socket().bind(new InetSocketAddress("localhost", port));

listenChannel.configureBlocking(false);

listenChannel.register(selector, SelectionKey.OP_ACCEPT);

}

while (true) {

if (selector.select(3000) == 0) {

System.out.print(".");

continue;

}

Iterator keyIter = selector.selectedKeys().iterator();

while (keyIter.hasNext()) {

SelectionKey key = keyIter.next();

if (key.isAcceptable()) {

SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();

clientChannel.configureBlocking(false);

clientChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(32));

}

if (key.isReadable()) {

SocketChannel clientChannel = (SocketChannel) key.channel();

ByteBuffer buffer = (ByteBuffer) key.attachment();

long bytesRead = clientChannel.read(buffer);

if (bytesRead == -1)

clientChannel.close();

else

key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);

}

if (key.isValid() && key.isWritable()) {

ByteBuffer buffer = (ByteBuffer) key.attachment();

buffer.flip();

SocketChannel clientChannel = (SocketChannel) key.channel();

clientChannel.write(buffer);

// No left, no longer interest in write

if (!buffer.hasRemaining())

key.interestOps(SelectionKey.OP_READ);

buffer.compact();

}

keyIter.remove();

}

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值