溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaNIO實(shí)現(xiàn)聊天室功能代碼實(shí)例

發(fā)布時間:2021-09-16 17:48:11 來源:億速云 閱讀:91 作者:chen 欄目:編程語言

這篇文章主要講解了“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)注!

向AI問一下細(xì)節(jié)

免責(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)容。

AI