六狼论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博账号登陆

只需一步,快速开始

搜索
查看: 36|回复: 0

java NIO 学习 聊天室程序 (2)

[复制链接]

升级  28%

28

主题

28

主题

28

主题

秀才

Rank: 2

积分
92
 楼主| 发表于 2013-2-3 14:14:28 | 显示全部楼层 |阅读模式
有了Command 接口,我们可以定义一些命令:
这些命令也代表了一些基本的需求

    1. @pub 向聊天室中发广播消息
    2. @quit 退出聊天室   , 如果断线,聊天室可以自动将与客户端的连接断开
    3. @listmember  查看聊天室中的所有人
    4. @login  username password  以用户名和密码登陆服务器
    5. @regist  username password 注册用户
    6. @usr  username|socket address 向特定用户发消息


有了以上需求的话,我们就需要定义一些数据结构:
首先定义会员类:
package com.tcl.chat;import java.lang.Thread.State;/** * 会员类 *  * @author tmdpzc *  */public class Member {private String mUsername;private String mPassword;private MemberState mState;/** * 定义会员的状态 * @author tmdpzc * */static class MemberState {public static final int OFFLINE = 0;public static final int UNLOGIN = 1;public static final int ONLINE = 2;public int state = 0;public MemberState() {// do nothing}public void setState(int state) {this.state = state;}}public boolean login(String username, String password) {if (this.mUsername.equals(username) && this.mPassword.equals(password)) {online();return true;}return false;}public void connected(){mState.setState(MemberState.UNLOGIN);}private void online() {mState.setState(MemberState.ONLINE);}public void offline() {mState.setState(MemberState.OFFLINE);}public Member(String username, String password) {super();this.mUsername = username;this.mPassword = password;this.mState = new MemberState();}public String getUsername() {return mUsername;}public void setUsername(String username) {this.mUsername = username;}public String getPassword() {return mPassword;}public void setPassword(String password) {this.mPassword = password;}}

定义一个存储会员的接口,可以理解为这是一个key-value 的数据库:
package com.tcl.chat;public interface MemberStorage {public boolean register(Member m);public boolean has(String m);public Member get(String name);}

既然是简单的NIO 聊天室,就使用一个Map来存储Member的数据好了
一个实现MemberStorage接口的类
package com.tcl.chat.impl;import java.util.HashMap;import com.tcl.chat.Member;import com.tcl.chat.MemberStorage;public class MemberStorageImpl implements MemberStorage{private HashMap<String, Member> mStorage;public  MemberStorageImpl(int size){mStorage = new HashMap<String, Member>(size);}@Overridepublic boolean register(Member m) {if (has(m.getUsername())) {return false;}else {mStorage.put(m.getUsername(), m);}return true;}@Overridepublic boolean has(String name) {Member m = mStorage.get(name);if (m == null) {return false;}else {return true;}}@Overridepublic Member get(String name) {return mStorage.get(name);}}
在ChatServer类中有这么几个Map:
Map<String, SocketChannel> mChannelMap;//socket address 到 SocketChannel的映射Map<String, Member>  mLoginMemberMap; //socket address  到 member的映射;MemberStorage mMemberStorage; //所有会员的记录;
这些基础设施建立好了,后面就可以实现我们的命令了,也就是具体的功能。
首先是PubCmd:
package com.tcl.chat.command;import java.io.IOException;import java.nio.channels.SelectionKey;import java.nio.channels.SocketChannel;import java.util.Iterator;import java.util.Set;import com.tcl.chat.ChatServer;public class PubCmd extends Command {@Overridepublic void handleCmd(SelectionKey key, ChatServer server, String[] args)throws IOException {SocketChannel sc = (SocketChannel) key.channel();StringBuilder sb = new StringBuilder();if (args.length <= 1) {return;} else {for (int i = 1; i < args.length; i++) {sb.append(args);}}String pub_msg = sc.socket().getRemoteSocketAddress() + ": "+ sb.toString();System.out.println(pub_msg);Iterator<SelectionKey> it = key.selector().keys().iterator();while (it.hasNext()) {SelectionKey sKey = (SelectionKey) it.next();if ((sKey != key) && (sKey != server.getServerKey())) {String address = ((SocketChannel) sKey.channel()).socket().getRemoteSocketAddress().toString();if (server.getLoginMemberMap().get(address) == null) {return;// 未登陆成功的终端看不到消息}if (sKey.attachment() == null) {sKey.attach(pub_msg);} else {String at = (String) sKey.attachment();sKey.attach(at + pub_msg);}sKey.interestOps(sKey.interestOps() | SelectionKey.OP_WRITE);}}}}
再来RegistCmd
package com.tcl.chat.command;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.SocketChannel;import com.tcl.chat.ChatServer;import com.tcl.chat.Member;import com.tcl.chat.utils.LogUtil;public class RegistCmd extends Command {private static ByteBuffer ERROR = ByteBuffer.wrap("Error regist username and password".getBytes());private static ByteBuffer SUCCESS = ByteBuffer.wrap("Regist success".getBytes());@Overridepublic void handleCmd(SelectionKey key, ChatServer server, String[] args)throws IOException {// TODO Auto-generated method stubLogUtil.i("Regist CMD :");SocketChannel sc = (SocketChannel) key.channel();LogUtil.e("args length " + args.length);if (args.length == 3) {String username = args[1];String password = args[2];Member mb = new Member(username, password);boolean flag = server.getMemberStorage().register(mb);if (flag) {sc.write(SUCCESS);SUCCESS.flip();return;}}sc.write(ERROR);ERROR.flip();//要注意,ByteBuffer要 调用filp才能在第二次的时候重新发送return;}}
您需要登录后才可以回帖 登录 | 立即注册 新浪微博账号登陆

本版积分规则

快速回复 返回顶部 返回列表