nio client和netty server實例

花了一週時間,研究了java裏面的nio和netty,其實nio很好理解,用過c語言的,都應該知道select和epoll,nio和select和epoll非常類似,使用方法和解決的問題也都是一樣的。

至於netty,不得不欽佩java語言的框架技術,雖說這個框架研究起來非常費勁,但是對於上層使用者,使用這個netty框架,會幫我們解決很多性能、穩定性問題。同時,使用框架,也會大大提高開發效率。

這裏,不想講太多關於nio和netty的東西,所有最基本的知識點,都在如下學習資料中。目前我對這個netty框架研究的還不深入,想了半天其實真寫不出啥有水平的文章,待今後深入研究後,將學習成果再和大家彙報。

學習資料:

NIO:http://www.iteye.com/magazines/132-Java-NIO#590

Netty:http://docs.jboss.org/netty/3.1/guide/html_single/

多線程:http://www.cnblogs.com/dolphin0520/p/3932921.html

這裏給出一個用nio實現的tcp client、用netty實現的一個tcp server的例子。

處理過程爲:client傳遞a、b兩個整型數,server計算和,將結果返回給客戶端。在服務端加入線程池,用來處理兩個數的和。當然了從性能角度,目前這麼簡單的操作完全沒有必要這樣做。主要考慮到如果有更復雜的操作,一般的服務端的模型都是將任務傳入一個消息隊列,後端再用線程池從消息隊列中取出任務進行處理,再返回處理結果。所以這個地方的線程池,可以認爲是以後的擴展,也可以認爲其就是個擺設。

client 代碼

NioClient.java

package nio.client.test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class NioClient {
	private final static int MAX_BUF_SIZE 					= 1024;
	private InetSocketAddress serverAddr;
	private int clientCount;
	
	public NioClient(String ip, int port, int clientCount) {
		this.clientCount 	= clientCount;
		this.serverAddr 	= new InetSocketAddress(ip, port);
	}
	
	private void sendMessageToSrv(SocketChannel sockChnl, int clientNo, int index) throws IOException {
		// send data to server...
/*		ByteBuffer sendBuf = ByteBuffer.allocate(MAX_BUF_SIZE);
		String sendText = "Client " + clientNo + " say " + index + "\r\n";
		sendBuf.put(sendText.getBytes());
		sendBuf.flip();
		sockChnl.write(sendBuf);
		System.out.println(sendText);*/
		
		ByteBuffer sendBuf = ByteBuffer.allocate(4*4);
		sendBuf.putInt(clientNo);
		sendBuf.putInt(index);
		sendBuf.putInt(clientNo);
		sendBuf.putInt(index);
		sendBuf.flip();
		
		sockChnl.write(sendBuf);
		String out = String.format("client: %d send message, index: %d, a: %d, b: %d", clientNo, index, clientNo, index);
		System.out.println(out);
	}
	
	private void recvMessage(SocketChannel sockChnl, int clientNo) throws IOException {
		/*ByteBuffer recvBuf = ByteBuffer.allocate(MAX_BUF_SIZE);
		int bytesRead = sockChnl.read(recvBuf);
		while (bytesRead > 0) {
			recvBuf.flip(); // write mode to read mode, position to 0, // limit to position
			String recvText = new String(recvBuf.array(), 0, bytesRead);
			recvBuf.clear(); // clear buffer content, read mode to write mode, position to 0, limit to capacity
			System.out.println("Client " + clientNo + " receive: " + recvText);
			bytesRead = sockChnl.read(recvBuf);
		}*/
		
		ByteBuffer recvBuf = ByteBuffer.allocate(MAX_BUF_SIZE);
		int bytesRead = sockChnl.read(recvBuf);
		while (bytesRead > 0) {
			recvBuf.flip(); // write mode to read mode, position to 0, // limit to position
			int result = recvBuf.getInt();
			recvBuf.clear(); // clear buffer content, read mode to write mode, position to 0, limit to capacity
			String out = String.format("client: %d recv message, result: %d", clientNo, result);
			System.out.println(out);
			bytesRead = sockChnl.read(recvBuf);
		}
	}

	public void startNioClient() throws IOException, InterruptedException {
		Selector selector = Selector.open();

		for (int i = 0; i < clientCount; i++) {
			SocketChannel socketChannel = SocketChannel.open();
			socketChannel.configureBlocking(false);
			Map<String, Integer> clientInfo = new HashMap<String, Integer>();
			clientInfo.put("no", i);
			clientInfo.put("index", 0);
			socketChannel.register(selector, SelectionKey.OP_CONNECT, clientInfo);
			socketChannel.connect(this.serverAddr);
		}

		while (true) {
			int readyChannels = selector.select();
			if (0 == readyChannels) {
				continue;
			}

			Set<SelectionKey> selectionKeys = selector.selectedKeys();
			for (SelectionKey sk : selectionKeys) {
				Map clientInfo = (Map) sk.attachment(); 
				int clientNo = (Integer) clientInfo.get("no");
				SocketChannel socketchnl = (SocketChannel) sk.channel();
				
				if (sk.isConnectable()) {
					while(!socketchnl.finishConnect()) {
						Thread.sleep(5);
					}
					if (socketchnl.isConnected()) {
                        System.out.println("connect is finish...");
                        // send data to server...
    					sendMessageToSrv(socketchnl, clientNo, -1);
    					sk.interestOps(SelectionKey.OP_READ);
					}
				} else if (sk.isReadable()) {
					// read data from server...
					recvMessage(socketchnl, clientNo);
					
					// send data to server...
					int index = (Integer) clientInfo.get("index");
					index += 1;
					sendMessageToSrv(socketchnl, clientNo, index);		
					clientInfo.put("index", index);
				}
			}
			selectionKeys.clear();
		}
	}

	public int getClientCount() {
		return clientCount;
	}

	public void setClientCount(int clientCount) {
		this.clientCount = clientCount;
	}
}

