本文实例为大家分享了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();
        }
    }
}

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