//client
public class NBChatClient {
private static final String CMD_QUIT = "/quit";
private Selector sel;
private SocketChannel socket;
private boolean feedback = false;
private boolean loginSeccess = false;
private boolean active = true;
private Object oLogin = new Object();
private ByteBuffer buf = ByteBuffer.allocate(128);
private static Properties props = new Properties();
NBChatClient(String fName) {
initConfig(fName);
initClient();
start();
}
private static void initConfig(String fName) {
try {
InputStream in = NBChatServer.class.getClassLoader().getResourceAsStream(fName);
props.load(in);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
private void initClient() {
String ipStr = props.getProperty(NBChatServer.key_ip);
String portStr = props.getProperty(NBChatServer.key_port);
try {
sel = Selector.open();
this.socket = SocketChannel.open();
InetAddress ip = InetAddress.getByName(ipStr);
InetSocketAddress remote = new InetSocketAddress(ip, Integer.parseInt(portStr));
this.socket.connect(remote);
this.socket.configureBlocking(NBChatServer.NON_BLOCKING);
socket.register(sel, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}
}
private void start() {
//create a new thread to read message from server.
new Thread() {
public void run() {
int readyCount = 0;
while (active) {
try {
readyCount = sel.select();
} catch (IOException e) {
if (sel.isOpen())
continue;
else
e.printStackTrace();
}
if (readyCount == 0)
continue;
Set readyKeys = sel.selectedKeys();
Iterator keys = readyKeys.iterator();
while (keys.hasNext()) {
SelectionKey key = (SelectionKey) keys.next();
if (!key.isValid())
continue;
keys.remove();
try {
if (key.isReadable()) {
SocketChannel socket = (SocketChannel) key.channel();
buf.clear();
socket.read(buf);
String input = ChatUtil.decode(buf);
//如果已经授权,则直接输出信息。
if (loginSeccess) {
System.out.println(input);
}
//如果没有登录, 且返回信息为授权登录, 则将登录旗标置为true, 并notify主线程。
else if (NBChatServer.LOGIN_OK.equals(input)) {
System.out.println("-------------------------------------------- Welcome ------------------------------------ ");
System.out.println("---------------------------------Non Blocking Chat Program------------------------------- ");
System.out.println("----------------------------------------Author: ChenLinping ----------------------------- ");
System.out.println("------------------------------------------------------------------------------------------");
feedback = loginSeccess = true;
// //用sleep保证此线程的notify在main线程的wait之后.
// //如果没有这个sleep,则可能发生先notify再wait的情况,则mail线程将一直等待oLogin的锁而阻塞。
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
synchronized (oLogin) {
oLogin.notifyAll();
}
}
//否则输出permission denied信息。
else {
System.out.println("Permission denied~");
feedback = true;
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
synchronized (oLogin) {
oLogin.notifyAll();
}
}
}
} catch (IOException e) {
System.out.println("You have disconnected!");
key.cancel();
try {
key.channel().close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
}
}.start();
}
private void handleMsg(String s) throws IOException {
if (CMD_QUIT.equals(s)) {
this.active = false;
socket.close();
System.exit(-1);
} else
socket.write(ByteBuffer.wrap(s.getBytes()));
}
private void doLogin(NBChatClient client, BufferedReader input) throws IOException {
while (!client.loginSeccess) {
System.out.print("user:");
String user = input.readLine();
System.out.print("pass:");
String pass = input.readLine();
client.handleMsg("/login:"+user+"/"+pass);
//可能存在的一种情况是:程序运行到这里,mail线程还没有block,而server已经响应了,告知client登录成功或失败。这种情况下,则不需要block mail线程。
//如果是登录成功,则不需要block线程,而应该直接进入聊天。
//如果是登录失败,也不需要block线程,而应该直接让用户重新登录。
while (!client.feedback) {
//输入登录信息后,阻塞main线程,等待系统验证,如果验证成功,则可以开始聊天。
synchronized (client.oLogin) {
try {
client.oLogin.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
NBChatClient client = new NBChatClient(args[0]);
try {
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
client.doLogin(client, input);
String s;
while ((s = input.readLine()) != null)
client.handleMsg(s);
} catch (IOException e) {
e.printStackTrace();
}
}
}