BIO实现即时通讯


前言`

bio即时通讯小案例


一、即时通讯案例

1.服务端

代码如下(示例):

package com.xuexi.testSocket.jiShiTongXun;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ServerChat {
    // 定义集合存放所有在线socket, key:socket,value:socket客户端的名称
    public static Map<Socket,String> onLineSockets = new HashMap<Socket,String>();
    public static void main(String[] args) {
        try{
            // 注册端口
            ServerSocket serverSocket = new ServerSocket(9999);
            // 循环等待所有可能的客户端连接
            while (true){
                Socket socket = serverSocket.accept();
                // 把客户端的socket管道单独配置一个线程来处理
                new ServerReader(socket).start();
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

package com.xuexi.testSocket.jiShiTongXun;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ConnectException;
import java.net.Socket;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Set;

public class ServerReader extends Thread{
    private Socket socket;
    public ServerReader(Socket socket){
        this.socket = socket;
    }

    public void run(){
        DataInputStream dis = null;
        try{
            dis = new DataInputStream(socket.getInputStream());
            // 循环等待客户端的消息
            while(true){
                // 获取消息类型:登录,群发,私聊,@消息
                int flag = dis.readInt();
                if(flag == 1){
                    // 先将当前登录的客户端socket存放到在线人数的socket集合中
                    String name = dis.readUTF();
                    System.out.println(name + "----->"+socket.getRemoteSocketAddress());
                    ServerChat.onLineSockets.put(socket,name);
                }
                writeMsg(flag,dis);
            }
        }catch (SocketException ce){
            System.out.println("-- \""+ServerChat.onLineSockets.get(socket)+"\"下线了 --");
            // 从在线集合中删除
            ServerChat.onLineSockets.remove(socket);
            try{
                writeMsg(1,dis);
            }catch (Exception e){
                e.printStackTrace();
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    public void writeMsg(int flag, DataInputStream dis) throws IOException {
        // 拿到所有的在线socket通道,给这些通道写出消息
        String  msg = null;
        if(flag == 1){
            // 读取所有在线人数发给客户端去更新所有列表在线人数
            StringBuilder rs = new StringBuilder();
            Collection<String> onLineNmaes = ServerChat.onLineSockets.values();
            // 判断是否存在在线人数
            if(onLineNmaes != null && onLineNmaes.size() > 0){
                for(String name : onLineNmaes){
                    rs.append(name + ",");
                }
                // 去掉最后一个分隔符
                msg = rs.substring(0,rs.lastIndexOf(","));

                // 将消息发送给客户端
                sendMsgToAll(flag,msg);
            }
        }else if(flag == 2 || flag == 3){
            // 读到群发消息或者@消息
            String newMsg = dis.readUTF();
            // 获取发件人
            String sendName = ServerChat.onLineSockets.get(socket);

            // 内容
            StringBuilder msgFinal = new StringBuilder();
            // 时间
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEE");
            if(flag == 2){ // 群发或@消息
                msgFinal.append(sendName).append(" ").append(sdf.format(new Date())).append("\n");
                msgFinal.append("    ").append(newMsg).append("\n");
                sendMsgToAll(flag,msgFinal.toString());
            }else if(flag == 3){// 私发
                msgFinal.append(sendName).append(" ").append(sdf.format(new Date())).append("对您私发\n");
                msgFinal.append("    ").append(newMsg).append("\n");
                String destName = dis.readUTF();
                sendMsgToOne(destName,msgFinal.toString());
            }
        }
    }

    private void sendMsgToOne(String destName, String msg) throws IOException {
        Set<Socket> allOnLineSockets = ServerChat.onLineSockets.keySet();
        for(Socket sk : allOnLineSockets){
            if(ServerChat.onLineSockets.get(sk).trim().equals(destName)){
                DataOutputStream dos = new DataOutputStream(sk.getOutputStream());
                dos.writeInt(2); // 消息类型
                dos.writeUTF(msg);
                dos.flush();
            }
        }
    }

    private void sendMsgToAll(int flag, String msg) throws IOException {
        // 拿到所有在线的socket管道,给这些管道写出消息
        Set<Socket> allOnLineSockets = ServerChat.onLineSockets.keySet();
        for(Socket sk : allOnLineSockets){
            DataOutputStream dos = new DataOutputStream(sk.getOutputStream());
            dos.writeInt(flag); // 消息类型
            dos.writeUTF(msg);
            dos.flush();
        }
    }

}

2.客户端

代码如下(示例):

package com.xuexi.testSocket.jiShiTongXun;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataOutputStream;
import java.net.Socket;

public class ClientChat implements ActionListener {

    // 设计界面
    private JFrame win = new JFrame();
    // 消息内容框架
    public JTextArea smsContent = new JTextArea(23,50);
    // 发送消息框
    private JTextArea smsSend = new JTextArea(4,40);

    // 在线人数的区域
    // 存放人数数据
    // 展示在线人数的窗口
    public JList<String> onLineUsers = new JList<String>();

    // 是否私聊按钮
    private JCheckBox isPrivateBn = new JCheckBox("私聊");
    // 消息按钮
    private JButton sendBn = new JButton("发送");

    // 登录界面
    private JFrame loginView;

    private JTextField ipEt, nameEt, idEt;

    private Socket socket;

    public static void main(String[] args) {
        new ClientChat().initView();
    }

    private void initView(){
        // 初始化聊天窗口界面
        win.setSize(650,600);

        // 展示登录界面
        displayLoginView();
    }

    private void displayChatView(){
        JPanel bottomPanel = new JPanel( new BorderLayout());

        // 将消息框和按钮添加到窗口底部
        win.add(bottomPanel,BorderLayout.SOUTH);
        bottomPanel.add(smsSend);
        JPanel btns = new JPanel(new FlowLayout(FlowLayout.LEFT));
        btns.add(sendBn);
        btns.add(isPrivateBn);
        bottomPanel.add(btns,BorderLayout.EAST);
        // ---------------------------------------------
        // 给按钮添加事件监听器
        // 将展示区添加到窗口中间
        smsContent.setBackground(new Color(0xdd,0xdd,0xdd));
        // 让展示消息可滚动
        win.add(new JScrollPane(smsContent),BorderLayout.CENTER);
        smsContent.setEditable(false);
        // ---------------------------------------------
        //用户列表和是否私聊在窗口最右边
        Box rightBox = new Box(BoxLayout.Y_AXIS);
        onLineUsers.setFixedCellWidth(120);
        onLineUsers.setVisibleRowCount(13);
        rightBox.add(new JScrollPane(onLineUsers));
        win.add(rightBox,BorderLayout.EAST);
        // ---------------------------------------------
        //关闭窗口推出当前程序
        win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        win.pack();// 关闭窗口功能
        // 设置窗口居中,显示窗口
        setWindowCenter(win,650,600,true);
        // 发送按钮绑定点击事件
        sendBn.addActionListener(this);
    }


    private void displayLoginView(){

        /** 登录
         * 服务器ip
         * 用户名
         * id
         */
        // 显示登录框
        loginView = new JFrame("登录");
        loginView.setLayout(new GridLayout(3,1));
        loginView.setSize(400,230);

        JPanel ip = new JPanel();
        JLabel label = new JLabel("  IP:");
        ip.add(label);
        ipEt = new JTextField(20);
        ip.add(ipEt);
        loginView.add(ip);

        JPanel name = new JPanel();
        JLabel label1 = new JLabel("姓名:");
        name.add(label1);
        nameEt = new JTextField(20);
        name.add(nameEt);
        loginView.add(name);

        JPanel btnView = new JPanel();
        JButton login = new JButton("登录");
        btnView.add(login);
        JButton cancle = new JButton("取消");
        btnView.add(cancle);
        loginView.add(btnView);

        // 关闭窗口退出当前程序
        loginView.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setWindowCenter(loginView, 400, 260, true);

        // 给登录取消绑定事件
        login.addActionListener(this);
        cancle.addActionListener(this);
    }

    private static void setWindowCenter(JFrame frame, int width, int height, boolean flag){
        // 获取所在系统的屏幕宽高
        Dimension ds = frame.getToolkit().getScreenSize();

        // 电脑屏幕宽
        int width1 = ds.width;
        // 电脑屏幕高
        int height1 = ds.height;

        System.out.println(width1 + "*" + height1);
        // 设置窗口左上角坐标
        frame.setLocation(width1/2 - width/2, height1/2 - height/2);
        frame.setVisible(flag);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // 获取点击事件
        JButton btn = (JButton) e.getSource();
        switch (btn.getText()){
            case "登录":
                String ip = ipEt.getText().toString();
                String name = nameEt.getText().toString();

                // 校验参数是否为空
                // 提示错误消息
                String msg = "";


                if(ip == null || !ip.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")){
                    msg = "请输入合法ip";
                }else if(name == null || !name.matches("\\S{1,}")){
                    msg = "姓名不少于1个字符";
                }

                if(!msg.equals("")){
                    JOptionPane.showMessageDialog(loginView,msg);
                }else {
                    try{
                        // 显示当前用户名称
                        win.setTitle(name);
                        // 连接一个socket管道
                        socket = new Socket(ip,9999);

                        // 分配线程,处理请求
                        new ClientReader(this,socket).start();

                        // 组装发送数据
                        DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
                        dos.writeInt(1);// 消息类型
                        dos.writeUTF(name.trim());
                        dos.flush();

                        // 关闭当前窗口,弹出聊天界面
                        loginView.dispose();// 登录窗口销毁
                        displayChatView();//展示聊天窗口
                    }catch (Exception exc){
                        exc.printStackTrace();
                    }
                }
                break;
            case "取消":
                // 推出系统
                System.exit(0);
                break;
            case "发送":
                // 得到发送消息的内容
                String msgSend = smsSend.getText().toString();
                if(!msgSend.trim().equals("")){
                    try{
                        // 判断是否对谁发消息
                        String selectName = onLineUsers.getSelectedValue();
                        int flag = 2;
                        if(selectName != null && !selectName.equals("")){
                            msgSend = ("@"+selectName+","+msgSend);
                            // 判断是否选中了私发
                            if(isPrivateBn.isSelected()){
                                flag = 3;
                            }
                        }

                        DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
                        dos.writeInt(flag);// 消息类型
                        dos.writeUTF(msgSend);
                        if(flag == 3){
                            dos.writeUTF(selectName.trim());
                        }
                        dos.flush();
                    }catch(Exception exc){
                        exc.printStackTrace();
                    }
                }
                smsSend.setText(null);
                break;
        }
    }
}

package com.xuexi.testSocket.jiShiTongXun;

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

public class ClientReader extends Thread{
    private Socket socket;
    private ClientChat clientChat;

    public ClientReader(ClientChat clientChat,Socket socket){
        this.clientChat = clientChat;
        this.socket = socket;
    }

    public void run(){
        try{
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            while(true){
                // 获取当前消息类型:登录,群发,私聊,@消息
                int flag = dis.readInt();
                if(flag ==1){
                    // 在线人数消息回来了
                    String nameDatas = dis.readUTF();
                    // 展示到在线人数的界面
                    String[] names = nameDatas.split(",");

                    clientChat.onLineUsers.setListData(names);
                }else if(flag ==2){
                    // 获取消息内容
                    String msg = dis.readUTF();
                    clientChat.smsContent.append(msg);

                    // 让消息界面滚动到底端
                    clientChat.smsContent.setCaretPosition(clientChat.smsContent.getText().length());
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您可以使用 Java 的 Socket 编程实现客户端和服务端的通信,以实现基本的客户端-服务端模型。以下是一个简单的示例: 服务端代码: ```java import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(8888); // 创建 ServerSocket System.out.println("Server started"); while (true) { // 开启循环,等待客户端连接 Socket socket = serverSocket.accept(); // 监听客户端连接 System.out.println("Client connected: " + socket.getInetAddress().getHostAddress()); new ServerThread(socket).start(); // 创建一个新线程处理客户端请求 } } catch (IOException e) { e.printStackTrace(); } } } class ServerThread extends Thread { private Socket socket; public ServerThread(Socket socket) { this.socket = socket; } @Override public void run() { try { // 处理客户端请求,可以使用 socket.getInputStream() 和 socket.getOutputStream() 读写数据 } catch (IOException e) { e.printStackTrace(); } finally { try { socket.close(); // 关闭 socket } catch (IOException e) { e.printStackTrace(); } } } } ``` 客户端代码: ```java import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; public class Client { public static void main(String[] args) { try { Socket socket = new Socket("localhost", 8888); // 创建 Socket,连接到服务端 System.out.println("Connected to server"); // 可以使用 socket.getInputStream() 和 socket.getOutputStream() 读写数据 } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } ``` 这是一个简单的示例,您可以根据您的需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值