欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java Socket模擬實(shí)現(xiàn)聊天室

 更新時(shí)間:2021年07月06日 08:12:17   作者:我喜歡煎蛋卷  
這篇文章主要為大家詳細(xì)介紹了Java Socket模擬實(shí)現(xiàn)聊天室,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

使用Java Socket模擬實(shí)現(xiàn)了一個(gè)聊天室,實(shí)現(xiàn)了基本的私聊以及群聊。分為服務(wù)器端和客戶(hù)端,下面我來(lái)介紹一下實(shí)現(xiàn)的步驟。

服務(wù)器端

服務(wù)器端是聊天室的核心所在,主要用來(lái)處理客戶(hù)端的請(qǐng)求,先來(lái)看一下服務(wù)器端的主方法:

public static void main(String[] args) {
        try {
            ExecutorService executorService = Executors.newFixedThreadPool(100);//最多容納100個(gè)客戶(hù)端聊天
            ServerSocket serverSocket = new ServerSocket(6655);//監(jiān)聽(tīng)6655號(hào)端口
            for (int i = 0; i < 100; i++) {
                Socket client = serverSocket.accept();
                System.out.println("有新的用戶(hù)連接 " + client.getInetAddress() +
                        client.getPort());
                executorService.execute(new ExecuteClientThread(client));
            }
            executorService.shutdown();
            serverSocket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

首先我創(chuàng)建了一個(gè)固定大小為100的線(xiàn)程池,這個(gè)聊天室的實(shí)現(xiàn)是一個(gè)服務(wù)器線(xiàn)程對(duì)應(yīng)一個(gè)客戶(hù)端線(xiàn)程的,就是說(shuō)線(xiàn)程池的大小就是最大的同時(shí)聊天的人數(shù)。服務(wù)器的執(zhí)行順序是這樣的:

1.監(jiān)聽(tīng)端口,等待客戶(hù)端連接

2.如果有客戶(hù)端連接到監(jiān)聽(tīng)的端口,那么通過(guò)accept()方法返回該客戶(hù)端的Socket,并且在線(xiàn)程池中啟動(dòng)一個(gè)新的服務(wù)器線(xiàn)程用來(lái)與剛剛連接的客戶(hù)端"溝通"。

3.把接收到的客戶(hù)端的Socket構(gòu)造注入新啟動(dòng)的服務(wù)器線(xiàn)程中,這樣服務(wù)器線(xiàn)程就可以獲取到客戶(hù)端對(duì)應(yīng)的流。

到這里,服務(wù)器已經(jīng)和客戶(hù)端連接成功了,我們現(xiàn)在來(lái)看一下服務(wù)器線(xiàn)程是如何處理客戶(hù)端的請(qǐng)求的,先上一段服務(wù)器代碼

private static Map<String, Socket> clientMap = new ConcurrentHashMap<>();//存儲(chǔ)所有的用戶(hù)信息
 
    static class ExecuteClientThread implements Runnable {
        private Socket client;//每一個(gè)服務(wù)器線(xiàn)程對(duì)應(yīng)一個(gè)客戶(hù)端線(xiàn)程
        ExecuteClientThread(Socket client) {
            this.client = client;
        }
......

代碼的第一行,創(chuàng)建了一個(gè)ConcurrentHashmap,這個(gè)map不是某個(gè)線(xiàn)程中的,而是服務(wù)器的static屬性,用來(lái)存儲(chǔ)所有客戶(hù)端的信息。因?yàn)榭蛻?hù)端是有姓名,有Socket的,所以采用K-value的模式來(lái)存儲(chǔ),用戶(hù)名作為Key??紤]到線(xiàn)程安全的原因,采用了ConcurrentHashmap,保證了線(xiàn)程安全。

接下來(lái)就是剛剛構(gòu)造注入的、連接的客戶(hù)端的Socket了,我們可以通過(guò)這個(gè)Socket獲取到輸入和輸出流。

然后就是服務(wù)器的線(xiàn)程執(zhí)行的run方法了,具體的就直接看代碼把。都有注釋?zhuān)筒灰灰唤忉屃耍韵率撬蟹?wù)器端的代碼

import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
 
 
public class Main {
 
 
    private static Map<String, Socket> clientMap = new ConcurrentHashMap<>();//存儲(chǔ)所有的用戶(hù)信息
 
