Java使用TCP实现聊天室系统

目标功能:
1.群聊
2.退出
3.私聊
4.查询其他在线玩家
5.管理员登陆
6.踢人
7.禁言
目前已实现四个功能
如何使用?
1 先启动服务端程序,再启动客户端
2 输入昵称后才能连接服务端
3 输入内容后回车即可发送群聊消息
3 输入 ls 查询在线玩家
4 输入 @要私聊对象的名字:要私聊的信息 即可实现私聊
5 输入 exit 即可退出聊天室

服务器

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class Server {
    //用该容器存储与用户建立的连接
    static CopyOnWriteArrayList<Channel> all = new CopyOnWriteArrayList<>();
    List<String> list = new ArrayList();
    //以下两个常量用来区分 系统和用户
    private final static boolean system = true;
    private final static boolean user = false;

    public static void main(String[] args) throws IOException {
        //创建服务端
        ServerSocket server = new ServerSocket(8888);
        System.out.println("服务端建立完毕,端口号:" + server.getLocalPort());
        //准备连接
        Channel c;
        //接受客户端
        while (true){
            //监听到连接后,为该客户端开辟一个线程
            Socket client = server.accept();
            c = new Channel(client);
            new Thread(c).start();
            //加入到容器中
            all.add(c);
            System.out.println("玩家:"+c.name+ " 已连接上...");
        }
    }
    //通信管道,一个用户对应一个管道(内部类),封装了流和用户信息
    static class Channel implements Runnable{
        private Socket client;
        private DataInputStream dis;
        private DataOutputStream dos;
        private boolean isRunning;

        private String name;

        //此构造方法用来初始化流和用户昵称
        public Channel(Socket client){
            this.client = client;
            try {
                dis = new DataInputStream(client.getInputStream());
                dos = new DataOutputStream(client.getOutputStream());
                //默认处于连接状态,用于收发信息
                isRunning = true;
                //初始化姓名
                name = dis.readUTF();
                //+1为包括自身
                this.send("欢迎进入聊天室,当前在线玩家数: "+(all.size()+1)+"\r\n"+
                        "指令:ls(查询当前在线用户),@XX:YY(向玩家XX发送消息YY),exit(退出聊天室)");
            }catch (IOException e) {
                System.out.println("初始化失败");
                //释放资源
                release();
            }
        }
        //run方法循环转发用户消息
        @Override
        public void run() {
            while (isRunning){
                String msg = receive(client);
                if(!msg.equals("")){
                    if (msg.equals("exit")){//断开连接
                        System.out.println("玩家:"+this.name+" 已断开连接");
                        sendOthers(name+"退出了群聊",system);//告诉所有人 此人离开了
                        release();
                        break;
                    }else if (msg.startsWith("@")){//私聊
                        int index = msg.indexOf(":");
                        if (index!= -1){
                            String targetName = msg.substring(1,index);
                            sendTarget(msg,targetName);
                        }else {
                            sendTarget(msg,"error");
                        }
                    }else if (msg.equals("ls")){//查询在线玩家
                        StringBuilder sb = searchOnline();
                        if (!sb.equals("]")) {
                            this.send("当前在线的玩家:"+sb.toString());
                        }
                    }
                    else {
                        sendOthers(name+"说:"+msg,user);//向所有人广播此消息,除了他自己
                    }
                }
            }
        }

        //接受信息
        public String receive(Socket client) {
            String msg = "";
            try {
                //阻塞,只有用户发送了信息,服务器才会接受并广播给其他用户
                msg = dis.readUTF();
            } catch (IOException e) {
                System.out.println("玩家:"+this.name+" 异常中断");
                //非输入exit退出,同样需要广播此用户的退出
                sendOthers(name+"退出了群聊",system);//告诉所有人 此人离开了
                //客户端强制退出时,释放资源
                release();
            }
            return msg;
        }

        //广播的最底层操作是调用Channel中的send方法
        public void send(String msg){
            try {
                dos.writeUTF(msg);
                dos.flush();
            }catch (IOException e){
                System.out.println("信息传出异常,连接已断开");
                release();
            }
        }

        //广播,调用时确认是系统还是用户
        public void sendOthers(String msg,boolean who){
            if (msg!=""){
                for (Channel c:all){
                    //任何消息不回反馈给发送者本身
                    if (c!=this) {
                        if (who == system) {
                            c.send("***系统消息: " + msg + "***");
                        } else {//用户
                            c.send(msg);
                        }
                    }
                }
            }
        }

        //私聊
        public void sendTarget(String msg,String name){
            boolean flag = true;
            for (Channel c:all){
                if (c.name.equals(name)){
                    c.send(this.name+"悄悄地对你说:"+msg.substring(msg.indexOf(":")+1));
                    flag = false;
                    break;
                }
            }
            if (flag){
                System.out.println(flag);
                if (name.equals("error")){
                    this.send("指令错误,请输入英文状态下的冒号");
                }else {
                    this.send("***找不到该用户***");
                }
            }
        }

        //查询其他在线的玩家
        public StringBuilder searchOnline(){
            StringBuilder sb = new StringBuilder("[");
            for (Channel c:all){
                sb.append(c.name+",");
            }
            sb.setCharAt(sb.length()-1,']');
            return sb;
        }

        //释放资源时从容器中剔除自身的引用(此Channel因各种原因断开),并关闭转发器(停止run方法中的while循环)
        private void release(){
            this.isRunning = false;
            all.remove(this);
            System.out.println("剩余玩家"+all.size());
            TianUtils.close(dis,dos,client);
        }
    }
}

