Java NIO实现简单聊天室(GUI版)

来源:互联网 发布:知乎 优越感 恶心 编辑:程序博客网 时间:2024/06/06 08:54

Java NIO实现简单聊天室(GUI版)

登录界面:

package com.chatroom;import java.awt.Toolkit;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JLabel;import javax.swing.JTextField;import com.chat.client.ChatFrame;import com.chat.client.ClientService;/* * 登录聊天室 */public class LoginFrame extends JFrame{static final long serialVersionUID = 1L;static JTextField txtName;static JButton btnOK;static JLabel label;public LoginFrame() {this.setLayout(null);Toolkit kit = Toolkit.getDefaultToolkit();int w = kit.getScreenSize().width;int h = kit.getScreenSize().height;this.setBounds(w / 2 - 230 / 2, h / 2 - 200 / 2, 230, 200);this.setTitle("设置名称");this.setDefaultCloseOperation(EXIT_ON_CLOSE);this.setResizable(false);txtName = new JTextField(4);this.add(txtName);txtName.setBounds(10, 10, 100, 25);btnOK = new JButton("OK");this.add(btnOK);btnOK.setBounds(120, 10, 80, 25);label = new JLabel("[w:" + w + ",h:" + h + "]");this.add(label);label.setBounds(10, 40, 200, 100);label.setText("<html>在上面的文本框中输入名字<br/>显示器宽度:" + w + "<br/>显示器高度:" + h+ "</html>");btnOK.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {String uname = txtName.getText();ClientService service = ClientService.getInstance();ChatFrame chatFrame = new ChatFrame(service, uname);chatFrame.show();setVisible(false);}});}public static void main(String[] args) {LoginFrame loginFrame = new LoginFrame();loginFrame.setVisible(true);}}

服务端:

package com.chatroom;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.text.SimpleDateFormat;import java.util.Date;import java.util.Iterator;import java.util.Vector;public class ChatServer implements Runnable{Selector selector;SelectionKey  selectionKey;boolean isRunning;Vector<String> usernames;//用来存放用户的名字SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public ChatServer(int port) throws IOException {isRunning = true;usernames = new Vector<>();init(port);}private void printInfo(String str) {System.out.println("[" + sdf.format(new Date()) + "] -> " + str);}/* * 初始化,启动服务器 */public void init(int port) throws IOException {selector=Selector.open();ServerSocketChannel server = ServerSocketChannel.open();server.socket().bind(new InetSocketAddress(port));server.configureBlocking(false);selectionKey = server.register(selector, SelectionKey.OP_ACCEPT);printInfo("启动服务器");}@Overridepublic void run() {try {while (isRunning) {int n=selector.select();if (n>0) {Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();/* * 如有客户端进来则进行连接 */if (key.isAcceptable()) {ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();SocketChannel socketChannel = serverSocketChannel.accept();if (socketChannel==null) {continue;}socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);}if (key.isReadable()) {readMsg(key);}if (key.isWritable()) {writeMsg(key);}}}}} catch (Exception e) {// TODO: handle exception}}/* * 读取信息 */public void readMsg(SelectionKey key) throws IOException {SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);StringBuffer sBuffer = new StringBuffer();int count = socketChannel.read(buffer);//读取从客户端传过来的信息大小/* * 如果有信息则加到StringBuffer中 */if (count>0) {buffer.flip();sBuffer.append(new String(buffer.array(), 0, count));}/*将信息转换成字符串*/String string = sBuffer.toString();/* * 处理用户上线的情况 */if (string.indexOf("open_") != -1) {String name = string.substring(5);printInfo(name+"上线");usernames.add(name);Iterator<SelectionKey> sIterator = selector.selectedKeys().iterator();while (sIterator.hasNext()) {SelectionKey keyss = sIterator.next();if (keyss!=selectionKey) {keyss.attach(usernames);keyss.interestOps(keyss.interestOps() | SelectionKey.OP_WRITE);}}/* * 处理用户下线的情况 */} else if (string.indexOf("exit_") != -1) {String username = string.substring(5);usernames.remove(username);key.attach("close");key.interestOps(SelectionKey.OP_WRITE);Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey sKey = iterator.next();if (sKey != selectionKey &&  sKey != key) {sKey.attach(usernames);sKey.interestOps(sKey.interestOps() | SelectionKey.OP_WRITE);}}printInfo(username+"下线");/* * 在控制台输出客户端的对话 */}else {String username = string.substring(0, string.indexOf("^"));String msg = string.substring(string.indexOf("^") + 1);printInfo("("+username+")说:" + msg);String dateTime = sdf.format(new Date());String smsg = username + " " + dateTime + "\n  " + msg + "\n";Iterator<SelectionKey> iter = selector.selectedKeys().iterator();while (iter.hasNext()) {SelectionKey selKey = iter.next();if (selKey != selectionKey) {selKey.attach(smsg);selKey.interestOps(selKey.interestOps() | SelectionKey.OP_WRITE);}}}}/* * 传递信息到客户端 */public void writeMsg(SelectionKey key) throws IOException {SocketChannel socketChannel =  (SocketChannel) key.channel();Object object = key.attachment();key.attach("");if (object.toString().equals("close")) {key.cancel();socketChannel.socket().close();socketChannel.close();return ;}else {socketChannel.write(ByteBuffer.wrap(object.toString().getBytes()));}key.interestOps(SelectionKey.OP_READ);}public static void main(String[] args) throws IOException {ChatServer chatServer = new ChatServer(4400);new Thread(chatServer).start();}}

客户端:

package com.chatroom;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SocketChannel;import com.chat.client.ClientService;public class ChatClient {static final String HOST = "127.0.0.1";static final int PORT = 4477;static SocketChannel sc;static Object lock = new Object();static ChatClient chatClient;public static ChatClient getInstance() throws IOException {synchronized (lock) {if (chatClient==null) {chatClient = new ChatClient();}}return chatClient;}public ChatClient() throws IOException {sc=SocketChannel.open();sc.configureBlocking(false);sc.connect(new InetSocketAddress(HOST, PORT));}public void sendMsg(String string) throws IOException {sc.write(ByteBuffer.wrap(string.getBytes()));}public String receiveMsg() throws InterruptedException, IOException {ByteBuffer buffer = ByteBuffer.allocate(1024);buffer.clear();StringBuffer stringBuffer = new StringBuffer();int count =0;String msg = null;Thread.sleep(100);while ((count=sc.read(buffer))>0) {stringBuffer.append(new String(buffer.array(), 0, count));}if (stringBuffer.length() > 0) {msg = stringBuffer.toString();if ("close".equals(stringBuffer.toString())) {msg = null;sc.close();sc.socket().close();}}return msg;}}

聊天室主体:

package com.chatroom;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import javax.swing.DefaultListModel;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JList;import javax.swing.JScrollPane;import javax.swing.JTextArea;import javax.swing.event.ListSelectionEvent;import javax.swing.event.ListSelectionListener;import com.chat.client.ClientService;public class ChatFrame {private JTextArea readContext = new JTextArea(18, 30);// 显示消息文本框private JTextArea writeContext = new JTextArea(6, 30);// 发送消息文本框private DefaultListModel modle = new DefaultListModel();// 用户列表模型private JList list = new JList(modle);// 用户列表private JButton btnSend = new JButton("发送");// 发送消息按钮private JButton btnClose = new JButton("关闭");// 关闭聊天窗口按钮private JFrame frame = new JFrame("ChatFrame");// 窗体界面private String uname;// 用户姓名private ClientService service;// 用于与服务器交互private boolean isRun = false;// 是否运行public ChatFrame(ClientService service, String uname) {this.isRun = true;this.uname = uname;this.service = service;}// 初始化界面控件及事件private void init() {frame.setLayout(null);frame.setTitle(uname + " 聊天窗口");frame.setSize(500, 500);frame.setLocation(400, 200);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setResizable(false);JScrollPane readScroll = new JScrollPane(readContext);readScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);frame.add(readScroll);JScrollPane writeScroll = new JScrollPane(writeContext);writeScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);frame.add(writeScroll);frame.add(list);frame.add(btnSend);frame.add(btnClose);readScroll.setBounds(10, 10, 320, 300);readContext.setBounds(0, 0, 320, 300);readContext.setEditable(false);readContext.setLineWrap(true);// 自动换行writeScroll.setBounds(10, 315, 320, 100);writeContext.setBounds(0, 0, 320, 100);writeContext.setLineWrap(true);// 自动换行list.setBounds(340, 10, 140, 445);btnSend.setBounds(150, 420, 80, 30);btnClose.setBounds(250, 420, 80, 30);frame.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {isRun = false;service.sendMsg("exit_" + uname);System.exit(0);}});btnSend.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {String msg = writeContext.getText().trim();if(msg.length() > 0){service.sendMsg(uname + "^" + writeContext.getText());}writeContext.setText(null);writeContext.requestFocus();}});btnClose.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {isRun = false;service.sendMsg("exit_" + uname);System.exit(0);}});list.addListSelectionListener(new ListSelectionListener() {@Overridepublic void valueChanged(ListSelectionEvent e) {// JOptionPane.showMessageDialog(null,// list.getSelectedValue().toString());}});writeContext.addKeyListener(new KeyListener() {@Overridepublic void keyTyped(KeyEvent e) {// TODO Auto-generated method stub}@Overridepublic void keyReleased(KeyEvent e) {if(e.getKeyCode() == KeyEvent.VK_ENTER){String msg = writeContext.getText().trim();if(msg.length() > 0){service.sendMsg(uname + "^" + writeContext.getText());}writeContext.setText(null);writeContext.requestFocus();}}@Overridepublic void keyPressed(KeyEvent e) {// TODO Auto-generated method stub}});}// 此线程类用于轮询读取服务器发送的消息private class MsgThread extends Thread {@Overridepublic void run() {while (isRun) {String msg = service.receiveMsg();if (msg != null) {if (msg.indexOf("[") != -1 && msg.lastIndexOf("]") != -1) {msg = msg.substring(1, msg.length() - 1);String[] userNames = msg.split(",");modle.removeAllElements();for (int i = 0; i < userNames.length; i++) {modle.addElement(userNames[i].trim());}} else {String str = readContext.getText() + msg;readContext.setText(str);readContext.selectAll();}}}}}// 显示界面public void show() {this.init();service.sendMsg("open_" + uname);MsgThread msgThread = new MsgThread();msgThread.start();this.frame.setVisible(true);}}

Java NIO实现简单聊天室(GUI版)

登录界面:

package com.chatroom;import java.awt.Toolkit;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JLabel;import javax.swing.JTextField;import com.chat.client.ChatFrame;import com.chat.client.ClientService;/* * 登录聊天室 */public class LoginFrame extends JFrame{static final long serialVersionUID = 1L;static JTextField txtName;static JButton btnOK;static JLabel label;public LoginFrame() {this.setLayout(null);Toolkit kit = Toolkit.getDefaultToolkit();int w = kit.getScreenSize().width;int h = kit.getScreenSize().height;this.setBounds(w / 2 - 230 / 2, h / 2 - 200 / 2, 230, 200);this.setTitle("设置名称");this.setDefaultCloseOperation(EXIT_ON_CLOSE);this.setResizable(false);txtName = new JTextField(4);this.add(txtName);txtName.setBounds(10, 10, 100, 25);btnOK = new JButton("OK");this.add(btnOK);btnOK.setBounds(120, 10, 80, 25);label = new JLabel("[w:" + w + ",h:" + h + "]");this.add(label);label.setBounds(10, 40, 200, 100);label.setText("<html>在上面的文本框中输入名字<br/>显示器宽度:" + w + "<br/>显示器高度:" + h+ "</html>");btnOK.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {String uname = txtName.getText();ClientService service = ClientService.getInstance();ChatFrame chatFrame = new ChatFrame(service, uname);chatFrame.show();setVisible(false);}});}public static void main(String[] args) {LoginFrame loginFrame = new LoginFrame();loginFrame.setVisible(true);}}

