Java-简易控制台聊天室

功能

1.登录和注册功能,客户端保存用户信息 

2.具有私聊功能

3.具有一个聊天室功能 

4.在服务端保存登录用户信息,总登录人数,私聊信息,群聊信息 

项目整体思路

客户端有一个主线程与一个子线程。主线程用来向服务端传递数据,子线程用来接收服务端传来的数据。

服务端有一个主线程与两个子线程。一个子线程专门处理群聊功能,一个子线程专门处理私聊功能。主线程通过接收客户端传来的数据,为客户端提供相应的服务端。

具体目录如下

所用到的一些模块知识 

IO流 数据流/序列化流 文件操作

多线程 多线程代码的调试 等

网络通信 此处所用TCP通信 Socket类 

ip地址、端口的认识

客户端

主线程
public class Client {
    private boolean IsRoom;
    private String userName;
    private String userPassWord;

    public String getUserPassWord() {
        return userPassWord;
    }

    public void setUserPassWord(String userPassWord) {
        this.userPassWord = userPassWord;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public boolean getIsRoom() {
        return IsRoom;
    }

    public void setIsRoom(boolean room) {
        IsRoom = room;
    }

    public static void main(String[] args) {
        Client client = new Client();
        Scanner scanner = new Scanner(System.in);
        try(
                Writer Infowriter = new FileWriter("Room4\\src\\ClientAccountInfo.txt",true);
                )
        {

            System.out.println("请输入您的账号名:");
            client.setUserName(scanner.nextLine());
            Infowriter.write ("账号名:"+ client.getUserName()+" ");

            System.out.println("请输入您的密码:");
            client.setUserPassWord(scanner.nextLine());
            Infowriter.write("密码:"+ client.getUserPassWord()+"\r\n");

//            刷新字符流的缓冲区
            Infowriter.flush();

//            注意这里的8888是对应服务端的端口
            Socket socket = new Socket("10.36.222.54",8888);
            OutputStream socketOs = socket.getOutputStream();
            DataOutputStream DataSocketOs = new DataOutputStream(socketOs);
//            将用户姓名传给服务端
            DataSocketOs.writeUTF(client.getUserName());
    
//            将用户信息传给服务端,让服务端保存
            DataSocketOs.writeUTF(client.getUserName()+" "+ client.getUserPassWord());

            System.out.println("登录成功,请做出您的选择:");
            System.out.println("1.进行私聊");
            System.out.println("2.加入群聊");
            int choice = scanner.nextInt();
            scanner.nextLine();
            new Thread(new ClientThread(socket)).start();
            switch (choice){
                case 1:
                    DataSocketOs.writeBoolean(false);
                    while (true) {
                        String targetUserName = scanner.nextLine();
                        DataSocketOs.writeUTF(targetUserName);
                        if (targetUserName.equals("exit")){
                            continue;
                        }
                        while (true) {
                            System.out.println("对Ta说点什么吧:");
                            String whatISaid = scanner.nextLine();
                            DataSocketOs.writeUTF(whatISaid);
                            if (whatISaid.equals("exit")){
                                break;
                            }
                        }
                    }
                case 2:
                    System.out.println("加入群聊成功!");
                    DataSocketOs.writeBoolean(true);
                    while(true){

                        System.out.println("说点什么吧:");
                        String whatISaid = scanner.nextLine();

                        DataSocketOs.writeUTF(whatISaid);

                    }
            }
        } catch (Exception e) {
//            在Java中,如果客户端断开与服务端的连接,服务端会报异常。
//            当客户端主动断开连接时,服务端在尝试读取或写入数据时会抛出异常
            throw new RuntimeException(e);
        }
    }

}
 子线程
public class ClientThread implements Runnable{
    Socket socket;
    public ClientThread(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try(
                InputStream is =  socket.getInputStream();
                DataInputStream DataIs = new DataInputStream(is);
        )
        {
            while (true){
                String msg = DataIs.readUTF();
                System.out.println(msg);
            }

        } catch (Exception e) {
            System.out.println("Ta挂了");
            throw new RuntimeException(e);
        }
    }
}
客户端注意事项  

主线程一定只能用来给服务端发送数据,子线程只能用来接收服务端传来的数据。

自己在写项目时,因为在客户端主线程接收了服务端传来的数据,导致原本的子线程无法接收数据,进而导致项目逻辑出错。

教训:客户端一定有且仅有一条用来接受数据的管道,万不可多开。

服务端

主线程
public class Servant {
    public static List<Boolean> isRoom = new ArrayList<>();
    public static Map<String,Socket> OnlineUsers = new HashMap<>();
    public static List<Socket> RoomSockets = new ArrayList<>();

    public static void main(String[] args) {

        try(
                Writer ServantUsersOs = new FileWriter("Room4\\src\\ServantUsers.txt",true);
                )
        {
            System.out.println("服务器启动成功");
            ServerSocket serverSocket = new ServerSocket(8888);
            int count = 0;

            while (true){
                count++;
                Socket socket =  serverSocket.accept();
                InputStream socketIs =  socket.getInputStream();
                DataInputStream DataSocketIS = new DataInputStream(socketIs);
                String userName= DataSocketIS.readUTF();

//              接收用户账号信息,并输出到文件中
                String OneUserInfo = DataSocketIS.readUTF();
                ServantUsersOs.write(OneUserInfo + "\r\n");
                ServantUsersOs.flush();

                Servant.OnlineUsers.put(userName,socket);
//                Servant.sockets.add(socket);

                Boolean tmp = DataSocketIS.readBoolean();
                Servant.isRoom.add(tmp);
                if (Servant.isRoom.get(count - 1)){
                    Servant.RoomSockets.add(socket);
                    new Thread(new RoomServantThread(userName,socket,count - 1)).start();
                }
                else {
                    new Thread(new PrivateChatThread(socket,userName)).start();

                }
                String nums =  String.valueOf(Servant.OnlineUsers.size());

                Writer allwriter = new FileWriter("Room4\\src\\Allpeople.txt");
                allwriter.write("当前总登录人数:"+ nums);
                allwriter.flush();
                allwriter.close();
            }


        } catch (Exception e) {
            throw new RuntimeException(e);
        }


    }


}
主线程记录总登录人数注意事项
                Writer allwriter = new FileWriter("Room4\\src\\Allpeople.txt");
                allwriter.write("当前总登录人数:"+ nums);
                allwriter.flush();
                allwriter.close();

文件要实现文件覆盖功能,需要在每次写完文件,并flush后,将文件流关闭。否则缓冲区中的数据将不会被加载进文件中。

服务群聊的子线程
public class RoomServantThread implements Runnable{
    public String userName ;
    public int Index;
    public Socket socket ;

    public RoomServantThread(String userName, Socket socket,int Index){
        this.userName = userName;
        this.socket = socket;
        this.Index = Index;
    }
    @Override
    public void run() {
        try (
                InputStream is = socket.getInputStream();
                DataInputStream DataIs1 = new DataInputStream(is);
                Writer pubWriter = new FileWriter("Room4\\src\\publicChat.txt",true);
                )
        {
            while (true){
                String someOneSaid = DataIs1.readUTF();
                for (Socket roomSocket : Servant.RoomSockets) {
                    OutputStream os = roomSocket.getOutputStream();
                    DataOutputStream DataOs2 = new DataOutputStream(os);
                    DataOs2.writeUTF(userName + " say:" +someOneSaid);
                    pubWriter.write(userName + " say:" + someOneSaid + "\r\n");
                    pubWriter.flush();
                }

            }
        } catch (Exception e) {
            Servant.isRoom.remove(Index);
            Servant.RoomSockets.remove(socket);
            Servant.OnlineUsers.remove(userName,socket);
        }
    }
}
服务私聊的子线程 
public class PrivateChatThread implements Runnable{
    public Socket socket;
    public String username;
    public PrivateChatThread(Socket socket,String username){
        this.socket = socket;
        this.username = username;
    }

    @Override
    public void run() {

        try (
                DataOutputStream DataSocketOs = new 
                 DataOutputStream(socket.getOutputStream());
                DataInputStream DataSocketIs = new 
                 DataInputStream(socket.getInputStream());
                Writer priWriter = new FileWriter("Room4\\src\\priChat.txt",true);
                )
        {
            while (true) {
                String tmp = "请选择您的聊天对象: ";
                Set<String> allTheOnlineUsers = Servant.OnlineUsers.keySet();
                for (String onlineUser : allTheOnlineUsers) {
                    tmp += onlineUser +" ";
                }
                DataSocketOs.writeUTF(tmp);
                boolean flag = false;
                String name = DataSocketIs.readUTF();
                if (name.equals("exit")){
                    continue;
                }
                for (String onlineUser : allTheOnlineUsers) {
                    if (onlineUser.equals(name)){
                        Socket beTalked = Servant.OnlineUsers.get(onlineUser);
                        while (true) {
                                String whatTalkedSaid = DataSocketIs.readUTF();
                                if (whatTalkedSaid.equals("exit")){
                                    flag = true;
                                    break;
                                }
                                DataOutputStream p = new     
                                DataOutputStream(beTalked.getOutputStream());
                                p.writeUTF(username + " say:" +whatTalkedSaid);

                                priWriter.write(username+" say:" + whatTalkedSaid 
                                +"\r\n");
                                priWriter.flush();
                        }
                    }
                    if (flag)
                        break;
                }
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值