【Java】NIO 客户端-服务器 聊天 例子

来源:互联网 发布:室内网络布线 编辑:程序博客网 时间:2024/05/18 00:39

想写一个简单的聊天C/S,client可以单聊和群发,代码里面有每一次发送的格式。

发现没写过NIO还真不习惯这种模式,总体感觉是不管哪一端,最终都有一个select的循环,然后循环里面会对每一个就绪的key处理,key的处理又是一种宏观的写法,read或者write的处理会包括每一个通道的处理,所以这些函数可能会有很多的case,写起来还是挺费劲的。但是基本还是遵循了服务器的请求-相应模式。就是最开始是一个accept事件,然后有了socket以后,就先注册一个read事件,这个read是用来获取客户端的请求的,然后一旦有了read事件,就需要在处理函数里解析客户端发来的请求,然后再注册一个wirte事件,这样在下一个周期,就会触发write事件,再根据上一次解析的内容把信息写入,最后再重新注册read事件。这是一次请求-响应的基本逻辑,只不过在read函数和write函数里写起来费劲,因为要照顾到所有的channel,感觉分开会更好把。

与传统的io不同点在于,传统io每一次读到客户端请求,就可以直接写内容回去,但是nio无法做到,因为同一个通道的读和写肯定是在两个周期或者两次while循环,这就涉及到了一个如何在第二次的write周期找到上一个read周期解析以后得到的要发送的内容,这个显然需要一个全局的list或者变量来存储,涉及到一些共享变量的东西。个人感觉用多线程做这个比较好把。慢慢研究。

服务端:

package server;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.ClosedChannelException;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.Iterator;import java.util.LinkedList;import java.util.List;import java.util.Map;import java.util.Map.Entry;public class Server {private Selector selector;private static final int PORT = 20001;Map<String, SelectionKey> map1;Map<SelectionKey, String> map2;List<Msg> msgs;public void connect(SelectionKey key){ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();System.out.println("log: client connects...");try {SocketChannel socketChannel = serverChannel.accept();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);} catch (ClosedChannelException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}public void read(SelectionKey key){SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);StringBuffer sb = new StringBuffer();int c = 0;try {while((c = socketChannel.read(buffer)) > 0){buffer.flip();int size = buffer.remaining();byte[] bytes = new byte[size];buffer.get(bytes, 0, size);sb.append(new String(bytes));}if(c == -1){System.out.println("log: client closes");socketChannel.close();return;}else{System.out.println("log: msg-> " + sb);handleMsg(sb.toString(), key);}} catch (IOException e) {e.printStackTrace();}}private void write(SelectionKey key){SocketChannel sc = (SocketChannel) key.channel();Iterator<Msg> itr = msgs.iterator();while(itr.hasNext()){Msg msg = itr.next();if(msg.getWho().equals("all") || msg.getWho().equals(map2.get(key))){ByteBuffer buffer = ByteBuffer.allocate(1024);String m = "[" + msg.getTime() + "]: " + msg.getMsg();buffer.put(m.getBytes());buffer.flip();try {sc.write(buffer);} catch (IOException e) {e.printStackTrace();}}}try {sc.register(selector, SelectionKey.OP_READ);} catch (ClosedChannelException e) {e.printStackTrace();}}//格式: cmd(who):msg//login:ly//send(all):hello//send(ly):helloprivate void handleMsg(String msg, SelectionKey key){String[] parse = msg.split(":");String cmd = parse[0];if(cmd.equals("login")){map1.put(parse[1], key);map2.put(key, parse[1]);}else if(cmd.startsWith("send")){Msg message = new Msg();DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String date = df.format(new Date());message.setTime(date);int start = cmd.indexOf('(');int end = cmd.indexOf(')');String who = cmd.substring(start + 1, end);message.setWho(who);message.setMsg(parse[1]);msgs.add(message);if(who.equals("all")){for(Entry<String, SelectionKey> entry : map1.entrySet()){SelectionKey sKey = entry.getValue();SocketChannel sc = (SocketChannel) sKey.channel();try {sc.register(selector, sKey.interestOps() | SelectionKey.OP_WRITE);} catch (ClosedChannelException e) {e.printStackTrace();}}}else{SelectionKey sKey = map1.get(who);SocketChannel sc = (SocketChannel) sKey.channel();try {sc.register(selector, sKey.interestOps() | SelectionKey.OP_WRITE);} catch (ClosedChannelException e) {e.printStackTrace();}}}else{//其余的不做处理,仍然监听read}}public void start(){map1 = new HashMap<>();map2 = new HashMap<>();msgs = new LinkedList<>();try {this.selector = Selector.open();ServerSocketChannel serverChannel = ServerSocketChannel.open();serverChannel.configureBlocking(false);serverChannel.bind(new InetSocketAddress(PORT));serverChannel.register(selector, SelectionKey.OP_ACCEPT, "server");System.out.println("log: sever starts...");boolean write = false;while(selector.select() > 0){Iterator<SelectionKey> itr = selector.selectedKeys().iterator();while(itr.hasNext()){SelectionKey key = itr.next();if(key.isAcceptable()){connect(key);}else if(key.isReadable()){read(key);}else if(key.isWritable()){write(key);write = true;}itr.remove();}if(write)msgs.clear();}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) {new Server().start();}}

Msg类:

package server;public class Msg {private String msg;private String who;private String time;public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public String getWho() {return who;}public void setWho(String who) {this.who = who;}public String getTime() {return time;}public void setTime(String time) {this.time = time;}}
客户端:

package client;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.Scanner;public class Client {private Selector selector;private Scanner sc = new Scanner(System.in);public static void main(String args[]){new Client().start();}private void start(){try {selector = Selector.open();SocketChannel socketChannel = SocketChannel.open();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_CONNECT);socketChannel.connect(new InetSocketAddress("localhost", 20001));while(true){selector.select();Iterator<SelectionKey> itr = selector.selectedKeys().iterator();while(itr.hasNext()){SelectionKey key = itr.next();SocketChannel channel = (SocketChannel) key.channel();if(key.isConnectable()){System.out.println("con");socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);socketChannel.finishConnect();  }else if(key.isReadable()){System.out.println("read");ByteBuffer buffer = ByteBuffer.allocate(1024);channel.read(buffer);String m = new String(buffer.array());System.out.println(m);}else{System.out.println("write");String m = sc.next();ByteBuffer buffer = ByteBuffer.allocate(1024);buffer.put(m.getBytes());buffer.flip();channel.write(buffer);}//itr.remove();}}} catch (IOException e) {e.printStackTrace();}}}
这次虽然写出来了,但是感觉还有好多地方需要理解和改进。


阅读全文
0 0
原创粉丝点击