1. 程式人生 > >Java學習筆記之--------網路程式設計之Socket通訊----聊天室實現

Java學習筆記之--------網路程式設計之Socket通訊----聊天室實現

Socket通訊

網路上的兩個程式通過一個雙向的通訊連線實現資料的交換,這個連線的一端稱為一個socket。基於TCP/IP協議,建立穩定的點對點的通訊。

特點:實時、快速、安全性高、佔用系統資源多、效率低。

通常也稱作"套接字",套接字是一種程序間的資料交換機制。這些程序既可以在同一機器上,也可以在通過網路連線的不同機器上。換句話說,套接字起到通訊端點的作用。單個套接字是一個端點,而一對套接字則構成一個雙向通訊通道,使非關聯程序可以在本地或通過網路進行資料交換。一旦建立套接字連線,資料即可在相同或不同的系統中雙向或單向傳送,直到其中一個端點關閉連線。

“請求----響應”模式

客戶端:在網路通訊中,第一次主動發起通訊的程式被稱作客戶端(client)程式。

伺服器:第一次通訊中等待連線的程式被稱作伺服器端(server)程式。

 

這裡模擬了一個聊天室的簡單的聊天功能:

首先我們先準備一個關閉流的工具類CloseUtil,以後流的關閉都可以通過呼叫這個方法來實現:

public class CloseUtil {
    public static void closeAll(Closeable... io){
        for (Closeable temp : io) {
            try {
                if (null != temp){
                    temp.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

然後我們實現資料的接收和傳送:

public class Send implements Runnable{
    //控制檯輸入流
    private BufferedReader console;
    //管道輸出流
    private DataOutputStream dos;
    //控制執行緒標識
    private boolean isRunning = true;
    //名稱
    private String name;

    public Send() {
        console = new BufferedReader(new InputStreamReader(System.in));
    }
    public Send(Socket client, String name){
        this();
        try {
            dos = new DataOutputStream(client.getOutputStream());
            this.name = name;
            send(this.name);
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.closeAll(dos,console);
        }
    }

    /**
     * 1.從控制檯接收資料
     * @return
     */
    private String getMsgFromConsole(){
        try {
            return console.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 2.傳送資料
     */
    public void send(String msg){
        try {
            if (null != msg && !msg.equals("")){
                dos.writeUTF(msg);
                dos.flush();//強制重新整理
            }
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.closeAll(dos,console);
        }
    }

    @Override
    public void run() {
        while (isRunning){
            send(getMsgFromConsole());
        }
    }
}
public class Receive implements Runnable{
    //輸入流
    private DataInputStream dis;
    //執行緒標識
    private boolean isRunning = true;

    public Receive() {
    }

    public Receive(Socket client) {
        try {
            dis = new DataInputStream(client.getInputStream());
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.closeAll(dis);
        }
    }

    /**
     * 接收資料
     * @return
     */
    public String receive(){
        String msg = "";
        try {
            msg = dis.readUTF();
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.closeAll(dis);
        }
        return msg;
    }

    @Override
    public void run() {
        //執行緒體
        while (isRunning){
            System.out.println(receive());
        }
    }
}

最後是我們模擬客戶端和伺服器的實現:

public class Server {
    private List<MyChannel> all = new ArrayList<MyChannel>();

    public static void main(String[] args) throws IOException {
        new Server().start();
    }

    public void start() throws IOException {
        ServerSocket server = new ServerSocket(9999);
        while (true){
            Socket client = server.accept();
            MyChannel channel = new MyChannel(client);
            all.add(channel);//統一管理
            new Thread(channel).start();//一條道路
        }
    }

    /**
     * 一個客戶端一條道路
     * 1.輸入流
     * 2.輸出流
     * 3.接收資料
     * 4.傳送資料
     */
    class MyChannel implements Runnable{
        private DataInputStream dis;
        private DataOutputStream dos;
        private String name;
        //執行緒標識
        private boolean isRunning = true;

        public MyChannel(Socket client) {
            try {
                dis = new DataInputStream(client.getInputStream());
                dos = new DataOutputStream(client.getOutputStream());
                this.name = dis.readUTF();
                this.send("歡迎進入聊天室");
                this.sendOthers(this.name + "進入了聊天室", true);
            } catch (IOException e) {
                CloseUtil.closeAll(dis,dos);
                isRunning = false;
            }
        }

        /**
         * 讀取資料
         * @return
         */
        private String receive(){
            String msg = "";
            try {
                msg = dis.readUTF();
            } catch (IOException e) {
                CloseUtil.closeAll(dis);
                isRunning = false;
                all.remove(this);//移除自身
            }
            return msg;
        }

        /**
         * 傳送資料
         * @return
         */
        private void send(String msg){
            if (null==msg || msg.equals("")){
                return;
            }
            try {
                dos.writeUTF(msg);
                dos.flush();
            } catch (IOException e) {
                CloseUtil.closeAll(dos);
                isRunning = false;
                all.remove(this);//移除自身
            }
        }

        /**
         * 傳送給其他的客戶端
         */
        private void sendOthers(String msg, boolean sys){
            //是否為私聊
            if (msg.startsWith("@") && msg.indexOf(":")>-1){ //約定私聊以@開始
                //獲取name
                String name = msg.substring(1, msg.indexOf(":"));
                String content = msg.substring(msg.indexOf(":") + 1);
                for (MyChannel other : all) {
                    if (other.name.equals(name)){
                        other.send(this.name + "對您悄悄的說:" + content);
                    }
                }
            } else {
                //遍歷容器
                for (MyChannel other : all) {
                    if (other == this){
                        continue;
                    }
                    if (sys){
                        //系統訊息
                        other.send("系統資訊:" + msg);
                    } else {
                        //傳送給其他客戶端
                        other.send(this.name + "對所有人說:" + msg);
                    }
                }
            }

        }

        @Override
        public void run() {
            while (isRunning){
                sendOthers(receive(), false);
            }
        }
    }
}
public class Client {
    public static void main(String[] args) throws IOException {
        System.out.println("請輸入名稱:");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String name = br.readLine();
        if (name.equals("")){
            return;
        }
        Socket client = new Socket("localhost",9999);
        new Thread(new Send(client, name)).start();//一條路徑
        new Thread(new Receive(client)).start();//一條路徑
    }
}

下面是我們的執行結果,先啟動Server。然後啟動Client,為第一個程序命名為a;然後再啟動一個程序,命名為b;最後再啟動一個程序並命名為c。在c程序中輸入“大家好”,則a程序和b程序都可以收到這條訊息,在c程序中輸入“@a:小a同學,你好。”,則我們只能在a程序中收到這條訊息。並且在b和c啟動的時候,會有系統訊息提示,b或者c進入聊天室。截圖如下,依次為abc三個程序的截圖:

以上就是模擬簡單的群聊功能的實現。還可以在結束程序的時候新增系統訊息提示某程序離開聊天室,只要對以上程式碼稍作修改即可。這裡沒有實現。

以上為尚學堂Java300集教學視訊中裴新老師所教授的網路程式設計相關課程的筆記。