Step By Step(Java 网络篇) (七)

2014-11-24 03:03:08 · 作者: · 浏览: 2
(long) (work[6] & 0xff) << 48 | (long) (work[5] & 0xff) << 40 | (long) (work[4] & 0xff) << 32
52 | (long) (work[3] & 0xff) << 24 | (long) (work[2] & 0xff) << 16
53 | (long) (work[1] & 0xff) << 8 | (long) (work[0] & 0xff);
54 }
55 public final short readShort() throws IOException {
56 din.readFully(work, 0, 2);
57 return (short) ((work[1] & 0xff) << 8 | (work[0] & 0xff));
58 }
59 public final String readUTF() throws IOException {
60 return din.readUTF();
61 }
62 public final int readUnsignedByte() throws IOException {
63 return din.readUnsignedByte();
64 }
65 public final int readUnsignedShort() throws IOException {
66 din.readFully(work, 0, 2);
67 return ((work[1] & 0xff) << 8 | (work[0] & 0xff));
68 }
69 public final int skipBytes(int n) throws IOException {
70 return din.skipBytes(n);
71 }
72 }
5. NIO:
为什么要使用nio呢?基本的Java套接字对于小规模系统可以很好的运行,但当涉及同时处理上千个客户端的服务器时,可能会产生一些问题。由于创建、维护和切换线程需要的系统开销,一客户一线程方式在系统扩展性方面受到了限制。使用线程池可以节省那种系统开销,同时允许实现者利用并行硬件的优势。nio中提供的Selector和Channel抽象对象为我们提供了一次轮询一组客户端的方法,从而避免了我们不得不主动轮询每个客户端并获悉是否有新的数据到来。在nio中还提供了一组Buffer类,如ByteBuffer、IntBuffer等,和普通io中的Stream对象相比,nio中的Buffer对象提供了更高的效率和可预测的I/O。Stream抽象好的方面是隐藏了底层缓冲区的有限性,提供了一个能够容纳任意长度的容器的假象。坏的方面是要实现这样一个假象,要么会产生大量的内存开销,要么会引入大量的上下文切换,甚至可能两者都有。在使用线程时,这些开销都隐藏在了具体实现中,因此也失去了对其可控性和可预测性。这种方法让编程变得容易,但要调整他们的性能则变得更困难。不幸的是,如果要使用Java的Socket对象,流是唯一的选择。下面将给出一个利用Channel完成非阻塞操作的例子:
1 public class MyTest {
2 public static void main(String[] args) throws IOException {
3 String server = "10.1.24.199";
4 int servPort = 5050;
5 //创建一个客户端SocketChannel,并将其设置为非阻塞方式。
6 SocketChannel clntChannel = SocketChannel.open();
7 clntChannel.configureBlocking(false);
8 //尝试连接服务器,由于当前的SocketChannel是非阻塞方式的,因此finishConnect()方法
9 //在成功连接之前将会立即返回false,知道成功连接后在返回true。如果在这个过程中出现
10 //任何网络错误,finishConnect()方法将会抛出IOException的异常。对于阻塞模式下的
11 //SocketChannel,finishConnect()在没有成功连接之前将不会立即返回,而是阻塞等待连接
12 //成功,或在出现网络错误时抛出异常。
13 if (!clntChannel.connect(new InetSocketAddress(server, servPort))) {
14 while (!clntChannel.finishConnect()) {
15 //TODO: 做一些其他的事情,以避免这种忙等待消耗更多的系统资源。
16 //即使是调用Thread.sleep(0);也可以降低这种忙等待产生的消耗。
17 System.out.print(".");
18 }
19 }
20 byte[] demoData = "hello world".getBytes();
21 //构建ByteBuffer的两种常用方式,一种是包装已经存在的数组对象,
22 //再有就是通过allocate静态方法直接分配内存。
23 ByteBuffer writeBuf = ByteBuffer.wrap(demoData);
24 ByteBuffer readBuf = ByteBuffer.allocate(demoData.length);
25 int totalBytesRecv = 0;
26 int bytesRecv;
27 while (totalBytesRecv < demoData.length) {
28 //由于该SocketChannel是非阻塞模式,因为write和read调用均会立即返回。
29 //如果此时没有数据可读,read将返回0,只有在出现网络异常的情况下,read
30 //才会返回-1。
31 if (writeBuf.hasRemaining())
32 clntChannel.write(writeBuf);
33 if ((bytesRecv = clntChannel.read(readBuf)) == -1)
34 throw new SocketException("Connection Closed Prematurely"