JAVA NIO 服务器(二)

开发前的准备:

在写NIO 服务器前,需要掌握IO的原理,以及那几个常用对象的关系,这样或许才能做到代码在心中,就算是出了问题也可以自己调试解决。

 ByteBuffer 与Channel:在读操作的时候,channel会把数据读到ByteBuffer里。在写操作的时候,channel会把ByteBuffer发送给客户端。

Channel与Selector:一个Selector可以管理多个Channel,一个Channel可以注册到多个Selector.Selector用来管理Channel的4种操作.

SelectionKey:选择键封装了特定的通道与特定的选择器的注册关系。

服务代码实例:

模仿了玩家登录,向服务器发送请求包,以及服务向端客户端返回请求结果.如标题一样,这是很简单的一个demo,还有很大地方封装不够,只是介绍一下NIO服务器怎么样的,暂时没有实际的作用,这些没完成的功能将在下一篇写出。


服务器代码:

package packge1;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.logging.Logger;

/**
 *
 * @author Jack Lei
 * @see readme.txt
 * @date 2016年1月17日 下午2:47:11
 */
public class Server {
	private final Logger		logger	= Logger.getLogger(Server.class.getName());
	private ServerSocketChannel ssc;
	private Selector			selector;

	public Server(String host, int port) {
		try {
			ssc = ServerSocketChannel.open();
			ssc.socket().bind(new InetSocketAddress(host, port));
			ssc.configureBlocking(false);// 设置为非阻塞
			selector = Selector.open();
			ssc.register(selector, SelectionKey.OP_ACCEPT);// 注册选择器,并注册接受连接的键
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void start() throws IOException{
		logger.info("######## Server Start ########");
		while(true){
			int select = selector.select();//获取已准备好的键
			if (select > 0) {
				Iterator<SelectionKey> iterator = selector.selectedKeys()
															.iterator();// 此选择器的已选择键集
				while (iterator.hasNext()) {
					SelectionKey key = iterator.next();
					if (key.isAcceptable()) {
						ServerSocketChannel ssc = (ServerSocketChannel) key.channel();// 这么做是安全的,因为只有ServerSocketChannel才支持ACCEPT
						onConnect(ssc.accept());
					} else if (key.isReadable()) {
						onReadDataFromSocket(key);
					}
					iterator.remove();
				}
			}
		}
	}

	public void onConnect(SocketChannel socket) {
		try {
			socket.configureBlocking(false);
			socket.register(selector,
							SelectionKey.OP_READ | SelectionKey.OP_WRITE);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void onReadDataFromSocket(SelectionKey key) {
		SocketChannel socket = (SocketChannel) key.channel();
		ByteBuffer bb = ByteBuffer.allocate(1024);
		try {
			int readCnt = socket.read(bb);
			if (readCnt > 0) {
				bb.flip();// 准备取数据
				short packetId = bb.getShort();
				int size = bb.getInt();
				byte[] body = new byte[size];
				bb.get(body);
				logger.info("recive data from client packetId = " + packetId + " size = " + size + "msg = " + new String(body));

				if (packetId == 1001) {
					String msg = "登录服务器成功";
					byte[] bytes = msg.getBytes();
					// 数据包= 包类型+包长度+包内容。
					ByteBuffer bf = ByteBuffer.allocate(Short.SIZE / 8 + Integer.SIZE / 8 + bytes.length);
					bf.putShort((short) 1002);// 假设1001是登录返回的包
					bf.putInt(bytes.length);// 这个包的大小,让客户端知道读到哪个位置结束
					bf.put(bytes);// 写入包的内容
					bf.flip();
					socket.write(bf);
				}

			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void main(String args[]) {
		try {
			new Server("127.0.0.1", 7777).start();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}


客户端代码:

package packge1;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.logging.Logger;

/**
 *
 * @author Jack Lei
 * @see readme.txt
 * @date 2016年1月17日 下午4:27:58
 */
public class Client {
	private Logger			logger	= Logger.getLogger(Client.class.getName());
	private SocketChannel	sc;
	private Selector		selector;
	private static boolean	login	= false;
	public Client(String host, int port) {
		try {
			sc = SocketChannel.open();
			selector = Selector.open();
			sc.configureBlocking(false);
			sc.register(selector,
						SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE);
			sc.connect(new InetSocketAddress(host, port));


		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void start() throws IOException {
		while (true) {
			int selectCnt = selector.select();
			if (selectCnt > 0) {
				Iterator<SelectionKey> iterator = selector.selectedKeys()
															.iterator();
				while (iterator.hasNext()) {
					SelectionKey key = iterator.next();
					if (key.isConnectable()) {
						SocketChannel socket = (SocketChannel) key.channel();
						socket.finishConnect();// 完成连接,不完成会一直执行这个操作
						login = true;
						logger.info("finish connect..");
					} else if (key.isReadable()) {
						SocketChannel socket = (SocketChannel) key.channel();
						ByteBuffer bb = ByteBuffer.allocate(1024);
						int readCnt = socket.read(bb);
						if (readCnt > 0) {
							bb.flip();
							short packetId = bb.getShort();
							int size = bb.getInt();
							byte[] body = new byte[size];
							bb.get(body);
							logger.info("recive server data packetId = " + packetId + " size = " + size + " body = " + new String(body));
						}

					} else if (key.isWritable()) {
						if (login) {
							SocketChannel socket = (SocketChannel) key.channel();
							// 向服务端发起请求
							ByteBuffer bb = ByteBuffer.allocate(1024);
							String msg = "请求连接";
							bb.putShort((short) 1001);
							byte[] bytes = msg.getBytes();
							bb.putInt(bytes.length);
							bb.put(bytes);
							bb.flip();
							socket.write(bb);
							login = false;
						}
					}
					iterator.remove();
				}
			}
		}
	}

	public static void main(String args[]) throws IOException {
		new Client("127.0.0.1", 7777).start();
	}
}

控制台信息:server/client

一月 17, 2016 10:38:51 下午 packge1.Server start
信息: ######## Server Start ########
一月 17, 2016 10:38:56 下午 packge1.Server onReadDataFromSocket
信息: recive data from client packetId = 1001 size = 8 msg = 请求连接


一月 17, 2016 10:38:56 下午 packge1.Client start
信息: finish connect..
一月 17, 2016 10:38:56 下午 packge1.Client start
信息: recive server data packetId = 1002 size = 14 body = 登录服务器成功


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值