Java實(shí)現(xiàn)Socket網(wǎng)絡(luò)編程(四)

在看到本文之前,如果讀者沒看過筆者的前文Java實(shí)現(xiàn)Socket網(wǎng)絡(luò)編程(三) ,請(qǐng)先翻閱。

下面,我們來實(shí)現(xiàn)服務(wù)器單體發(fā)送和廣播發(fā)送:

我們?yōu)镴List客戶端列表設(shè)置監(jiān)聽,在每次點(diǎn)擊觸發(fā)時(shí),先默認(rèn)設(shè)置所有Socket未選中,避免錯(cuò)亂,然后獲取全部已選中下標(biāo),并做好標(biāo)記。筆者此處采用HashMap存儲(chǔ)Socket,如Socket被選中,其對(duì)應(yīng)Value為true,反之為false。

        // 添加監(jiān)聽
        clientList.addListSelectionListener(new ListSelectionListener() {

            @Override
            public void valueChanged(ListSelectionEvent e) {

                // 先默認(rèn)把所有Socket未選中
                resetSocket(ListenThread.clientSockets);

                // 獲取全部已選中下標(biāo)
                int[] selecteds = clientList.getSelectedIndices();

                for (int i = 0; i < selecteds.length; i++) {
                    // 獲取所選中項(xiàng)的HashMap
                    HashMap<Socket, Boolean> map = ListenThread.clientSockets
                            .get(selecteds[i]);
                    // 用迭代器獲取HashMap的Key,即所選中的Socket
                    Iterator iter = map.entrySet().iterator();
                    Map.Entry<Socket, Boolean> entry = (Entry<Socket, Boolean>) iter
                            .next();
                    Socket key = (Socket) entry.getKey();
                    // 把所選中的Socket設(shè)置為true
                    map.replace(key, true);
                }
            }
        });

然后,在點(diǎn)擊發(fā)送消息的按鈕時(shí),根據(jù)之前在JList中標(biāo)記的Socket,向指定客戶端發(fā)送消息。

        // 設(shè)置發(fā)送消息監(jiān)聽
        jbSendMessage.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {

                if (jtaSendMessage.getText().equals("")) {
                    JOptionPane.showMessageDialog(null, "發(fā)送內(nèi)容不能為空!");
                    return;
                }

                if (jtaSendMessage.getText().length() > 5000) {
                    JOptionPane.showMessageDialog(null, "發(fā)送數(shù)據(jù)過大,最大不能超過5000字!");
                    return;
                }
                // 取得要發(fā)送的消息
                // 代表服務(wù)器正常連接
                String message = Common.OK;
                String t = "server " + Common.IP + ":" + Common.PORT + " "
                        + jtaSendMessage.getText();

                OutputStreamWriter outstream = null;
                // 將信息發(fā)送給每個(gè)選中的客戶端
                for (int i = 0; i < ListenThread.clientSockets.size(); i++) {
                    try {
                        HashMap<Socket, Boolean> map = ListenThread.clientSockets
                                .get(i);
                        // 用迭代器獲取HashMap的Key,即所選中的Socket
                        Iterator iter = map.entrySet().iterator();
                        Map.Entry<Socket, Boolean> entry = (Entry<Socket, Boolean>) iter
                                .next();

                        // 如果Socket已選中
                        if ((Boolean) entry.getValue()) {
                            Socket key = (Socket) entry.getKey();
                            outstream = new OutputStreamWriter(key
                                    .getOutputStream(), "GBK");
                            outstream.write(message);
                            outstream.flush();
                        }
                    } catch (IOException e1) {
                        if (outstream != null)
                            try {
                                outstream.close();
                            } catch (IOException e2) {
                                e2.printStackTrace();
                            }
                        e1.printStackTrace();
                    }
                }

                // 清空文本
                jtaSendMessage.setText(null);
            }
        });

為了實(shí)現(xiàn)客戶端自動(dòng)連接服務(wù)器,我們要為客戶端實(shí)現(xiàn)一個(gè)子線程,用于輪詢檢測(cè)服務(wù)器是否已啟動(dòng):

    public void run() {
        while (true) {

            // 窗口關(guān)閉則直接退出循環(huán),避免在關(guān)閉過程中再次連接服務(wù)器
            if (ClientMain.isWindowClosing)
                break;

            try {
                if (ClientMain.mSocket == null || ClientMain.mSocket.isClosed()) {
                    ClientMain.isConnected = false;
                    ClientMain.jlConnect.setText("Out Of Connect.");
                }

                if (!ClientMain.isConnected) {
                    Socket socket = new Socket(Common.IP, Common.PORT);
                    ClientMain.mSocket = socket;
                    // 啟動(dòng)客戶端連接子線程
                    new Thread(new ClientReceivedThread(socket)).start();
                    ClientMain.isConnected = true;
                    ClientMain.jlConnect.setText("Success Connect.");
                }
            } catch (Exception e) {
                // 無法連接服務(wù)器
                ClientMain.isConnected = false;
                ClientMain.jlConnect.setText("Out Of Connect.");
                e.printStackTrace();
            }
        }
    }

在這里,讀者可能會(huì)遇到一個(gè)問題,客戶端是如何檢測(cè)到服務(wù)器斷開,而服務(wù)器又如何檢測(cè)到客戶端斷開呢?

筆者為讀者提供兩種方法:
1、檢測(cè)讀寫錯(cuò)誤,在BufferedReader的read()方法中,如果發(fā)生讀寫錯(cuò)誤,便能捕獲到。
2、發(fā)送心跳包檢測(cè)

對(duì)于方法1,服務(wù)器和客戶端實(shí)現(xiàn)的方法一樣
對(duì)于方法2,服務(wù)器應(yīng)發(fā)送心跳包0,客戶端應(yīng)發(fā)送心跳到0xFF

mSocket.sendUrgentData(0);//服務(wù)器檢測(cè)客戶端斷開
mSocket.sendUrgentData(0xFF);//客戶端檢測(cè)服務(wù)器斷開

當(dāng)捕獲到異常時(shí),便能得知對(duì)方斷開。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容