网络编程 (5)

来源:互联网 发布:js中给div赋值 编辑:程序博客网 时间:2024/06/04 19:24

使用Java NIO提高服务端程序的性能

     在前面的章节里,我们讨论了Java NIO的基本概念,在这一节里,我们将结合具体的Java Socket编程,讨论使用NIO提高服务端程序的性能的问题。 

    Java NIO增加了新的SocketChannel、ServerSocketChannel等类来提供对构建高性能的服务端程序的支持。SocketChannel、ServerSocketChannel能够在非阻塞的模式下工作,它们都是selectable的类。在构建服务器或者中间件时,推荐使用Java NIO。 

    在传统的网络编程中,我们通常使用一个专用线程(Thread)来处理一个Socket连接,通过使用NIO,一个或者很少几个Socket线程就可以处理成千上万个活动的Socket连接。 

    通常情况下,通过ServerSocketChannel.open()获得一个ServerSocketChannel的实例,通过SocketChannel.open或者serverSocketChannel.accept()获得一个SocketChannel实例。要使ServerSocketChannel或者SocketChannel在非阻塞的模式下操作,可以调用

    serverSocketChannel.configureBlocking (false);

    或者

    socketChannel.configureBlocking (false); 

    语句来达到目的。通常情况下,服务端可以使用非阻塞的ServerSocketChannel,这样,服务端的程序就可以更容易地同时处理多个socket线程。 

    下面我们来看一个综合例子,这个例子使用了ServerSocketChannel、SocketChannel开发了一个非阻塞的、能处理多线程的Echo服务端程序,见示例12-14。

    【程序源代码】 

1 // ==================== Program Discription =====================

2 // 程序名称:示例12-14 : SocketChannelDemo.java

3 // 程序目的:学习Java NIO#SocketChannel

4 // ==============================================================

5

6

7 import java.nio.ByteBuffer;

8 import java.nio.channels.ServerSocketChannel;

9 import java.nio.channels.SocketChannel;

10 import java.nio.channels.Selector;

11 import java.nio.channels.SelectionKey;

12 import java.nio.channels.SelectableChannel;

13

14 import java.net.Socket;

15 import java.net.ServerSocket;

16 import java.net.InetSocketAddress;

17 import java.util.Iterator;

18

19 public class SocketChannelDemo


20

21 {

22 public static int PORT_NUMBER = 23;//监听端口

23 ServerSocketChannel serverChannel;

24 ServerSocket serverSocket ;

25 Selector selector ;

26 private ByteBuffer buffer = ByteBuffer.allocateDirect (1024);

27

28 public static void main (String [] args)

29 throws Exception

30 {

31 SocketChannelDemo server=new SocketChannelDemo();

32 server.init(args);

33 server.startWork();

34 }

35

36

37 public void init (String [] argv)throws Exception

38 {

39 int port = PORT_NUMBER;

40

41 if (argv.length > 0) {

42 port = Integer.parseInt (argv [0]);

43 }

44

45 System.out.println ("Listening on port " + port);

46

47 // 分配一个ServerSocketChannel

48 serverChannel = ServerSocketChannel.open();

49 // 从ServerSocketChannel里获得一个对应的Socket

50 serverSocket = serverChannel.socket();

51 // 生成一个Selector

52 selector = Selector.open();

53

54 // 把Socket绑定到端口上

55 serverSocket.bind (new InetSocketAddress (port));

56 //serverChannel为非bolck

57 serverChannel.configureBlocking (false);

58

59 // 通过Selector注册ServerSocetChannel

60 serverChannel.register (selector, SelectionKey.OP_ACCEPT);

61

62 }

63

64    public void startWork()throws Exception


65

66    {

67     while (true) {

68

69 int n = selector.select();//获得IO准备就绪的channel数量

70

71 if (n == 0) {

72 continue; // 没有channel准备就绪,继续执行

73 }

74

75 // 用一个iterator返回Selector的selectedkeys

76 Iterator it = selector.selectedKeys().iterator();

77

78 // 处理每一个SelectionKey

79 while (it.hasNext()) {

80 SelectionKey key = (SelectionKey) it.next();

81

82 // 判断是否有新的连接到达

83 if (key.isAcceptable()) {

84           //返回SelectionKey的ServerSocketChannel

85 ServerSocketChannel server =

(ServerSocketChannel) key.channel();

86 SocketChannel channel = server.accept();

87

88 registerChannel (selector, channel,

89 SelectionKey.OP_READ);

90

91 doWork (channel);

92 }

93

94 // 判断是否有数据在此channel里需要读取

95 if (key.isReadable()) {

96   

97 processData (key);

98

99 }

100

101 //删除 selectedkeys

102 it.remove();

103 }

104 }

105 }

106 protected void registerChannel (Selector selector,

107 SelectableChannel channel, int ops)

108 throws Exception

109 {


110 if (channel == null) {

111 return;

112 }

113

114

115 channel.configureBlocking (false);

116

117 channel.register (selector, ops);

118 }

119

120    //处理接收的数据

121 protected void processData (SelectionKey key)

122 throws Exception

123 {

124

125

126 SocketChannel socketChannel = (SocketChannel) key.channel();

127 int count;

128

129 buffer.clear(); // 清空buffer

130

131 // 读取所有的数据

132 while ((count = socketChannel.read (buffer)) > 0) {

133 buffer.flip();

134

135 // send the data, don't assume it goes all at once

136 while (buffer.hasRemaining())

137 {

138 //如果收到回车键,则在返回的字符前增加[echo]$字样

139 if(buffer.get()==(char)13)

140 {

141 buffer.clear();

142 buffer.put("[echo]$".getBytes());

143 buffer.flip();

144

145 }

146 socketChannel.write (buffer);//在Socket里写数据

147 }

148

149 buffer.clear(); // 清空buffer

150 }

151

152 if (count < 0) {

153 // count<0,说明已经读取完毕

154 socketChannel.close();


155 }

156 }

157

158

159 private void doWork (SocketChannel channel)throws Exception

160 {

161 buffer.clear();

162 buffer.put ("

Hello,I am working,please input some thing,and i will echo to you!\r\n[echo] 

$".getBytes());

163 buffer.flip();

164 channel.write (buffer);

165 }

166

167 }

    使用:运行此程序,然后在控制台输入命令telnet localhost 23。 

    【程序输出结果】如图12-1所示。 

图12-1 输出结果


    【程序注解】

    关于程序的解释已经包含在程序里面了,在这里我们总结以下使用ServerSocket Channel开发服务端程序的过程:

    (1)分配一个ServerSocketChannel。

    (2)从ServerSocketChannel里获得一个对应的ServerSocket。

    (3)生成一个Selector实例。

    (4)把ServerSocket绑定到端口上。

    (5)设置ServerSocketChannel为非block模式(可选)。

    (6)在Selector里注册ServerSocetChannel。

    (7)用一个无限循环语句始终查看Selector里是否有IO准备就绪的channel。如果有,就执行对应的处理,如果没有,继续循环。 

     小 结

     在本章我们主要介绍了Java中的网络编程。Java一开始就是一种网络编程语言,到后来才应用到各个方面,所以在Java中进行网络编程远比在C/C++中方便。

0 0
原创粉丝点击