Java实现多线程聊天室

本文实例为大家分享了Java实现多线程聊天室的具体代码,供大家参考,具体内容如下

用多线程来实现,功能会比单线程聊天室更加齐全,也更人性化一点。

多线程版本的聊天室

1. 功能分析: 实现用户注册,上线,下线 实现群聊和私聊 统计当前在线人数 2. 服务端实现

1.维护所有的在线用户

2.注册功能:客户端名称,添加到服务器的客户端集合里

3.群聊功能:客户端发送消息,所有的客户端都能接收到

4.私聊功能:客户端与指定客户端进发送和接收消息

5.退出功能: 从服务器客户端集合中移除客户端

3. 客户端实现

1.注册功能:创建Socket对象,给服务器发送注册执行(消息)

2.群聊功能:客户端发送和接收数据

3.私聊功能:客户端指定客户端(用户),发送和接收数据

4.退出功能:给服务器发送退出指令(消息)

5.命令行的交互式输入输出

4.实现思路:

首先,要实现服务端与客户端之间的连接

这里是使用套接字建立TCP连接:

(1)服务器端先实例化一个描述服务器端口号的ServerSocket对象

(2)客户端要创建Socket对象来连接指定的服务器端

(3)服务器端调用ServerSocket类的accept()方法来监听连接到服务器端的客户端信息

(4)若服务器端与客户端连接成功,双方将返回一个Socket对象,此时双方可以进行通信

(5)服务器端与客户端使用I/O流进行连接,服务端的输出流连接客户端的输入流,客户端的输出流连接服务端的输入流

(6)使用close()方法关闭套接字(一定要记得关闭)

2.因为是拥有一个服务端来实现多个客户端的连接,此处还要解决的是多线程的问题。

每个客户端需要两个线程,来分别处理向服务端发送消息和向服务端接收消息

而服务端,当每增加一个客户端与服务端连接,服务端都要多创建一个线程来处理与客户端的连接

5. 图解析

6.服务端代码实现

Server类

package test.Server; import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; /** * package:test.Server * Description:服务器端 * @date:2019/8/14 * @Author:weiwei **/public class server {    public static void main(String[] args) {        try {            int port = 6666;             ServerSocket serverSocket = new ServerSocket(port);             System.out.println("服务器启动..." + serverSocket.getLocalSocketAddress());  //服务器启动,打印本地地址             //线程池            ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);             while (true) {  //死循环                Socket client = serverSocket.accept();                System.out.println("有客户端连接到服务器:" + client.getRemoteSocketAddress());                executorService.execute(new HandlerClient(client));            }        } catch (IOException e) {            e.printStackTrace();        }    }}

HandlerClient类

package test.Server; import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.net.Socket;import java.util.Map;import java.util.Scanner;import java.util.concurrent.ConcurrentHashMap;  /** * Author:weiwei * description:HandlerClient * Creat:2019/3/12 **/public class HandlerClient implements Runnable {     /**     * 维护所有的连接到服务端的客户端对象     */    private static final Map<String,Socket> ONLINE_CLIENT_MAP =            new ConcurrentHashMap<String, Socket>();  //静态是为了不让对象变化,final不让对象被修改,ConcurrentHashMap是线程安全的类                                        //static final修饰后变量名应该用常量--大写字母加下划线分隔    private final Socket client;    public HandlerClient(Socket client) {  //HandlerClient在多线程环境下调用,所以会产生资源竞争,用一个并发的HashMap        this.client = client;          //为了防止变量被修改,用final修饰    }     //@Override    public void run() {        try {            InputStream clientInput=client.getInputStream(); //获取客户端的数据流            Scanner scanner = new Scanner(clientInput); //字节流转字符流             /**             *消息是按行读取             * 1.register:<username> 例如: register:张三             * 2.群聊: groupChat:<message> 例如:groupChat:大家好             * 3.私聊: privateChat:张三:你好,还钱             * 4.退出:bye             */             while(true){                String data = scanner.nextLine();  //读数据,按行读                if(data.startsWith("register:")){                    //注册                    String userName = data.split(":")[1];//冒号分隔,取第一个                    register(userName);                    continue;                }                 if(data.startsWith("groupChat:")){                    String message = data.split(":")[1];                    groupChat(message);                    continue;                }                 if(data.startsWith("privateChat:")){                    String [] segments = data.split(":");                    String targetUserName = segments[1].split("\\-")[0]; //取目标用户名                    String message = segments[1].split("\\-")[1];   //因为要取两次,所以用数组 //取发送的消息内容                    privateChat(targetUserName,message);                    continue;                }                 if(data.equals("bye")){                    //表示退出                    bye();                    continue;                }            }        } catch (IOException e) {            e.printStackTrace();        }    }     /**     * 当前客户端退出     */    private void bye() {        for(Map.Entry<String,Socket> entry : ONLINE_CLIENT_MAP.entrySet()){            Socket target = entry.getValue();            if(target.equals(this.client)){   //在在线用户中找到自己并且移除                ONLINE_CLIENT_MAP.remove(entry.getKey());                break;            }            System.out.println(getCurrentUserName()+"退出聊天室");        }        printOnlineClient();//打印当前用户    }     private String getCurrentUserName(){        for (Map.Entry<String, Socket> entry : ONLINE_CLIENT_MAP.entrySet()) {            Socket target = entry.getValue(); //getvalue得到Socket对象            if(target.equals(this.client)){ //排除群聊的时候自己给自己发消息的情况                return entry.getKey();            }        }        return "";    }     /**     * 私聊,给targetUserName发送message消息     * @param targetUserName     * @param message     */    private void privateChat(String targetUserName, String message) {        Socket target = ONLINE_CLIENT_MAP.get(targetUserName);//获取目标用户名        if(target == null){            this.sendMessage(this.client,"没有这个人"+targetUserName,false);        }else{            this.sendMessage(target,message,true);        }    }     /**     * 群聊,发送message     * @param message     */    private void groupChat(String message) {        for (Map.Entry<String, Socket> entery : ONLINE_CLIENT_MAP.entrySet()) {            Socket target = entery.getValue(); //getvalue得到Socket对象            if(target.equals(this.client)){                continue;            //排除群聊的时候自己给自己发消息的情况            }            this.sendMessage(target,message,true);        }    }     /**     * 以userName为key注册当前用户(Socket client)     * @param userName     */    private void register(String userName) {        if(ONLINE_CLIENT_MAP.containsKey(userName)){            this.sendMessage(this.client,"您已经注册过了,无需重复注册",false);        }else{            ONLINE_CLIENT_MAP.put(userName,this.client);            printOnlineClient();            this.sendMessage(this.client,"恭喜"+userName+"注册成功\n",false);        }    }     private void sendMessage(Socket target,String message,boolean prefix){        OutputStream clientOutput = null;      //value是每一个客户端        try {            clientOutput = target.getOutputStream();            OutputStreamWriter writer = new OutputStreamWriter(clientOutput);            if(prefix) {                String currentUserName = this.getCurrentUserName();                writer.write("<" + currentUserName + "说:>" + message + "\n");            }else{                writer.write( message + "\n");            }            writer.flush();        } catch (IOException e) {            e.printStackTrace();        }    }    /**     * 打印在线客户端     */    private void printOnlineClient(){        System.out.println("当前在线人数:"+ONLINE_CLIENT_MAP.size()+","+"用户名如下列表:");        for(String userName : ONLINE_CLIENT_MAP.keySet()){  //Map的key为用户名            System.out.println(userName);        }    }}

7. 客户端代码实现

Client类

package Cilent; import java.io.IOException;import java.net.Socket; /** * package:Cilent * Description:客户端 * @date:2019/8/14 * @Author:weiwei **/public class cilent {    public static void main(String[] args) {        try {            //读取地址            String host = "127.0.0.1";            //读取端口号            int port = 6666;             Socket client = new Socket(host,port); //先写数据再读数据,读写线程分离            new ReadDataFromServerThread(client).start();//启动读线程            new WriteDataToServerThread(client).start();//启动写线程        } catch (IOException e) {            e.printStackTrace();        }    }}

WriteDateToServer类

package Cilent; import java.io.IOException;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.net.Socket;import java.util.Scanner; /** * Author:weiwei * description:客户端给服务端发送数据的线程 * 发送的数据来自命令行的交互式输入 * Creat:2019/3/12 **/public class WriteDataToServerThread extends Thread{    private final Socket client;    public WriteDataToServerThread(Socket client){        this.client = client;    }    @Override    public void run(){        try {            OutputStream clientOutput = this.client.getOutputStream();            OutputStreamWriter writer = new OutputStreamWriter(clientOutput);            Scanner scanner = new Scanner(System.in);  //有客户端输入数据            while(true){                System.out.print("请输入>>");                String data = scanner.nextLine(); //读数据                writer.write(data+"\n");                writer.flush();                if(data.equals("bye")){                    System.out.println("您已下线...");                    break;                }            }            this.client.close();        } catch (IOException e) {           // e.printStackTrace();        }    }}

ReadDateFromServer类

package Cilent; import java.io.IOException;import java.io.InputStream;import java.net.Socket;import java.util.Scanner; /** * Author:weiwei * description:客户端从服务端读取数据的线程 * Creat:2019/3/12 **/public class ReadDataFromServerThread extends Thread {    private final Socket client;    public ReadDataFromServerThread(Socket client){        this.client=client;    }     @Override    public void run(){        try {            InputStream clientInput = this.client.getInputStream();            Scanner scanner = new Scanner(clientInput);            while(true){                String data = scanner.nextLine();//按行读数据                System.out.println("来自服务端消息:"+data);            }        } catch (IOException e) {            e.printStackTrace();        }    }}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

君子无故,玉不去身。

Java实现多线程聊天室

相关文章:

你感兴趣的文章:

标签云: