前言

今天带来的是利用JAVA制作一个简易无GUI的纯控制台聊天室。 实现原理:简易TCP网络编程实现客户端与服务器的交流,IO流读写数据,多线程实现多人交流。

具体用途

(××为指定用户或管理员名字) ①普通用户功能:可以聊天室群聊,一对一的私聊(使用@××>); ②管理员功能:可以利用#××进行踢人,*广告*发送广告,*广告>修改默认的广告内容

步骤

① 首先是实现登陆验证的服务器端

用的是java.net包里面的ServerSocket创建服务器端口,且设定了管理员特定密码,以及聊天室的普通用户的通用密码 需要开启多线程 一个线程应付一个客户端

package cn.Himit_ZH.ServerLogin;

import cn.Himit_ZH.ServerLogin.CloseUtils;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;

/**
 * 名字:登陆验证服务器
 * 用途:实现验证登陆,判断是否为管理员。
 * 步骤:
 * 创建服务器
 * 1、指定端口 使用ServerSocket创建服务器
 * 2、阻塞式等待连接 accept
 * 3、操作: 输入输出流操作
 * 4、释放资源
 * IO流实现数据的对接
 * 
 * @author Himit_ZH
 *
 */
public class ChatRoomLoginServer {
    private static String RoomPwd = "666";//普通用户进入聊天室的密码
    private static String AdminPWd = "admin";//管理员进入聊天室的密码
    public static void main(String[] args) throws IOException {
        System.out.println("-----The login Server-----");
        // 1、指定端口 使用ServerSocket创建服务器
        ServerSocket server =new ServerSocket(61111);
        boolean isRunning =true;;
        // 2、阻塞式等待连接 accept
        while(isRunning) {
            Socket client =server.accept();
            System.out.println("-----一个客户端建立了连接-----");
            new Thread(new Channel(client)).start();
        }
        server.close();
    }
    //一个channel就代表一个客户端
    static class Channel implements Runnable{
        private Socket  client;
        //输入流
        private DataInputStream dis;
        //输出流
        private DataOutputStream dos;
        private boolean IsAdmin;
        public Channel(Socket  client) {
            this.client = client;
            this.IsAdmin = false;
            try {
                //输入
                dis = new DataInputStream(client.getInputStream());
                //输出
                dos =new DataOutputStream(client.getOutputStream());
            } catch (IOException e) {
                e.printStackTrace();
                CloseUtils.close(dos,dis);
            }

        }

        // 判断是否为管理员
        public boolean isAdmin() {
            return IsAdmin;
        }