Main.java

package nio.client.test;

import java.io.IOException;

public class Main {
	public static void main(String[] args) {
		System.out.println("clients start..............");
		NioClient client = new NioClient("127.0.0.1", 8080, 5000);
		try {
			client.startNioClient();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

server端代碼:

NettyServer.java

package com.bj58.nettyTest;

import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.ChannelGroupFuture;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;

public class NettyServer {

	static final ChannelGroup allChannels = new DefaultChannelGroup("time-server");
	
	public static void main(String[] args) throws Exception {
	    ExecutorService threadPool = Executors.newFixedThreadPool(5);
	    
		ChannelFactory factory = new NioServerSocketChannelFactory(
				Executors.newCachedThreadPool(),
				Executors.newCachedThreadPool());

		ServerBootstrap bootstrap = new ServerBootstrap(factory);

		bootstrap.setPipelineFactory(new ServerPipelineFactory(threadPool));
		
		bootstrap.setOption("child.tcpNoDelay", true);
		bootstrap.setOption("child.keepAlive", true);

		System.out.println("Netty Server start...");
		Channel channel = bootstrap.bind(new InetSocketAddress(8080));
		
/*		allChannels.add(channel);
		System.out.println("1111111111111111111");
		Thread.sleep(2*60*1000);
		System.out.println("2222222222222222222");
        ChannelGroupFuture future = allChannels.close();
        future.awaitUninterruptibly();
        factory.releaseExternalResources();
        System.out.println("3333333333333333333");*/
		
	}
}

ServerDecoder.java

package com.bj58.nettyTest;

import java.util.ArrayList;
import java.util.List;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;

public class ServerDecoder extends FrameDecoder{
    @Override
    protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
        if (buffer.readableBytes() < 8) {
            return null;
        }
        
        int clientNo = buffer.readInt();
        int index = buffer.readInt();
        int a = buffer.readInt();
        int b = buffer.readInt();

        List<Integer> data = new ArrayList<Integer>();
        data.add(clientNo);
        data.add(index);
        data.add(a);
        data.add(b);
        
        return data;
    }
}

ServerHandler.java

package com.bj58.nettyTest;

import java.util.List;
import java.util.concurrent.ExecutorService;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;



public class ServerHandler extends SimpleChannelHandler {

    private ExecutorService threadPool;

    public ServerHandler(ExecutorService threadPool) {
        this.threadPool = threadPool;
    }
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws InterruptedException {
        /*
         * ChannelBuffer buf = (ChannelBuffer)e.getMessage(); byte[] des =
         * buf.array(); String recvText = new String(des, 0, des.length);
         * System.out.println(recvText); Channel ch = e.getChannel();
         * ch.write(e.getMessage());
         */
        
        List<Integer> data = (List<Integer>) e.getMessage();
        HandleTask ht = new HandleTask(e);
        threadPool.submit(ht);
        
        int clientNo = data.get(0);
        int index = data.get(1);
        int a = data.get(2);
        int b = data.get(3);

        String content = String.format("client: %d, index: %d, a: %d, b: %d", clientNo, index, a, b);
        System.out.println(content);
        
        Channel ch = e.getChannel();
        ChannelBuffer buf = ChannelBuffers.buffer(4);
        buf.writeInt(a+b);
        ch.write(buf);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
        e.getCause().printStackTrace();

        Channel ch = e.getChannel();
        ch.close();
    }
}

class HandleTask implements Runnable {
    MessageEvent e;
    
    public HandleTask(MessageEvent e) {
        this.e = e;
    }
    
    public void run() {
        List<Integer> data = (List<Integer>) e.getMessage();
        
        int clientNo = data.get(0);
        int index = data.get(1);
        int a = data.get(2);
        int b = data.get(3);

        String content = String.format("client: %d, index: %d, a: %d, b: %d", clientNo, index, a, b);
        System.out.println(content);
        
        Channel ch = e.getChannel();
        ChannelBuffer buf = ChannelBuffers.buffer(4);
        buf.writeInt(a+b);
        ch.write(buf);
    }
}

ServerPipelineFactory.java

package com.bj58.nettyTest;

import static org.jboss.netty.channel.Channels.pipeline;

import java.util.concurrent.ExecutorService;

import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;

public class ServerPipelineFactory implements ChannelPipelineFactory{   
    
    private ExecutorService threadPool;

    public ServerPipelineFactory(ExecutorService threadPool) {
        this.threadPool = threadPool;
    }
    
    public ChannelPipeline getPipeline() throws Exception {
        ChannelPipeline pipeline = pipeline();

        pipeline.addLast("framer", new ServerDecoder());
        pipeline.addLast("handler", new ServerHandler(threadPool));
        return pipeline;
    }
}


代碼理解:

客戶端可以同時啓動N個tcp client,同時連接一個tcp server,傳遞a/b兩個數,獲取a/b之和。

當代碼寫完之後,發現通過java的序列化技術,可以直接傳遞一個java 對象,這樣一來,發送和接收端,處理起來會更簡單一些,而且實際項目中,傳遞的數據要比這個複雜的多。

接下來研究一下java的序列化技術、netty如何傳遞對象、以及Google protobuf,給出一個完整的rpc的代碼例子。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章