服务端:

package com.chatroom;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.text.SimpleDateFormat;import java.util.Date;import java.util.Iterator;import java.util.Vector;public class ChatServer implements Runnable{Selector selector;SelectionKey  selectionKey;boolean isRunning;Vector<String> usernames;//用来存放用户的名字SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public ChatServer(int port) throws IOException {isRunning = true;usernames = new Vector<>();init(port);}private void printInfo(String str) {System.out.println("[" + sdf.format(new Date()) + "] -> " + str);}/* * 初始化,启动服务器 */public void init(int port) throws IOException {selector=Selector.open();ServerSocketChannel server = ServerSocketChannel.open();server.socket().bind(new InetSocketAddress(port));server.configureBlocking(false);selectionKey = server.register(selector, SelectionKey.OP_ACCEPT);printInfo("启动服务器");}@Overridepublic void run() {try {while (isRunning) {int n=selector.select();if (n>0) {Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();/* * 如有客户端进来则进行连接 */if (key.isAcceptable()) {ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();SocketChannel socketChannel = serverSocketChannel.accept();if (socketChannel==null) {continue;}socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);}if (key.isReadable()) {readMsg(key);}if (key.isWritable()) {writeMsg(key);}}}}} catch (Exception e) {// TODO: handle exception}}/* * 读取信息 */public void readMsg(SelectionKey key) throws IOException {SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);StringBuffer sBuffer = new StringBuffer();int count = socketChannel.read(buffer);//读取从客户端传过来的信息大小/* * 如果有信息则加到StringBuffer中 */if (count>0) {buffer.flip();sBuffer.append(new String(buffer.array(), 0, count));}/*将信息转换成字符串*/String string = sBuffer.toString();/* * 处理用户上线的情况 */if (string.indexOf("open_") != -1) {String name = string.substring(5);printInfo(name+"上线");usernames.add(name);Iterator<SelectionKey> sIterator = selector.selectedKeys().iterator();while (sIterator.hasNext()) {SelectionKey keyss = sIterator.next();if (keyss!=selectionKey) {keyss.attach(usernames);keyss.interestOps(keyss.interestOps() | SelectionKey.OP_WRITE);}}/* * 处理用户下线的情况 */} else if (string.indexOf("exit_") != -1) {String username = string.substring(5);usernames.remove(username);key.attach("close");key.interestOps(SelectionKey.OP_WRITE);Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey sKey = iterator.next();if (sKey != selectionKey &&  sKey != key) {sKey.attach(usernames);sKey.interestOps(sKey.interestOps() | SelectionKey.OP_WRITE);}}printInfo(username+"下线");/* * 在控制台输出客户端的对话 */}else {String username = string.substring(0, string.indexOf("^"));String msg = string.substring(string.indexOf("^") + 1);printInfo("("+username+")说:" + msg);String dateTime = sdf.format(new Date());String smsg = username + " " + dateTime + "\n  " + msg + "\n";Iterator<SelectionKey> iter = selector.selectedKeys().iterator();while (iter.hasNext()) {SelectionKey selKey = iter.next();if (selKey != selectionKey) {selKey.attach(smsg);selKey.interestOps(selKey.interestOps() | SelectionKey.OP_WRITE);}}}}/* * 传递信息到客户端 */public void writeMsg(SelectionKey key) throws IOException {SocketChannel socketChannel =  (SocketChannel) key.channel();Object object = key.attachment();key.attach("");if (object.toString().equals("close")) {key.cancel();socketChannel.socket().close();socketChannel.close();return ;}else {socketChannel.write(ByteBuffer.wrap(object.toString().getBytes()));}key.interestOps(SelectionKey.OP_READ);}public static void main(String[] args) throws IOException {ChatServer chatServer = new ChatServer(4400);new Thread(chatServer).start();}}

客户端:

package com.chatroom;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SocketChannel;import com.chat.client.ClientService;public class ChatClient {static final String HOST = "127.0.0.1";static final int PORT = 4477;static SocketChannel sc;static Object lock = new Object();static ChatClient chatClient;public static ChatClient getInstance() throws IOException {synchronized (lock) {if (chatClient==null) {chatClient = new ChatClient();}}return chatClient;}public ChatClient() throws IOException {sc=SocketChannel.open();sc.configureBlocking(false);sc.connect(new InetSocketAddress(HOST, PORT));}public void sendMsg(String string) throws IOException {sc.write(ByteBuffer.wrap(string.getBytes()));}public String receiveMsg() throws InterruptedException, IOException {ByteBuffer buffer = ByteBuffer.allocate(1024);buffer.clear();StringBuffer stringBuffer = new StringBuffer();int count =0;String msg = null;Thread.sleep(100);while ((count=sc.read(buffer))>0) {stringBuffer.append(new String(buffer.array(), 0, count));}if (stringBuffer.length() > 0) {msg = stringBuffer.toString();if ("close".equals(stringBuffer.toString())) {msg = null;sc.close();sc.socket().close();}}return msg;}}

聊天室主体:

package com.chatroom;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import javax.swing.DefaultListModel;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JList;import javax.swing.JScrollPane;import javax.swing.JTextArea;import javax.swing.event.ListSelectionEvent;import javax.swing.event.ListSelectionListener;import com.chat.client.ClientService;public class ChatFrame {private JTextArea readContext = new JTextArea(18, 30);// 显示消息文本框private JTextArea writeContext = new JTextArea(6, 30);// 发送消息文本框private DefaultListModel modle = new DefaultListModel();// 用户列表模型private JList list = new JList(modle);// 用户列表private JButton btnSend = new JButton("发送");// 发送消息按钮private JButton btnClose = new JButton("关闭");// 关闭聊天窗口按钮private JFrame frame = new JFrame("ChatFrame");// 窗体界面private String uname;// 用户姓名private ClientService service;// 用于与服务器交互private boolean isRun = false;// 是否运行public ChatFrame(ClientService service, String uname) {this.isRun = true;this.uname = uname;this.service = service;}// 初始化界面控件及事件private void init() {frame.setLayout(null);frame.setTitle(uname + " 聊天窗口");frame.setSize(500, 500);frame.setLocation(400, 200);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setResizable(false);JScrollPane readScroll = new JScrollPane(readContext);readScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);frame.add(readScroll);JScrollPane writeScroll = new JScrollPane(writeContext);writeScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);frame.add(writeScroll);frame.add(list);frame.add(btnSend);frame.add(btnClose);readScroll.setBounds(10, 10, 320, 300);readContext.setBounds(0, 0, 320, 300);readContext.setEditable(false);readContext.setLineWrap(true);// 自动换行writeScroll.setBounds(10, 315, 320, 100);writeContext.setBounds(0, 0, 320, 100);writeContext.setLineWrap(true);// 自动换行list.setBounds(340, 10, 140, 445);btnSend.setBounds(150, 420, 80, 30);btnClose.setBounds(250, 420, 80, 30);frame.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {isRun = false;service.sendMsg("exit_" + uname);System.exit(0);}});btnSend.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {String msg = writeContext.getText().trim();if(msg.length() > 0){service.sendMsg(uname + "^" + writeContext.getText());}writeContext.setText(null);writeContext.requestFocus();}});btnClose.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {isRun = false;service.sendMsg("exit_" + uname);System.exit(0);}});list.addListSelectionListener(new ListSelectionListener() {@Overridepublic void valueChanged(ListSelectionEvent e) {// JOptionPane.showMessageDialog(null,// list.getSelectedValue().toString());}});writeContext.addKeyListener(new KeyListener() {@Overridepublic void keyTyped(KeyEvent e) {// TODO Auto-generated method stub}@Overridepublic void keyReleased(KeyEvent e) {if(e.getKeyCode() == KeyEvent.VK_ENTER){String msg = writeContext.getText().trim();if(msg.length() > 0){service.sendMsg(uname + "^" + writeContext.getText());}writeContext.setText(null);writeContext.requestFocus();}}@Overridepublic void keyPressed(KeyEvent e) {// TODO Auto-generated method stub}});}// 此线程类用于轮询读取服务器发送的消息private class MsgThread extends Thread {@Overridepublic void run() {while (isRun) {String msg = service.receiveMsg();if (msg != null) {if (msg.indexOf("[") != -1 && msg.lastIndexOf("]") != -1) {msg = msg.substring(1, msg.length() - 1);String[] userNames = msg.split(",");modle.removeAllElements();for (int i = 0; i < userNames.length; i++) {modle.addElement(userNames[i].trim());}} else {String str = readContext.getText() + msg;readContext.setText(str);readContext.selectAll();}}}}}// 显示界面public void show() {this.init();service.sendMsg("open_" + uname);MsgThread msgThread = new MsgThread();msgThread.start();this.frame.setVisible(true);}}