您好,登錄后才能下訂單哦!
這篇文章主要講解了“JavaNIO實(shí)現(xiàn)聊天室功能代碼實(shí)例”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“JavaNIO實(shí)現(xiàn)聊天室功能代碼實(shí)例”吧!
一、服務(wù)器端
package com.chat.server;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;/** * 聊天室:服務(wù)端 * @author zing * */public class ChatServer implements Runnable { //選擇器 private Selector selector; //注冊ServerSocketChannel后的選擇鍵 private SelectionKey serverKey; //標(biāo)識是否運(yùn)行 private boolean isRun; //當(dāng)前聊天室中的用戶名稱列表 private Vector<String> unames; //時間格式化器 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * 構(gòu)造函數(shù) * @param port 服務(wù)端監(jiān)控的端口號 */ public ChatServer(int port) { isRun = true; unames = new Vector<String>(); init(port); } /** * 初始化選擇器和服務(wù)器套接字 * * @param port 服務(wù)端監(jiān)控的端口號 */ private void init(int port) { try { //獲得選擇器實(shí)例 selector = Selector.open(); //獲得服務(wù)器套接字實(shí)例 ServerSocketChannel serverChannel = ServerSocketChannel.open(); //綁定端口號 serverChannel.socket().bind(new InetSocketAddress(port)); //設(shè)置為非阻塞 serverChannel.configureBlocking(false); //將ServerSocketChannel注冊到選擇器,指定其行為為"等待接受連接" serverKey = serverChannel.register(selector, SelectionKey.OP_ACCEPT); printInfo("server starting..."); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { try { //輪詢選擇器選擇鍵 while (isRun) { //選擇一組已準(zhǔn)備進(jìn)行IO操作的通道的key,等于1時表示有這樣的key int n = selector.select(); if (n > 0) { //從選擇器上獲取已選擇的key的集合并進(jìn)行迭代 Iterator<SelectionKey> iter = selector.selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey key = iter.next(); //若此key的通道是等待接受新的套接字連接 if (key.isAcceptable()) { //記住一定要remove這個key,否則之后的新連接將被阻塞無法連接服務(wù)器 iter.remove(); //獲取key對應(yīng)的通道 ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); //接受新的連接返回和客戶端對等的套接字通道 SocketChannel channel = serverChannel.accept(); if (channel == null) { continue; } //設(shè)置為非阻塞 channel.configureBlocking(false); //將這個套接字通道注冊到選擇器,指定其行為為"讀" channel.register(selector, SelectionKey.OP_READ); } //若此key的通道的行為是"讀" if (key.isReadable()) { readMsg(key); } //若次key的通道的行為是"寫" if (key.isWritable()) { writeMsg(key); } } } } } catch (IOException e) { e.printStackTrace(); } } /** * 從key對應(yīng)的套接字通道上讀數(shù)據(jù) * @param key 選擇鍵 * @throws IOException */ private void readMsg(SelectionKey key) throws IOException { //獲取此key對應(yīng)的套接字通道 SocketChannel channel = (SocketChannel) key.channel(); //創(chuàng)建一個大小為1024k的緩存區(qū) ByteBuffer buffer = ByteBuffer.allocate(1024); StringBuffer sb = new StringBuffer(); //將通道的數(shù)據(jù)讀到緩存區(qū) int count = channel.read(buffer); if (count > 0) { //翻轉(zhuǎn)緩存區(qū)(將緩存區(qū)由寫進(jìn)數(shù)據(jù)模式變成讀出數(shù)據(jù)模式) buffer.flip(); //將緩存區(qū)的數(shù)據(jù)轉(zhuǎn)成String sb.append(new String(buffer.array(), 0, count)); } String str = sb.toString(); //若消息中有"open_",表示客戶端準(zhǔn)備進(jìn)入聊天界面 //客戶端傳過來的數(shù)據(jù)格式是"open_zing",表示名稱為zing的用戶請求打開聊天窗體 //用戶名稱列表有更新,則應(yīng)將用戶名稱數(shù)據(jù)寫給每一個已連接的客戶端 if (str.indexOf("open_") != -1) {//客戶端連接服務(wù)器 String name = str.substring(5); printInfo(name + " online"); unames.add(name); //獲取選擇器已選擇的key并迭代 Iterator<SelectionKey> iter = selector.selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey selKey = iter.next(); //若不是服務(wù)器套接字通道的key,則將數(shù)據(jù)設(shè)置到此key中 //并更新此key感興趣的動作 if (selKey != serverKey) { selKey.attach(unames); selKey.interestOps(selKey.interestOps() | SelectionKey.OP_WRITE); } } } else if (str.indexOf("exit_") != -1) {// 客戶端發(fā)送退出命令 String uname = str.substring(5); //刪除此用戶名稱 unames.remove(uname); //將"close"字符串附加到key key.attach("close"); //更新此key感興趣的動作 key.interestOps(SelectionKey.OP_WRITE); //獲取選擇器上的已選擇的key并迭代 //將更新后的名稱列表數(shù)據(jù)附加到每個套接字通道key上,并重設(shè)key感興趣的操作 Iterator<SelectionKey> iter = key.selector().selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey selKey = iter.next(); if (selKey != serverKey && selKey != key) { selKey.attach(unames); selKey.interestOps(selKey.interestOps() | SelectionKey.OP_WRITE); } } printInfo(uname + " offline"); } else {// 讀取客戶端聊天消息 String uname = str.substring(0, str.indexOf("^")); String msg = str.substring(str.indexOf("^") + 1); printInfo("("+uname+")說:" + msg); String dateTime = sdf.format(new Date()); String smsg = uname + " " + dateTime + "\n " + msg + "\n"; Iterator<SelectionKey> iter = selector.selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey selKey = iter.next(); if (selKey != serverKey) { selKey.attach(smsg); selKey.interestOps(selKey.interestOps() | SelectionKey.OP_WRITE); } } } } /** * 寫數(shù)據(jù)到key對應(yīng)的套接字通道 * @param key * @throws IOException */ private void writeMsg(SelectionKey key) throws IOException { SocketChannel channel = (SocketChannel) key.channel(); Object obj = key.attachment(); //這里必要要將key的附加數(shù)據(jù)設(shè)置為空,否則會有問題 key.attach(""); //附加值為"close",則取消此key,并關(guān)閉對應(yīng)通道 if (obj.toString().equals("close")) { key.cancel(); channel.socket().close(); channel.close(); return; }else { //將數(shù)據(jù)寫到通道 channel.write(ByteBuffer.wrap(obj.toString().getBytes())); } //重設(shè)此key興趣 key.interestOps(SelectionKey.OP_READ); } private void printInfo(String str) { System.out.println("[" + sdf.format(new Date()) + "] -> " + str); } public static void main(String[] args) { ChatServer server = new ChatServer(19999); new Thread(server).start(); }}
二、客戶端
1、服務(wù)類,用于與服務(wù)端交互
package com.chat.client;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SocketChannel;public class ClientService { private static final String HOST = "127.0.0.1"; private static final int PORT = 19999; private static SocketChannel sc; private static Object lock = new Object(); private static ClientService service; public static ClientService getInstance(){ synchronized (lock) { if(service == null){ try { service = new ClientService(); } catch (IOException e) { e.printStackTrace(); } } return service; } } private ClientService() throws IOException { sc = SocketChannel.open(); sc.configureBlocking(false); sc.connect(new InetSocketAddress(HOST, PORT)); } public void sendMsg(String msg) { try { while (!sc.finishConnect()) { } sc.write(ByteBuffer.wrap(msg.getBytes())); } catch (IOException e) { e.printStackTrace(); } } public String receiveMsg() { ByteBuffer buffer = ByteBuffer.allocate(1024); buffer.clear(); StringBuffer sb = new StringBuffer(); int count = 0; String msg = null; try { while ((count = sc.read(buffer)) > 0) { sb.append(new String(buffer.array(), 0, count)); } if (sb.length() > 0) { msg = sb.toString(); if ("close".equals(sb.toString())) { msg = null; sc.close(); sc.socket().close(); } } } catch (IOException e) { e.printStackTrace(); } return msg; }}
2、登陸窗體,用戶設(shè)置名稱
package com.chat.client;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;/** * 設(shè)置名稱窗體 * * @author zing * */public class SetNameFrame extends JFrame { private static final long serialVersionUID = 1L; private static JTextField txtName;// 文本框 private static JButton btnOK;// ok按鈕 private static JLabel label;// 標(biāo)簽 public SetNameFrame() { 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("設(shè)置名稱"); 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() { @Override public 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) { SetNameFrame setNameFrame = new SetNameFrame(); setNameFrame.setVisible(true); }}
3、聊天室窗體
package com.chat.client;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;/** * 聊天室窗體 * @author zing * */public class ChatFrame { private JTextArea readContext = new JTextArea(18, 30);// 顯示消息文本框 private JTextArea writeContext = new JTextArea(6, 30);// 發(fā)送消息文本框 private DefaultListModel modle = new DefaultListModel();// 用戶列表模型 private JList list = new JList(modle);// 用戶列表 private JButton btnSend = new JButton("發(fā)送");// 發(fā)送消息按鈕 private JButton btnClose = new JButton("關(guān)閉");// 關(guān)閉聊天窗口按鈕 private JFrame frame = new JFrame("ChatFrame");// 窗體界面 private String uname;// 用戶姓名 private ClientService service;// 用于與服務(wù)器交互 private boolean isRun = false;// 是否運(yùn)行 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); //設(shè)置可關(guān)閉 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //不能改變窗體大小 frame.setResizable(false); //聊天消息顯示區(qū)帶滾動條 JScrollPane readScroll = new JScrollPane(readContext); readScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); frame.add(readScroll); //消息編輯區(qū)帶滾動條 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);//設(shè)置為不可編輯 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); //窗體關(guān)閉事件 frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { isRun = false; service.sendMsg("exit_" + uname); System.exit(0); } }); //發(fā)送按鈕事件 btnSend.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String msg = writeContext.getText().trim(); if(msg.length() > 0){ service.sendMsg(uname + "^" + writeContext.getText()); } //發(fā)送消息后,去掉編輯區(qū)文本,并獲得光標(biāo)焦點(diǎn) writeContext.setText(null); writeContext.requestFocus(); } }); //關(guān)閉按鈕事件 btnClose.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { isRun = false; service.sendMsg("exit_" + uname); System.exit(0); } }); //右邊名稱列表選擇事件 list.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { // JOptionPane.showMessageDialog(null, // list.getSelectedValue().toString()); } }); //消息編輯區(qū)鍵盤按鍵事件 writeContext.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } //按下鍵盤按鍵后釋放 @Override public void keyReleased(KeyEvent e) { //按下enter鍵發(fā)送消息 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(); } } @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub } }); } // 此線程類用于輪詢讀取服務(wù)器發(fā)送的消息 private class MsgThread extends Thread { @Override public void run() { while (isRun) { String msg = service.receiveMsg(); if (msg != null) { //若是名稱列表數(shù)據(jù),則更新聊天窗體右邊的列表 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 { //將聊天數(shù)據(jù)設(shè)置到聊天消息顯示區(qū) 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); }}
感謝各位的閱讀,以上就是“JavaNIO實(shí)現(xiàn)聊天室功能代碼實(shí)例”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對JavaNIO實(shí)現(xiàn)聊天室功能代碼實(shí)例這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。