多人聊天室(Java)


第1部分 TCP和UDP

TCP:是一種可靠地傳輸協議,是把消息按一個個小包傳遞並確認消息接收成功和正確才發送下一個包,速度相對於UDP慢,但是信息准確安全;常用於一般不要求速度和需要准確發送消息的場合。

UDP:是一種不可靠的傳輸協議,常用於視頻直播、游戲等及時性比較強的場景。

進行相關編程可以查詢API手冊。

 

第2部分 多人聊天室

下面是利用Java的TCP協議的API實現多人聊天室的案例代碼:

服務端:

運行在服務端的SeverSocket主要負責:

 1 向系統申請服務端口號,客戶端是通過這個端口號和IP地址與之連接的;端口號一般取四千之一萬之間,因為其余有的已經綁定系統系統應用和主流應用程序,最常用的便是8088。

 2 監聽申請的服務端口號,當一個客戶端通過該端口號嘗試建立聯系連接時,SeverSocket會在服務端創建一個Socket與客戶端建立連接,服務端正對不同客戶端建立多個Socket,利用多線程實現。

工作模塊及過程:

服務器先初始化,包括申請端口號,和創建共享消息集合(可以用map,key是用戶名,value是消息,私聊返回value值,廣播則遍歷value),另外創建增刪和發送消息的方法;

創建處理客戶端交互的類,繼承runnable,通過socket的getInetAdress和getHostAdress方法獲取客戶端計算機的信息,其start方法用來完成從一個客戶端接收消息,並將其他客戶端的消息返回給該客戶端;

package chat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;


/**
 * 
 * 運行在服務端的SeverSocket主要負責;
 * 1 向系統申請服務端口號客戶端是通過這個端口號與之連接的
 * 2 監聽申請的服務端口號,當一個客戶端通過該端口號嘗試建立聯系連接時,
 *      SeverSocket會在服務端創建一個Socket與客戶端建立連接
 *       服務端正對不同客戶端建立多個Socket
 * 
 * @author KwinWei QQ:885251358
 *
 */
public class Server {
    
    public ServerSocket server;
    /*
     * 用來保存客戶端輸出流的集合,
     *因為線程安全的也不和遍歷互斥,要自己維護也可以保證安全
     */
    private List<PrintWriter> allOut;     //私聊可以用map,key為昵稱,value是對應消息,廣播則遍歷value
    
    /*
     *初始化 服務端
     */
    public Server()throws Exception  {
        /*
         * 初始化的同時申請端口號
         */
        server = new ServerSocket(8788);
        allOut = new ArrayList<PrintWriter>();
    }
    
    /**
     * 將給定的輸出流存入共享集合
     * @param out
     */
    private synchronized void addOut(PrintWriter out) {
        allOut.add(out);
    }
    /**
     * 將給定的輸出流從共享集合中刪除
     * @param out
     */
    private synchronized void removeOut(PrintWriter out) {
        allOut.remove(out);
    }
    /**
     * 將給定的消息發送給所有客戶端
     * @param out
     */
    private synchronized void  sendMessage(String message) {
        for(PrintWriter out : allOut) {
            out.println(message);
        }
    }
    