        //接收数据
        private String receive() {
            String datas ="";
            try {
                datas = dis.readUTF();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return datas;
        }

        //发送数据
        private void send(String msg) {
            try {
                dos.writeUTF(msg);
                dos.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void run() {
            // 3、操作: 输入输出流操作
            String uname ="";
            String upwd ="";
            //数据分析
            String[] dataArray = receive().split("&");
            for(String info:dataArray) {
                String[] userInfo =info.split("=");
                if(userInfo[0].equals("uname")) {
                    System.out.println("用户名为:"+userInfo[1]);
                    uname = userInfo[1];
                }else if(userInfo[0].equals("upwd")) {
                    System.out.println("聊天室的密码为:"+userInfo[1]);
                    upwd = userInfo[1];
                }
            }
            if (uname.contains("admin")&&upwd.equals(AdminPWd)){
                send("TRUE_ADMIN");//验证为管理员
                this.IsAdmin = true;
            }
            else {
                if (upwd.equals(RoomPwd)) { //成功
                    send("TRUE_USER");//验证为普通用户
                } else { //失败
                    send("FALSE");
                }
            }
            CloseUtils.close(client,dos,dis);

        }

    }

}

②第二是识别管理员指令和转发用户内容的中转服务器

具体作用:根据指令转发私聊,群聊,实现踢人,发广告,修改广告内容,服务器实现监控连接 需要开启多线程 一个线程应付一个客户端

package cn.Himit_ZH.ServerTransponder;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;


/**
 * 名字: 指令识别和内容中转服务器
 * 作用: 根据指令转发私聊,群聊,实现踢人,发广告,修改广告内容,服务器实现监控连接
 * 利用使用ServerSocket创建服务器
 * 阻塞式等待连接 accept
 * @author Himit_ZH
 *
 */

public class ChatRoomServer {
    private static CopyOnWriteArrayList<Channel> all = new CopyOnWriteArrayList();
    private static Map<String,String> map = new HashMap<>();
    private static String advert = "人生苦短,我用python!";
    public static void main(String[] args) throws IOException {
        System.out.println("-----The Chat Server-----");
        map.put("admin1","Himit_ZH");
        map.put("admin2","小浩");//此次添加管理员信息
        ServerSocket server = new ServerSocket(62222);

        while(true) {
            Socket client = server.accept();
            ChatRoomServer.Channel c = new ChatRoomServer.Channel(client);
            all.add(c);
            if (c.isAdmin) {
                System.out.println("-----一个客户端建立了连接--->[管理员]"+c.name+",目前服务器连接人数为:"+all.size()+"个");
            }else{
                System.out.println("-----一个客户端建立了连接--->[用户]"+c.name+",目前服务器连接人数为:"+all.size()+"个");
            }
            (new Thread(c)).start();
        }
    }

static class Channel implements Runnable {
        private DataInputStream dis;
        private DataOutputStream dos;
        private Socket client;
        private boolean isRunning;
        private String name;
        private boolean isAdmin;
        public Channel(Socket client) {
            this.client = client;
            try {
                this.dis = new DataInputStream(client.getInputStream());
                this.dos = new DataOutputStream(client.getOutputStream());
                this.isRunning = true;
                String result = this.receive();
                //判断是否为管理员
                String []results=result.split("#");
                if(results[1].equals("TRUE_ADMIN")){
                    this.isAdmin = true;
                    this.name = map.get(results[0]); //获得map容器里面的管理员对应名字
                }else {
                    this.isAdmin = false;
                    this.name = results[0];//用户名直接获取
                }
                if (this.isAdmin){
                    this.send("\t-----尊敬的管理员["+this.name+"],欢迎您的到来!-----\t\n\t-----目前聊天室总人数为:" + (String.valueOf(all.size() + 1))+"个-----\t");
                    this.send("\t-----温馨提示:使用[@××>]格式可进行私聊,使用#××可踢出对应成员-----\t");
                    this.send("\t-----温馨提示:使用[*广告*]格式可发系统广告,使用[*广告>××]可踢可修改原定广告内容--\t");
                    sendOthers("管理员[" + this.name + "]加入<相亲相爱一家人>的聊天室,请大家注意言行!", true);
                }else {
                    this.send("\t-----亲爱的用户[" + this.name + "],欢迎你的到来!-----\t\n\t-----目前聊天室总人数为:" + (String.valueOf(all.size() + 1)) + "个-----\t");
                    this.send("\t-----温馨提示:使用[@××>]格式可进行私聊-----\t");
                    sendOthers("用户[" + this.name + "]加入<相亲相爱一家人>的聊天室", true);
                }
            } catch (IOException var3) {
                System.out.println("聊天室服务器初始化失败");
                this.release();
            }

        }


private String receive() {
            String msg = "";
            try {
                msg = this.dis.readUTF();
            } catch (IOException var3) {
                this.release();
                if(this.isAdmin) {
                    System.out.println("管理员["+this.name + "]断开了与服务器的连接,目前服务器连接人数为"+all.size()+"人");
                }else{
                    System.out.println("用户["+this.name + "]断开了与服务器的连接,目前服务器连接人数为"+all.size()+"人");
                }

            }

            return msg;
        }

        private void send(String msg) {
            try {
                this.dos.writeUTF(msg);
                this.dos.flush();
            } catch (IOException var3) {
                System.out.println("服务器故障,未能成功发送消息!");
                this.release();
            }

        }

        private void sendOthers(String msg, boolean isSys) {
            char instruction = msg.charAt(0);
            boolean flag1 = false;
            boolean flag2 = false;
            int idx = msg.indexOf(">");
            if(msg.equals("*广告*")&&this.isAdmin){
                sendOthers("[广告]"+advert,true);
                return;
            }
            if (msg.indexOf("*广告>")==0&&this.isAdmin){
                advert = msg.substring(4,msg.length());
                send("\t-----系统通知:广告内容修改成功!-----\t");
                return;
            }
            if (instruction == '@'&&idx!=-1) {
                String targetName = msg.substring(1, idx);
                msg = msg.substring(idx + 1);
                for(Channel other: all) {
                    if (targetName.equals(this.name)){
                        send("\t-----系统提示:对不起,你不能私聊自己!-----\t");
                        flag1 = true;
                        break;
                    }else {
                        if (other.name.equals(targetName)) {//目标
                            if (this.isAdmin) {
                                other.send("管理员[" + this.name + "]私聊你:" + msg);
                            } else {
                                other.send("用户[" + this.name + "]私聊你:" + msg);
                            }
                            flag1=true;
                            break;
                        }
                    }
                }
                if (!flag1){
                    send("\t-----系统提示:对不起,聊天室并无["+targetName+"]用户或管理员-----\t");
                }
            }else if(instruction == '#'&&this.isAdmin){
                String targetName = msg.substring(1, msg.length());
                if (targetName.equals(this.name)){
                    send("\t-----系统提示:对不起,你不能踢出自己!-----\t");
                    flag2 = true;
                }else {
                    for (Channel other : all) {
                        if (other.name.equals(targetName)) {//目标
                            sendOthers("用户[" + other.name + "]被管理员[" + this.name + "]踢出了聊天室", true);
                            other.send("#对不起,你被管理员[" + this.name + "]踢出了聊天室!");
                            other.isRunning = false;
                            CloseUtils.close(other.dis,other.dos,other.client);//退出
                            all.remove(other);
                            flag2 = true;
                            break;
                        }
                    }
                }
 private String receive() {
            String msg = "";
            try {
                msg = this.dis.readUTF();
            } catch (IOException var3) {
                this.release();
                if(this.isAdmin) {
                    System.out.println("管理员["+this.name + "]断开了与服务器的连接,目前服务器连接人数为"+all.size()+"人");
                }else{
                    System.out.println("用户["+this.name + "]断开了与服务器的连接,目前服务器连接人数为"+all.size()+"人");
                }

            }

            return msg;
        }

        private void send(String msg) {
            try {
                this.dos.writeUTF(msg);
                this.dos.flush();
            } catch (IOException var3) {
                System.out.println("服务器故障,未能成功发送消息!");
                this.release();
            }

        }

        private void sendOthers(String msg, boolean isSys) {
            char instruction = msg.charAt(0);
            boolean flag1 = false;
            boolean flag2 = false;
            int idx = msg.indexOf(">");
            if(msg.equals("*广告*")&&this.isAdmin){
                sendOthers("[广告]"+advert,true);
                return;
            }
            if (msg.indexOf("*广告>")==0&&this.isAdmin){
                advert = msg.substring(4,msg.length());
                send("\t-----系统通知:广告内容修改成功!-----\t");
                return;
            }
            if (instruction == '@'&&idx!=-1) {
                String targetName = msg.substring(1, idx);
                msg = msg.substring(idx + 1);
                for(Channel other: all) {
                    if (targetName.equals(this.name)){
                        send("\t-----系统提示:对不起,你不能私聊自己!-----\t");
                        flag1 = true;
                        break;
                    }else {
                        if (other.name.equals(targetName)) {//目标
                            if (this.isAdmin) {
                                other.send("管理员[" + this.name + "]私聊你:" + msg);
                            } else {
                                other.send("用户[" + this.name + "]私聊你:" + msg);
                            }
                            flag1=true;
                            break;
                        }
                    }
                }

 if (!flag1){
                    send("\t-----系统提示:对不起,聊天室并无["+targetName+"]用户或管理员-----\t");
                }
            }else if(instruction == '#'&&this.isAdmin){
                String targetName = msg.substring(1, msg.length());
                if (targetName.equals(this.name)){
                    send("\t-----系统提示:对不起,你不能踢出自己!-----\t");
                    flag2 = true;
                }else {
                    for (Channel other : all) {
                        if (other.name.equals(targetName)) {//目标
                            sendOthers("用户[" + other.name + "]被管理员[" + this.name + "]踢出了聊天室", true);
                            other.send("#对不起,你被管理员[" + this.name + "]踢出了聊天室!");
                            other.isRunning = false;
                            CloseUtils.close(other.dis,other.dos,other.client);//退出
                            all.remove(other);
                            flag2 = true;
                            break;
                        }
                    }
                }
                if (!flag2){
                    send("\t-----系统提示:对不起,聊天室并无["+targetName+"]用户或管理员-----\t");
                }
            }else {
                for(Channel other: all) {
                    if(!isSys) {
                        if(other==this) { //自己
                            if(this.isAdmin) {
                                send("[管理员]自己:" + msg);
                            }else{
                                send("[用户]自己:" + msg);
                            }
                        }else {//别人
                            if (this.isAdmin) {
                                other.send("[管理员]" + this.name + ":" + msg);//群聊消息
                            } else {
                                other.send("[用户]" + this.name + ":" + msg);//群聊消息

                            }
                        }
                    }else {
                        other.send("\t-----系统通知:"+msg+"-----\t"); //系统消息
                    }
                }
            }
        }
        //释放资源
        private void release() {
            this.isRunning = false;
            CloseUtils.close(dis,dos,client);
            //退出
            all.remove(this);
            sendOthers("用户["+this.name+"]退出了聊天室",true);
        }
        @Override
        public void run() {
            while(isRunning) {
                String  msg= receive() ;
                if(!msg.equals("")) {
                    //send(msg);
                    sendOthers(msg,false);
                }
            }
        }
    }
}

③客户端

首先先验证登陆,利用TCP,IO流(当然服务器的IP都是“localhost”本机,因为你的服务器也是运行在本机上的,端口就是上面的验证登录服务器设定的端口号)往验证登陆的服务器发送用户名和密码,验证成功后会得到指令TRUE_USER(标识为普通用户)或者是TRUE_ADMIN(标识为管理员),接着中转服务器获取信息初始化,开启对应的用户线程,用户自身也开启两个线程,一个为时时传递消息的发送端线程,一个为时时接受信息的接受端线程。 《1》用户运行的程序(几个用户就启动几个程序)

package cn.Himit_ZH.Client;
import java.io.*;
import java.net.Socket;

/**
 * @Author: Himit_ZH
 * @Date: 2020/2/20 17:09
 * @Description:
 * @ProjectName:
 */
public class ChatRoomClient {
    public static boolean flag = false;
    public static void main(String[] args) throws IOException {
        String result = "";
        String uname = "";
        System.out.println("\t-----The Sign Client-----\t");
        System.out.println("\t--<相亲相爱一家人>聊天室---\t");
        while (!flag) {
            BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
            System.out.print("\t     请输入昵称:");
            uname = console.readLine();
            System.out.print("\t     请输入密码:");
            String upwd = console.readLine();

            //1、建立连接: 使用Socket创建客户端 +服务的地址和端口
            Socket client1 = new Socket("localhost", 61111);
            //2、操作: 输入输出流操作
            DataOutputStream dos = new DataOutputStream(client1.getOutputStream());
            dos.writeUTF("uname=" + uname + "&" + "upwd=" + upwd);
            dos.flush();

            DataInputStream dis = new DataInputStream(client1.getInputStream());
            result = dis.readUTF();
            dos.close();
            client1.close();
            System.out.println("\t-----The Sign Client-----\t");
            if (result.equals("FALSE")){
                System.out.println("-----密码错误,请重新输入-----");
            }
            else {
                flag =true;
            }
            //3、释放资源
        }
        System.out.println("\t-----The Chat Client-----\t");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        Socket client2 = new Socket("localhost", 62222);
        (new Thread(new Send(client2,uname,result))).start();//Thread1:向服务器发送自己的消息
        (new Thread(new Receive(client2))).start();//Thread2:接受服务器的消息(系统消息和其他网友的信息)
    }
}
《2》发送端

package cn.Himit_ZH.Client;

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

/**
 * 使用多线程封装:发送端
 * 作用:获取键盘输入的信息或者指令,往中转服务器发送信息
 * @author Himit_ZH
 *
 */
public class Send implements Runnable {
    private BufferedReader console ;
    private DataOutputStream dos;
    private Socket client;
    private boolean isRunning;
    private String name;
    private String resultOfAdmin;
    public Send(Socket client,String name,String result) {
        this.client =client;
        console =new BufferedReader(new InputStreamReader(System.in));
        this.isRunning = true;
        this.resultOfAdmin = result;
        this.name = name;
        try {
            dos =new DataOutputStream(client.getOutputStream());
            //发送名称
            send(name+"#"+resultOfAdmin);
        } catch (IOException e) {
            System.out.println("数据输出流初始化错误,请重启");
            this.release();
        }
    }
    @Override
    public void run() {
        while(isRunning) {
            String msg = getStrFromConsole();
            if(!msg.equals("")) {
                send(msg);
            }
        }
    }

    //发送消息
    private void send(String msg) {
        try {
            dos.writeUTF(msg);
            dos.flush();
        } catch (IOException e) {
            System.out.println("与服务器断开连接,消息发送失败!请重启客户端!");
            release();
        }
    }
    /**
     * 从控制台获取消息
     * @return
     */
    private String getStrFromConsole() {
        try {
            return  console.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }
    //释放资源
    private void release() {
        this.isRunning = false;
        CloseUtils.close(dos,client);
    }

}

《3》接受端

package cn.Himit_ZH.Client;


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

/**
 * 使用多线程封装:接收端
 * 作用:接受中转服务器发来的信息,打印到控制台
 * @author Himit_ZH
 *
 */
public class Receive implements Runnable {
    private DataInputStream dis ;
    private Socket client;
    private boolean isRunning;
    public Receive(Socket client) {
        this.client = client;
        this.isRunning = true;
        try {
            dis =new DataInputStream(client.getInputStream());
        } catch (IOException e) {
            System.out.println("数据输入流初始化错误,请重启");
            release();
        }
    }
    //接收消息
    private String receive() {
        String msg ="";
        try {
            msg =dis.readUTF();
        } catch (IOException e) {
            System.out.println("未能成功接受到服务器的消息,请重启客户端!");
            release();
        }
        return msg;
    }



    @Override
    public void run() {
        while(isRunning) {
            String msg =receive();
            if (msg.charAt(0) =='#'){
                System.out.println(msg.substring(1,msg.length()));
                isRunning = false; //收到#指令,关闭线程
            }else {
                if (!msg.equals("")) {
                    System.out.println(msg);
                }
            }
        }
    }
    //释放资源
    private void release() {
        this.isRunning = false;
        CloseUtils.close(dis,client);
    }

}

④最后一个是三个运行程序都要用的工具类

作用是关闭各种资源(IO流和端口)

package cn.Himit_ZH.ServerTransponder;

import java.io.Closeable;


/**
 * 名字:工具类
 * 作用:关闭各种资源
 * @author Himit_ZH
 *
 */
public class CloseUtils {
    public static void close(Closeable... targets ) {
        for(Closeable target:targets) {
            try {
                if(target!=null) {
                    target.close();
                }
            }catch(Exception e) {
             System.out.println("资源关闭失败");
            }
        }
    }
}

演示

登陆界面 默认的管理员账号为admin1或者admin2 密码都为admin 用户账号昵称可以随便设置 密码为聊天室房间默认密码666 要修改的话在步骤②中的代码找,有备注了 演示图片1 群聊私聊 演示图片2 两个广告指令 演示图片3 踢人 演示图片4

评论