Java NIO Socket通信_亮仔_新浪博客

本文介绍如何使用Java NIO实现服务器与客户端的非阻塞模式通信,涵盖ServerSocketChannel与SocketChannel的配置及Selector的使用方法。

http://www.open-open.com/lib/view/open1343002247880.html

服务器端的代码如下,相关说明就带在注释里了:

// 创建一个选择器,可用close()关闭,isOpen()表示是否处于打开状态,他不隶属于当前线程

02 Selector selector = Selector.open();

03 // 创建ServerSocketChannel,并把它绑定到指定端口上

04 ServerSocketChannel server = ServerSocketChannel.open();

05 server.bind(new InetSocketAddress("127.0.0.1", 7777));

06 // 设置为非阻塞模式, 这个非常重要

07 server.configureBlocking(false);

08 // 在选择器里面注册关注这个服务器套接字通道的accept事件

09 // ServerSocketChannel只有OP_ACCEPT可用,OP_CONNECT,OP_READ,OP_WRITE用于SocketChannel

10 server.register(selector, SelectionKey.OP_ACCEPT);

11 while (true) {

12     // 测试等待事件发生,分为直接返回的selectNow()和阻塞等待的select(),另外也可加一个参数表示阻塞超时

13     // 停止阻塞的方法有两种: 中断线程和selector.wakeup(),有事件发生时,会自动的wakeup()

14     // 方法返回为select出的事件数(参见后面的注释有说明这个值为什么可能为0).

15     // 另外务必注意一个问题是,当selector被select()阻塞时,其他的线程调用同一个selector的register也会被阻塞到select返回为止

16     // select操作会把发生关注事件的Key加入到selectionKeys中(只管加不管减)

17     if (selector.select() == 0) { //

18         continue;

19     }

20  

21     // 获取发生了关注时间的Key集合,每个SelectionKey对应了注册的一个通道

22     Set keys = selector.selectedKeys();

23     // 多说一句selector.keys()返回所有的SelectionKey(包括没有发生事件的)

24     for (SelectionKey key : keys) {

25         // OP_ACCEPT 这个只有ServerSocketChannel才有可能触发

26         if (key.isAcceptable()) {

27             // 得到与客户端的套接字通道

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

29             // 同样设置为非阻塞模式

30             channel.configureBlocking(false);

31             // 同样将于客户端的通道在selector上注册,OP_READ对应可读事件(对方有写入数据),可以通过key获取关联的选择器

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

33         }

34         // OP_READ 有数据可读

35         if (key.isReadable()) {

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

37             // 得到附件,就是上面SocketChannel进行register的时候的第三个参数,可为随意Object

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

39             // 读数据 这里就简单写一下,实际上应该还是循环读取到读不出来为止的

40             channel.read(buffer);

41             // 改变自身关注事件,可以用位或操作|组合时间

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

43         }

44         // OP_WRITE 可写状态 这个状态通常总是触发的,所以只在需要写操作时才进行关注

45         if (key.isWritable()) {

46             // 写数据掠过,可以自建buffer,也可用附件对象(看情况),注意buffer写入后需要flip

47             // ......

48             // 写完就吧写状态关注去掉,否则会一直触发写事件

49             key.interestOps(SelectionKey.OP_READ);

50         }

52         // 由于select操作只管对selectedKeys进行添加,所以key处理后我们需要从里面把key去掉

53         keys.remove(key);

54     }

55 }


    还有一个状态上面没有使用,OP_CONNECT这个主要是用于客户端,对应的key的方法是isConnectable()表示已经创建好了连接。

非阻塞实现的客户端如下:

01 Selector selector = Selector.open();

02 // 创建一个套接字通道,注意这里必须使用无参形式

03 SocketChannel channel = SocketChannel.open();

04 // 设置为非阻塞模式,这个方法必须在实际连接之前调用(所以open的时候不能提供服务器地址,否则会自动连接)

05 channel.configureBlocking(false);

06 // 连接服务器,由于是非阻塞模式,这个方法会发起连接请求,并直接返回false(阻塞模式是一直等到链接成功并返回是否成功)

07 channel.connect(new InetSocketAddress("127.0.0.1", 7777));

08 // 注册关联链接状态

09 channel.register(selector, SelectionKey.OP_CONNECT);

10 while (true) {

11     // 前略 和服务器端的类似

12     // ...

13     // 获取发生了关注时间的Key集合,每个SelectionKey对应了注册的一个通道

14     Set keys = selector.selectedKeys();

15     for (SelectionKey key : keys) {

16         // OP_CONNECT 两种情况,链接成功或失败这个方法都会返回true

17         if (key.isConnectable()) {

18             // 由于非阻塞模式,connect只管发起连接请求,finishConnect()方法会阻塞到链接结束并返回是否成功

19             // 另外还有一个isConnectionPending()返回的是是否处于正在连接状态(还在三次握手中)

20             if (channel.finishConnect()) {

21                 // 链接成功了可以做一些自己的处理,略

22                 // ...

23                 // 处理完后必须吧OP_CONNECT关注去掉,改为关注OP_READ

24                 key.interestOps(SelectionKey.OP_READ);

25             }

26         }

27         // 后略 和服务器端的类似

28         // ...

29     }

30 }

 

    虽然例子是这样的,不过服务器和客户端可以自己单方面选择是否采用非阻塞模式,用阻塞模式的客户端连接非阻塞模式的服务器端是OK的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值