    static class ExecuteClientThread implements Runnable {
        private Socket client;//每一個(gè)服務(wù)器線(xiàn)程對(duì)應(yīng)一個(gè)客戶(hù)端線(xiàn)程
        ExecuteClientThread(Socket client) {
            this.client = client;
        }
 
        @Override
        public void run() {
            boolean Flag = true;//防止一個(gè)客戶(hù)端多次注冊(cè)所做的標(biāo)記位置
            try {
                PrintStream PrintToCilent = new PrintStream(client.getOutputStream());//服務(wù)器向用戶(hù)輸出一些提示信息
 
 
                Scanner scanner = new Scanner(client.getInputStream());
                String str = null;//用戶(hù)外部的輸入信息
                while (true) {
                    if (scanner.hasNext()) {
                        str = scanner.next();//外部的用戶(hù)輸出
 
                        Pattern pattern = Pattern.compile("\r");//排除特殊符號(hào)
                        Matcher matcher = pattern.matcher(str);
                        str = matcher.replaceAll("");
 
                        if (str.startsWith("userName")) {
                            String userName = str.split(":")[1];
                            userRegist(userName, client, Flag);
                            Flag = false;
                        }
                        // 群聊流程
                        else if (str.startsWith("G:")) {
                            PrintToCilent.println("已進(jìn)入群聊模式!");
                            groupChat(scanner,client);
                        }
                        // 私聊流程
                        else if (str.startsWith("P")) {//模式
                            String userName = str.split("-")[1];
                            PrintToCilent.println("已經(jīng)進(jìn)入與"+userName+"的私聊");
 
                            privateChat(scanner,userName);
                        }
                        // 用戶(hù)退出
                        else if (str.contains("byebye")) {
                            String userName = null;
                            for (String getKey:clientMap.keySet()) {
                                if (clientMap.get(getKey).equals(client)) {
                                    userName = getKey;
                                }
                            }
 
                            System.out.println("用戶(hù)"+userName+"下線(xiàn)了..");
                            clientMap.remove(userName);//將此實(shí)例從map中移除
                        }
                    }
                }
 
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
       
        private void userRegist(String userName, Socket client, boolean Flag) throws IOException {
            PrintStream PrintToCilent = new PrintStream(client.getOutputStream());//服務(wù)器向用戶(hù)輸出一些提示信息
            if(Flag) {
                System.out.println("用戶(hù)" + userName + "上線(xiàn)了!");
 
                clientMap.put(userName, client);//把用戶(hù)加入儲(chǔ)存map
                System.out.println("當(dāng)前群聊人數(shù)為" + (clientMap.size()) + "人");
                PrintToCilent.println("注冊(cè)成功!");
            }else {
                PrintToCilent.println("警告:一個(gè)客戶(hù)端只能注冊(cè)一個(gè)用戶(hù)!");
            }
        }
     
        private void groupChat(Scanner scanner,Socket client) throws IOException {
            // 取出clientMap中所有客戶(hù)端Socket,然后遍歷一遍
            // 分別取得每個(gè)Socket的輸出流向每個(gè)客戶(hù)端輸出
            PrintStream PrintToClient = new PrintStream(client.getOutputStream());//在群聊的時(shí)候服務(wù)器向客戶(hù)端發(fā)送數(shù)據(jù)
            boolean ExitFlag = false;
 
            Set<Map.Entry<String, Socket>> entrySet =
                    clientMap.entrySet();
 
            String userName = null;
            for (Map.Entry<String, Socket> socketEntry : entrySet) {//獲得:是哪個(gè)用戶(hù)說(shuō)的話(huà)
                if (socketEntry.getValue() == client) {
                    userName = socketEntry.getKey();//發(fā)出信息的用戶(hù)
                }
            }
            String msg = null;
 
            while (true) {
                if (scanner.hasNext()) {
                    msg = scanner.next();
                    if("exit".equals(msg)){//如果用戶(hù)退出了
                        for(Map.Entry<String,Socket> stringSocketEntry : entrySet){
                            new PrintStream(stringSocketEntry.getValue().getOutputStream(),true).println("用戶(hù)"+userName+"剛剛退出了群聊??!");//給所有人發(fā)退出群聊的消息
                        }
                        return;
                    }
 
 
                    for (Map.Entry<String, Socket> stringSocketEntry : entrySet) {//遍歷用戶(hù)的map,獲取所有用戶(hù)的Socket
                        try {
                            Socket socket = stringSocketEntry.getValue();
                            PrintStream ps = new PrintStream(socket.getOutputStream(), true);
 
                            ps.println("群聊:用戶(hù)" + userName + "說(shuō): " + msg);//給每個(gè)用戶(hù)發(fā)消息
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
 
                }
            }
 
        }
        private void privateChat(Scanner scanner, String privatepeopleName) throws IOException {
 
            Socket privateUser = clientMap.get(privatepeopleName);
            PrintStream ps = new PrintStream(privateUser.getOutputStream());//拿到私聊對(duì)象的輸出流
            PrintStream PrintToClient = new PrintStream(client.getOutputStream());//拿到當(dāng)前客戶(hù)端的輸出流
            String Message = null;
            String MyName = null;
            Set<Map.Entry<String,Socket>> set = clientMap.entrySet();
            for(Map.Entry<String,Socket> value : set){
                if(value.getValue() == client){
                    MyName = value.getKey();
                    break;
                }
            }
 
            while (true) {
                if(scanner.hasNext()) {
                    Message = scanner.next();
                    if ("exit".equals(Message)){//如果用戶(hù)輸入了退出
                        PrintToClient.println("已退出和"+privatepeopleName+"的私聊");
                        ps.println("對(duì)方已經(jīng)退出了私聊");
                        break;
                    }
                    ps.println(MyName+"說(shuō)"+Message);//如果用戶(hù)沒(méi)有退出,向私聊對(duì)象發(fā)送消息
                }
            }
 
 
        }
 
 
    }
 
    public static void main(String[] args) {
        try {
            ExecutorService executorService = Executors.newFixedThreadPool(100);//最多容納100個(gè)客戶(hù)端聊天
            ServerSocket serverSocket = new ServerSocket(6655);
            for (int i = 0; i < 100; i++) {
                Socket client = serverSocket.accept();
                System.out.println("有新的用戶(hù)連接 " + client.getInetAddress() +
                        client.getPort());
                executorService.execute(new ExecuteClientThread(client));
            }
            executorService.shutdown();
            serverSocket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

然后是客戶(hù)端的代碼,客戶(hù)端的代碼比較簡(jiǎn)單:分為兩個(gè)線(xiàn)程,一個(gè)線(xiàn)程用于接收服務(wù)器的數(shù)據(jù),一個(gè)線(xiàn)程用于向服務(wù)器發(fā)送數(shù)據(jù)。我就直接上代碼了,里面有注釋的。

import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
 
 
class ExcuteServerInPut implements Runnable{//接收服務(wù)器的數(shù)據(jù)
    private Socket ToServer;
 
    ExcuteServerInPut(Socket ToServer){
        this.ToServer = ToServer;
    }
 
    @Override
    public void run() {
        try {
            Scanner scanner = new Scanner(ToServer.getInputStream());
               while (scanner.hasNext()){
                System.out.println(scanner.nextLine());
            }
            scanner.close();
            ToServer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
 
class ExcuteServerOutPut implements Runnable{//向服務(wù)器發(fā)送數(shù)據(jù)
 
    private Socket Socket;
    ExcuteServerOutPut(Socket Socket){
        this.Socket = Socket;
    }
 
    @Override
    public void run() {
        try {
            PrintStream printStream = new PrintStream(Socket.getOutputStream());
            Scanner scanner = new Scanner(System.in);
            scanner.useDelimiter("\n");
            System.out.println("*****************************************");
            System.out.println("***用戶(hù)注冊(cè):useerName:同戶(hù)名(僅限一次)***");
            System.out.println("***進(jìn)入群聊:G:           退出群聊:exit***");
            System.out.println("***私聊:P-用戶(hù)名         退出私聊:exit***");
            System.out.println("***********退出聊天室:byebye*************");
            while (true){
                if(scanner.hasNext()) {
                    String string = scanner.next();
                    printStream.println(string);
                    if ("byebye".equals(string)) {
                        System.out.println("退出!");
                        printStream.close();
                        scanner.close();
                        break;
                    }
                }
 
            }
 
            Socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
 
 
public class Main {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 6655);
        ExcuteServerInPut excuteServerInPut = new ExcuteServerInPut(socket);
        ExcuteServerOutPut excuteServerOutPut = new ExcuteServerOutPut(socket);
        new Thread(excuteServerInPut).start();
        new Thread(excuteServerOutPut).start();
        }
}

后續(xù)我會(huì)做一些改進(jìn),希望可以對(duì)大家有所幫助

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論