【java网络编程】之四

使用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]___FCKpd___0quot;.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!
[echo] 
___FCKpd___0quot;.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++中方便。 

    我们介绍了几个在网络编程中很重要的类,如InetAddress、URL、URLConnection、Socket、 ServerSocket、DatagramSocket、DatagramPacket、MulticastSocket等。这些类包含了进行基本网络编程的所有内容。要熟练地应用这些类,关键还是要多多练习。 

    基于套接字的编程基本上是客户/服务器模式,我们具体介绍了编写这种模式的步骤。在实例方面,我们给出了一个基于TCP的套接字客户/服务器程序,与此相对应,还给出了基于UDP的客户/服务器程序。两者的模式是很相似的,其实这也就是编写客户/服务器程序的一般模式。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值