[知了堂學習筆記]_網絡聊天室

實現客戶端監控

目的:在服務器端寫線程,讓服務端線程代理客戶端的socket
技術:利用線程池來管理所有的客戶端套接字

package chat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TCPServer {    

    private ServerSocket serverSocket;//創建服務器端的套接字
    private ExecutorService executorService;//線程池,代理監控到的客戶端套接字對象
    private Map<String , PrintWriter> storeInfo;//儲存客戶端的信息,儲存客戶端名稱+IO流

    public TCPServer(){
        try {
            serverSocket = new ServerSocket(8888);
            executorService = Executors.newCachedThreadPool();
            storeInfo = new HashMap<>();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    /**
     * 儲存客戶端的信息(key--暱稱,value--ip)
     * @param key
     * @param value
     */

    public synchronized void putIn(String key,PrintWriter value){
        storeInfo.put(key, value);
    }

    /**
     * 發送消息給所有客戶端
     * @param message
     */
    public synchronized void sendToAll(String message){
        /**
         * 原理Map接口還是這樣子做遍歷
         */
        for(PrintWriter pw : storeInfo.values()){
            pw.println(message);
        }
    }

    /**
     * 發送消息給指定客戶端(私聊)
     * @param name
     * @param message
     */
    public synchronized void sendToSomeone(String name,String message){
        PrintWriter pw = storeInfo.get(name);
        if(name != null){
            pw.println(message);
        }
    }

    /**
     * 移除客戶端的信息
     * @param name
     */
    public synchronized void remove(String name){
        storeInfo.remove(name);
        System.out.println("當前在線人數:"+storeInfo.size());
    }

    /*
     * 啓動服務器端套接字
     */
    public void start(){
        try {
            while(true){
                System.out.println("等待客戶端連接....");
                Socket socket = serverSocket.accept();

                //獲取客戶端IP地址
                InetAddress inetAddress = socket.getInetAddress();
                System.out.println("客戶端:"+inetAddress.getHostAddress()+"連接成功");

                //線程來代理客戶端的套接字對象
                //線程代理後,啓動run方法
                executorService.execute(new ListenerClient(socket));

            }           
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /*
     * 用線程代理客戶端的套接字對象
     */
    class ListenerClient implements Runnable{

        private Socket socket;
        private String name;

        public ListenerClient(Socket socket){
            this.socket = socket;
        }

        /*
         * 1.獲取輸出流,發送信息
         * 2.讀取客戶端的信息,讀取客戶端的暱稱
         * 3.檢驗是羣聊和私聊(客戶端如果發送@...,代表私聊)
         * 4.客戶端關閉,程序還得把客戶端套接字關閉
         * 
         */
        @Override
        public void run() {
            //一定要注意把客戶端套接字的流保存storeInfo裏面
            PrintWriter pw;
            try {
                pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"),true);
                String name = getName();
                putIn(name, pw);
                Thread.sleep(100);

                sendToAll("[系統通知]"+name+"已上線");
                //獲取輸入信息
                BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));
                //讀取信息,判斷是羣聊還是私聊
                String message = null;
                while((message = br.readLine()) != null){
                    if(message.startsWith("@")){
                        int index = message.indexOf(":");
                        if(index>0){
                            //@對方的名字
                            String theName = message.substring(1,index);
                            //@對方的內容
                            String info = message.substring(index+1);
                            //組合消息內,自己的名字+發送的內容(對方看到)
                            info = name +":"+info;
                            //調用私聊方法
                            sendToSomeone(theName, info);
                            continue;
                        }
                    }
                    //羣聊
                    sendToAll(name+":"+message);
                }
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }finally{
                remove(name);
                sendToAll("[系統通知]:"+name+"已經下線");
                if(socket != null){
                    try {
                        socket.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }

          /**
         * 獲取客戶端暱稱
         * @return
         */
        private String getName(){
            try {
                //服務器發送消息給客戶的輸入的信息是否有重複
                PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),"UTF-8"),true);
                //讀取客戶端輸入的暱稱
                BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));
                while(true){
                    String name = br.readLine();
                    if(name.trim().length() == 0 || storeInfo.containsKey(name)){//未寫或者重複
                        pw.println("FAIL");
                    }else {
                        pw.println("OK");
                        return name;
                    }
                }
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }

    }

    public static void main(String[] args) {

        TCPServer tcpServer = new TCPServer();
        tcpServer.start();

    }
}
package chat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TCPClient {

    private static Socket socket;

    public TCPClient() throws UnknownHostException, IOException{
        System.out.println("設置服務器端的地址:");
        Scanner scanner = new Scanner(System.in);
        String ip = scanner.nextLine();
        socket = new Socket(ip,8888);
        start();
    }

    /**
     * 啓動客戶端套接字
     * 同時需要有線程代理管理套接字
     * @throws IOException 
     * @throws UnsupportedEncodingException 
     */
    private void start() throws UnsupportedEncodingException, IOException{
        /**
         * 連上服務器後輸入暱稱
         * 客戶端設置暱稱----必須根據服務器端返回的是否能使用來設置
         * 發送聊天內容----聊天內容需要線程
         */
        Scanner scanner = new Scanner(System.in);
        setName(scanner);

        /**
         * 聊天內容需要線程代理----專門讀取羣聊和私聊信息
         */
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new ListenerClient());

        /**
         * 看到信息後,在發送信息給對方
         */
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),"UTF-8"),true);
        while(true){
            pw.println(scanner.nextLine());
        }
    }

    public void setName(Scanner scanner) throws UnsupportedEncodingException, IOException{
        String name;
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),"UTF-8"),true);

        while(true){
            System.out.println("請輸入你的暱稱:");
            name = scanner.nextLine();
            if(name.trim().length()==0){
                System.out.println("暱稱不能爲空");
            }else {
                pw.println(name);//把暱稱發給服務器端
                String pass = br.readLine();//得到"FAIL"或"OK"
                if(pass != null && (!pass.equals("OK"))){
                    System.out.println("暱稱已被佔用");
                }else {
                    System.out.println("暱稱"+name+"已設置成功,可以使用了");
                    break;
                }
            }
        }
    }

    /**
     * 專門監聽服務器端返回的信息
     * 
     * @author asus
     *
     */
    class ListenerClient implements Runnable{

        @Override
        public void run() {

            try {
                BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));
                String message = null;
                while((message = br.readLine()) != null){
                    System.out.println(message);
                }
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }       

        }

    }

    public static void main(String[] args) throws UnknownHostException, IOException {
          new TCPClient();
    }

}

請關注“知了堂學習社區”,地址:http://www.zhiliaotang.com/portal.php

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章