客户端

import java.io.*;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    public static void main(String[] args) throws IOException {
        //输入昵称后,再向服务器建立连接
        System.out.print("请输入昵称:");
        String name = new Scanner(System.in).nextLine();
        //创建客户端
        Socket client = new Socket("localhost",8888);
        /**
         * 用户应该能同时收发消息,所以需要用到多线程
         * Sender为发送者线程
         * Receiver为接收者线程
         */
        new Thread(new Sender(client,name)).start();
        new Thread(new Receiver(client)).start();
    }
}

客户端子线程:发送者

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class Sender implements Runnable{
    private Socket client;
    private DataOutputStream dos;
    private BufferedReader reader;

    //初始化流,并向服务器传递用户昵称,服务器在初始化链接(Channel)时接收昵称
    public Sender(Socket client,String name){
        this.client = client;
        try {
            dos = new DataOutputStream(client.getOutputStream());
            reader = new BufferedReader(new InputStreamReader(System.in));
            dos.writeUTF(name);
        } catch (IOException e) {
            System.out.println("初始化流失败");
            release();
        }
    }

    @Override
    public void run() {
        while (true){
            try {
                String msg = reader.readLine();
                if (msg.equals("exit")){
                    dos.writeUTF(msg);//输入exit主动断开连接
                    dos.flush();
                    break;
                }
                dos.writeUTF(msg);
                dos.flush();
            } catch (IOException e) {
                System.out.println("发送异常");
                release();
            }
        }
    }

    //释放资源
    private void release(){
        TianUtils.close(client,dos,reader);
    }
}

客户端子线程:接收者

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

public class Receiver implements Runnable{
    private Socket client;
    private DataInputStream dis = null;

    //初始化流,用来接收服务器广播的消息
    public Receiver(Socket client){
        this.client = client;
        try {
            dis = new DataInputStream(client.getInputStream());
        } catch (IOException e) {
            System.out.println("初始化失败");
            release();
        }
    }
    @Override
    public void run() {
        while (true){
            try {
                //当输入exit时,此操作异常,run方法终止
                System.out.println(dis.readUTF());
            } catch (IOException e) {
                System.out.println("与服务器断开连接");
                break;
            }
        }
        release();
    }

    //释放资源
    private void release(){
        TianUtils.close(client,dis);
    }
}
发布了15 篇原创文章 · 获赞 9 · 访问量 693

猜你喜欢

转载自blog.csdn.net/weixin_43544077/article/details/102601968