    /*服務端開始工作的方法
     * 
     */
    public void start() {
        try {
            /*
             * ServerSocket的accept的方法
             * 是一個阻塞的方法,作用是監聽服務端口號,知道一個客戶端;連接並創建一個Socket,使用該Socket
             *     即可與剛才鏈接的客戶端進行交互
             */
            
            while(true) {
                System.out.println("等待客戶端連接...");
                Socket socket = server.accept();
                System.out.println("一個客戶端連接了!");
                
                /*
                 * 啟動一個線程,來完成與該客戶端的交互
                 */
                ClientHandler handler= new ClientHandler(socket);
                Thread t = new Thread(handler);
                t.start();    
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
    public static void main(String[] args) {
        try {
            Server server = new Server();
            server.start();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("服務端建立聯系失敗!");
        }
    }
    
    /**
     *該線程負責處理一個客戶端的交互
     * 
     */
    class ClientHandler implements Runnable{
        /*
         * 該線程處理的客戶端的Socket
         */
        private Socket socket ;
        
        
         // 客戶端的地址信息,區分不同客戶端
        private String host;
        
        //用戶的昵稱
        private String nickName;
        
    
        public ClientHandler(Socket socket) {
            this.socket = socket;
            /*
             * 通過Socket可以獲取遠端計算機的地址信息
             */
            InetAddress address = socket.getInetAddress();
            //獲取IP地址
            host = address.getHostAddress();
        }
        public void run() {
            PrintWriter pw = null;
            try {
                
                /*
                 * Socket 提供的方法
                 * InputStream getInputStream()
                 * 該方法可以獲取一個輸入流,從該方法讀取的數據就是從遠端計算機發送來的 
                 */
                InputStream in = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(in,"UTF-8");
                BufferedReader br = new BufferedReader(isr);
                
                //首先讀取一行字符串為昵稱
                nickName =  br.readLine();
                //System.out.println(host+"上線了");
                sendMessage(nickName+"上線了");
                
                /*
                 * 通過Socket創建輸出流用於將消息發送給客戶端
                 */
                OutputStream out = socket.getOutputStream();
                OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
                 pw = new PrintWriter(osw,true);
                
                /*
                 * 將該客戶端對的輸出流存入到共享集合中
                 */
                addOut(pw);
                
                
                
                String message = null;
                /*
                 * br.readLine()在讀取客戶戶端發送過來的消息時,由於客戶端斷線,
                 * 而操作系統的不同,這里讀取后的結果不同:
                 * 當windows的客戶端斷開時:br.readLine會拋出異常
                 * 當linux的客戶端斷開時:br.readLine 會返回null
                 */
                
                while((message = br.readLine())!=null) {
                    //System.out.println(host+"說:"+ message);
                    //pw.println(host+"說:"+message);
                    //廣播消息
                    sendMessage(nickName+"說:"+message);
                } 
                
            } catch (Exception e) {
                
                
            }finally {
                /*
                 * 處理當前客戶端斷開后的邏輯
                 */
                //將該客戶端的輸出流從共享集合中刪除
                removeOut(pw);
                //System.out.println(host+"下線了");
                sendMessage(nickName+"下線了");
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }    
        }
        
    }
    
}
View Code

客戶端:

package chat;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * 
 * @author KwinWei QQ:885251358
 *
 */
public class Client {
    /*
     * 套接字
     * 
     * 理解為電話,撥號,建立聯系,麥克風說話,
     * socket輸入和輸出流,
     * 你輸入流對應他輸出流,你說話他聽
     * 你輸出流對應他輸入流
     * 
     * java,net.Socket;
     * 封裝了TCP協議,使用它就可以基於某種TCP協議進行網絡通信
     * Socket試運行在客戶端的
     */
    private Socket socket;
    
    public Client() throws Exception {
        /*
         *實例化Socket的時候需要傳入兩個參數
         * 1 IP:127.0.0.1或localhost本機;
         * 2 端口:0-65535,聽服務端的,區用網程序,
         *    一般使用四千以上一萬以下,前四千被占用綁定主流應用程序等    
         * 通過Ip可以找到服務的那台計算機,通過端口號可以找到服務端計算機上服務端用程序
         * 
         * 實例化Socket的過程就是連接的過程,若遠端計算機沒有響應會拋出異常
         *  
         */
        System.out.println("正在連接服務端...");
        socket = new Socket("localhost",8788);
        System.out.println("已經和服務端建立聯系");
    }
    
    /*
     * 啟動客戶端的方法
     */
    public void start() {
        try {
            Scanner scanner = new Scanner(System.in);
            /*
             * 先要求用戶創建一個昵稱
             */
            //System.out.println("");
            String nickName = null;
            while(true) {
                System.out.println("請用戶輸入用戶名");
                nickName = scanner.nextLine();
                if(nickName.length()>0) {
                    break;
                }
                System.out.println("輸入有誤");
            }
            System.out.println("歡迎你"+nickName+"!開始聊天吧!");
            /*socket 提供的方法:
             * OutputStream getOutStream
             * 獲取一個字節輸出流,通過該流寫出的數據會被發送至遠端計算機。
             */
            OutputStream out = socket.getOutputStream();
            OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
            PrintWriter pw =  new  PrintWriter(osw,true);  //不能指明字節流,所以要加一個轉換流,第二參數為true會行刷新
            
            //先將昵稱發給服務器
            
            pw.println(nickName);
            
            /*
             * 啟動讀取服務端發送過來消息的線程
             */
            ServerHandler  handler = new ServerHandler();
            Thread t = new Thread(handler);
            t.start();
            
            /*
             * 將字符串發送至服務端,加上行刷新,說一句發一句
             * 否則需要用  flush()強制寫出
             */
            while(true) {
                pw.println(scanner.nextLine());
                //pw.flush();
                
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
    
    
    public static void main(String[] args) {
        try {
            Client client = new Client();
            client.start();
        }catch(Exception e) {
            e.printStackTrace();
            System.out.println("客戶端啟動失敗!");
        }
    }

    /*
     * 該線程用來讀取服務器端發送來的消息,並輸出到客戶端控制台顯示
     */
    class ServerHandler implements Runnable {
        public void run() {
            try {
                InputStream in = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(in,"UTF-8");
                BufferedReader br = new BufferedReader(isr);
                
                String message = null;
                while ((message = br.readLine())!=null) {
                    System.out.println(message);
                }
                
            } catch (Exception e) {
                
                
            }
        }
    }
    
    
}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM