Java搭建简易聊天室(Scoket,IO,序列化,Thread)

创建NetSocketIoChat聊天系统

使用技术关键字有 Socket,Io流,Thread

step1:首先创建客户端与服务端以及实体类

Message实体类的创建

public class Message implements Serializable {
    //序列化
    private static final long serialVersionUID = 4497214422911483808L;
    //操作类型
    private Integer type;
    //消息
    private String content;
    //用来登录
    private String username;
    private String password;
    //给朋友发消息时,用于指定用户
    private String friendUsername;
​
    public void setType(Integer type) {
        this.type = type;
    }
​
    public void setContent(String content) {
        this.content = content;
    }
​
    public void setUsername(String username) {
        this.username = username;
    }
​
    public void setPassword(String password) {
        this.password = password;
    }
​
    public void setFriendUsername(String friendUsername) {
        this.friendUsername = friendUsername;
    }
​
    public Integer getType() {
        return type;
    }
​
    public String getContent() {
        return content;
    }
​
    public String getUsername() {
        return username;
    }
​
    public String getPassword() {
        return password;
    }
​
    public String getFriendUsername() {
        return friendUsername;
    }
​
    //登录构造器
    public Message(String username, String password,Integer type) {
        this.username = username;
        this.password = password;
        this.type = type;
    }
    //登录,向服务器发消息
    public Message(Integer type, String content, String username) {
        this.type = type;
        this.content = content;
        this.username = username;
    }
    //向在线用户发消息
​
    public Message(Integer type, String content, String username, String friendUsername) {
        this.type = type;
        this.content = content;
        this.username = username;
        this.friendUsername = friendUsername;
    }
​
    public Message() {
    }
​
    @Override
    public String toString() {
        return "Message{" +
                "type=" + type +
                ", content='" + content + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", friendUsername='" + friendUsername + '\'' +
                '}';
    }
}
常量进行打包存放
public class Constant {
    public static final String SUCCESS = "SUCCESS";
    public static final String FAIL = "FAIL";
​
    public static final String SERVER_NAME = "server";
​
    public static final String OK= "ok";
​
    public static final String DEFAULT_PASSWORD = "123";
    //保存在线的用户
    public static final Map<String, Socket> ONLINE_USERS = new ConcurrentHashMap<>(8);
​
}
​
​
//操作类型
​
​
public class MessageType {
    public static final int TO_SERVER = 2;
​
    public static final int TO_FRIEND = 3;
​
    public static final int TO_ALL = 4;
​
    public static final int LOGIN = 1;
​
    public static final int FORM_SERVER = 6;
​
    public static final int RECEIVER = 5;
}
​
读写工具类打包
public class MsgUtils {
    //读操作
    public static Optional<Message> readMsg(InputStream inputStream) {
        ObjectInputStream ois = null;
​
            try {
                ois = new ObjectInputStream(inputStream);
                //封装成一个optional,将来使用的时候就可以避免空指针
                return Optional.ofNullable((Message) ois.readObject());
            } catch (IOException | ClassNotFoundException e) {
                e.printStackTrace();
            }
            return Optional.empty();
    }
    //向输出流写数据
    public static void writeMsg(OutputStream outputStream,Message message) {
        ObjectOutputStream oos = null;
​
        try {
            oos = new ObjectOutputStream(outputStream);
            //封装成一个optional,将来使用的时候就可以避免空指针
            oos.writeObject(message);
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
ScannerUtil打包为后续操作提供便利
public class ScannerUtil {
    private static final Scanner scanner = new Scanner(System.in);
    public static String input(){
        return scanner.next();
​
    }
​
}
服务端与客户端创建
public class Server {
    public static void main(String[] args){
        try (
              //创建端口服务
              ServerSocket serverSocket = new ServerSocket()
              ){
            //2.随便绑定端口
            serverSocket.bind(new InetSocketAddress(8888));
            System.out.println("服务器已经启动,在8888端口!");
            //开始监听
            //建立一个连接
            while (true){
                Socket accept = serverSocket.accept();
                new ServerThread(accept).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
​
​
    }
​
}
​
​
​
//客户端
Socket socket = new Socket();
        //绑定服务端的端口
        socket.connect(new InetSocketAddress(8888));
​
        //给服务端发消息
        //发消息
        OutputStream outputStream = socket.getOutputStream();
        InputStream inputStream = socket.getInputStream();
//一切就绪,开始进行登录操作
​
        String username = null;
public static void main(String[] args) throws IOException {
        Socket socket = new Socket();
        //绑定服务端的端口
        socket.connect(new InetSocketAddress(8888));
​
        //给服务端发消息
        //发消息
        OutputStream outputStream = socket.getOutputStream();
        InputStream inputStream = socket.getInputStream();
​
//开始登录工作
        String username = null;
​
        //表示当前登录的用户
        while (true) {
            if (username == null) {
                username = login(outputStream, inputStream);
            } else {
                //打印菜单
                printOrder();
                String input = ScannerUtil.input();
                //把scanner内容转为int类型
                switch (Integer.parseInt(input)) {
                    case MessageType.LOGIN:
                        login(outputStream, inputStream);
                    case MessageType.TO_SERVER:
                        sendToServer(username, outputStream, inputStream);
                        break;
                    case MessageType.TO_FRIEND:
                        sendToFriend(username, outputStream, inputStream);
                        break;
                    case MessageType.TO_ALL:
                        sendToAll(username, outputStream, inputStream);
                        break;
                    case MessageType.RECEIVER:
                        receiveMsg(inputStream);
                        break;
                    default:
                        break;
​
                }
            }
​
        }
    }
​
​

step2:开始进行各项模块的验证

登录模块
//登录模块 客户端
 //登录的方法
    private static String login(OutputStream outputStream, InputStream inputStream) {
​
        //登录
        System.out.println("请您输入用户名:");
        String name = ScannerUtil.input();
        System.out.println("请输入密码");
        String pwd = ScannerUtil.input();
​
        Message message = new Message();
        message.setType(MessageType.LOGIN);
        message.setUsername(name);
        message.setPassword(pwd);
        //发送给服务器
        MsgUtils.writeMsg(outputStream, message);
        //接收来自服务的的消息
        Optional<Message> msg = MsgUtils.readMsg(inputStream);
        if (msg.isPresent() && Constant.SUCCESS.equals(msg.get().getContent())) {
            return name;
        }
​
        return null;
    }
​
​
//服务端验证
   private void loginHandler(InputStream inputStream, OutputStream outputStream, Message message, Socket accept) {
        //判断登录的逻辑
        //1.message 不存在
        if (message.getUsername() == null || !Constant.DEFAULT_PASSWORD.equals(message.getPassword())) {
            MsgUtils.writeMsg(outputStream, new Message(MessageType.FORM_SERVER, Constant.FAIL, Constant.SERVER_NAME));
​
        } else {
            //验证成功放入在线用户map中
            Constant.ONLINE_USERS.put(message.getUsername(), accept);
            System.out.println(message.getUsername() + "登录成功!");
            MsgUtils.writeMsg(outputStream, new Message(MessageType.FORM_SERVER, Constant.SUCCESS, "accept"));
        }
    }
​

给服务器发消息
//客户端
​
​
    private static void sendToServer(String username, OutputStream outputStream, InputStream inputStream) {
        System.out.print(username + ":");
        String msg = ScannerUtil.input();
        //给服务器发消息
        MsgUtils.writeMsg(
                outputStream, new Message(MessageType.TO_SERVER, msg, username)
        );
        //接收消息,服务器收到消息后回消息给客户端
        Optional<Message> massage = MsgUtils.readMsg(inputStream);
        massage.ifPresent(m -> System.out.println(m.getUsername() + ":" + m.getContent()));
​
    }
​
​
//服务器端
private void sendToClient(InputStream inputStream, OutputStream outputStream, Message message) {
        //接收消息并打印
        System.out.println(message.getUsername() + ":" + message.getContent());
        //给客户端回消息
        MsgUtils.writeMsg(outputStream, new Message(MessageType.FORM_SERVER, Constant.OK, Constant.SERVER_NAME));
​
    }
给指定在线用户发消息

既然是用户之间的通信那么就应该开启多线程进行多用户登录

//之前服务器端的操作已经进行封装
 //开始监听
            //建立一个连接
            while (true){
                Socket accept = serverSocket.accept();
                new ServerThread(accept).start();
            }

一个人发,一个人收,所以就要再弄两个方法一个收一个发

现在做发的操作

//客户端代码
//在while循环中加入方法,具体可看前面的Client完整代码
case MessageType.TO_FRIEND:
                        sendToFriend(username, outputStream, inputStream);
                        break;
//发送主方法
   private static void sendToFriend(String username, OutputStream outputStream, InputStream inputStream) {
        System.out.println("请输入好友的名字:");
        String friend = ScannerUtil.input();
        boolean flag = true;
        while (flag) {
            System.out.print(username + ":");
            String msg = ScannerUtil.input();
            //退出机制
            if ("bye".equals(msg)) {
                flag = false;
            }
            MsgUtils.writeMsg(
                    outputStream,new Message(MessageType.TO_FRIEND, msg, username, friend)
            );
           
        }
​
    }
//接收方法
    private static void receiveMsg(InputStream inputStream) {
        while (true){
            Optional<Message> massage = MsgUtils.readMsg(inputStream);
            massage.ifPresent(m -> System.out.println(m.getUsername() + ":" + m.getContent()));
        }
​
    }
​
//---------------------------------------------------
​
//服务端
​
 //给目标用户发送消息
    private void sendToTarget( Message message) {
        //先找到对应的socket
        try {
            Socket friendSocket = Constant.ONLINE_USERS.get(message.getFriendUsername());
            MsgUtils.writeMsg(friendSocket.getOutputStream(),message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
​
群发消息

群发需要用到多线程的知识,因此,在一开始把服务端的所有操作都打包起来,然后重写Thread的run方法,这样可以实现一个客户端操作对应一个线程

//线程创建如下
public class ServerThread extends Thread {
    private Socket accept;
​
    public ServerThread() {
    }
    //构造器,在server类中进行调用
    public ServerThread(Socket accept) {
        this.accept = accept;
    }
​
    @Override
    public void run() {
        try (InputStream inputStream = accept.getInputStream();
             OutputStream outputStream = accept.getOutputStream()
        ) {
            while (true) {
                //调用readMsg方法准备接收客户端发来的消息
                Optional<Message> message = MsgUtils.readMsg(inputStream);
                if (message.isPresent()) {
                    //获得客户端输入内容
                    Message msg = message.get();
                    //判断客户端操作的类型,然后进行判断转跳到对应的处理方法
                    switch (msg.getType()) {
                        case MessageType.LOGIN:
                            loginHandler(inputStream, outputStream, msg, accept);
                            break;
                        case MessageType.TO_SERVER:
                            sendToClient(inputStream, outputStream, msg);
                            break;
                        case MessageType.TO_FRIEND:
                            sendToTarget(msg);
                            break;
                        case MessageType.TO_ALL:
                            sendToAll(msg);
                            break;
                        default:
                            break;
                    }
                }
            }
​
        } catch (IOException e) {
            e.printStackTrace();
        }
​
    }
    
    //server类中进行接收客户端传来的消息
    while (true){
                Socket accept = serverSocket.accept();
        //每次创建一个Socket就启动一个线程,实现多用户用时在线
                new ServerThread(accept).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
客户端写群发方法
 private static void sendToAll(String username, OutputStream outputStream, InputStream inputStream) {
        boolean flag = true;
        while (flag) {
            System.out.print(username + ":");
            String msg = ScannerUtil.input();
            if ("bye".equals(msg)) {
                flag = false;
            }
            MsgUtils.writeMsg(
                    outputStream,new Message(MessageType.TO_ALL, msg, username)
            );
        }
​
    }

服务端进行处理

 private void sendToAll(Message message) {
        //遍历map
        for (Map.Entry<String,Socket> entry :Constant.ONLINE_USERS.entrySet()){
            try {
                MsgUtils.writeMsg(entry.getValue().getOutputStream(), message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
​

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值