Java 基于BIO实现 Client与Server通信(单聊、群聊实现)

基于BIO实现 Client与Server通信

1、BIO模型中服务端与客户端的响应过程

在这里插入图片描述

根据上图可清晰看出,Client与Server的响应流程如下:

1、Server 服务器端 serverSocket先要和端口进行绑定

ServerSocket serverSocket = new ServerSocket(6767);  

2、绑定完成后,通过accept方法,阻塞式等待客户端的连接,这个方法是阻塞式调用,直到有客户端连接才往下执行;

 Socket accept = serverSocket.accept();

3、客户端创建Socket对象,绑定服务器的ip地址端口号,与服务器进行连接

 //建立socket连接  host=127.0.0.1   port=6767
socket = new Socket(host, port);

4、服务器接收到客户端的连接请求,accept方法获取到客户端的socket信息,连接成功
服务器与客户端创建各自的io流,实现全双工通信
在这里插入图片描述

2、案例一(BIO 版 服务端——客户端 聊天实现)

客户端:

package com.xiaojie.net;

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

/**
 * @author Mrli
 * @date 2020/9/25 19:36
 */
public class Client {
    public static void main(String[] args) {
        final String host = "127.0.0.1";
        final int port = 6767;
        final String QUIT = "quit";

        Socket socket = null;
        BufferedReader reader = null;
        BufferedWriter writer = null;

        try {
            //建立socket连接
            socket = new Socket(host, port);

            //获取输入字符流
            reader = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()
            ));

            //获取输出字符流
            writer = new BufferedWriter(new OutputStreamWriter(
                    socket.getOutputStream()
            ));

            //获取输入字符流
            BufferedReader scan = new BufferedReader(new InputStreamReader(System.in));

            while (true) {
                String msg = scan.readLine();

                //向服务器发送消息
                writer.write(msg + "\n");
                writer.flush();
			   
                //接收消息
                String line = reader.readLine();
                System.out.println("server:" + line);

                if (msg.equals(QUIT)) {
                    break;
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(writer != null) {
                try {
                    writer.close();
                    System.out.println("Client close!");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

服务端:

package com.xiaojie.net;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author Mrli
 * @date 2020/9/25 19:37
 */
public class Server {
    public static void main(String[] args) {
        final int port = 6767;
        final String QUIT = "quit";
        ServerSocket serverSocket = null;

        try {
            serverSocket = new ServerSocket(port);
            System.out.println("服务器已启动,监听端口:" + port);

            while(true) {
                Socket accept = serverSocket.accept();
                System.out.println("客户端" + accept.getPort() + ":" + "已经连接");

                //获取输入字符流
                BufferedReader reader = new BufferedReader(new InputStreamReader(
                        accept.getInputStream()
                ));
                //获取输出字符流
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
                        accept.getOutputStream()
                ));

                String msg = null;
                while ((msg = reader.readLine()) != null ) {
                    //接收消息
                    System.out.println("client[" + accept.getPort() + "]:" +msg);
                    
                    //转发到Client,
                    writer.write( msg + "\n");
                    writer.flush();

                    if(msg.equals(QUIT)) {
                        System.out.println("client close!");
                        break;
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}



运行结果:

client端:

在这里插入图片描述

服务端:
在这里插入图片描述





3、案例二(BIO多客户端聊天室实现)

此案例分为:

Server(服务端)
ChartHandler(服务端多线程监听客户端发送的信息)

Client(客户端)
InputHandler( 客户端监听用户输入的信息)



Server.java

package com.xiaojie.net.bio.chatRoom;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @author Mrli
 * @date 2020/9/26 16:20
 * 多人聊天室,服务端
 */
public class Server {
    private int port = 6767;
    private final String QUIT = "quit";

    private ServerSocket serverSocket = null;

    /**
     * 存放所有的客户端连接(socket、writer)
     */
    private Map<Integer, Writer> clientList;

    /**
     *构建线程池
     */
    private ExecutorService executor;

    /**
     * 构造初始化
     */
    public Server() {
        this.clientList = new HashMap<>();
        this.executor = new ThreadPoolExecutor(10,10,0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());
    }

    /**
     * 添加客户端
     * @param socket
     */
    public synchronized void addClient(Socket socket) throws IOException {
        if(socket != null) {
            int key = socket.getPort();
            Writer writer = new BufferedWriter(new OutputStreamWriter(
                    socket.getOutputStream()
            ));

            clientList.put(key,writer);
            System.out.println("客户端["+port + "]:已连接!");
        }
    }

    /**
     * 删除客户端
     * @param socket
     */
    public synchronized void removeClient(Socket socket) throws IOException {
        if(socket != null) {
            int key = socket.getPort();
            clientList.get(key).close();
            clientList.remove(key);
            System.out.println("客户端["+port + "]:已断开连接!");
        }
    }

    /**
     * 将消息转发给所有客户端
     * @param socket
     * @param msg
     */
    public synchronized void forwardMsg(Socket socket, String msg) throws IOException {
        for (Integer key : clientList.keySet()) {
            if(!key.equals(socket.getPort())) {
                Writer writer = clientList.get(key);
                writer.write(msg);
                writer.flush();
            }
        }
    }

    /**
     * 判断用户消息是否为quit指令
     * @param msg
     * @return
     */
    public synchronized boolean isQuit(String msg) {
        return QUIT.equals(msg);
    }

    /**
     * 关闭serverSocket
     */
    public synchronized void close(){
        if(serverSocket != null) {
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void start() {
        try {
            ServerSocket serverSocket = new ServerSocket(port);
            System.out.println("服务器启动,正在监听端口:" + port);

            while (true) {
                //监听客户端连接
                Socket socket = serverSocket.accept();

                //创建线程处理
                //new Thread(new ChartHandler(this,socket)).start();
                executor.execute(new ChartHandler(this,socket));
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close();
        }
    }

    /**
     * 通过主线程启动server
     * @param args
     */
    public static void main(String[] args) {
        Server server = new Server();
        server.start();
    }
}

ChartHandler.java

package com.xiaojie.net.bio.chatRoom;

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

/**
 * @author Mrli
 * @date 2020/9/26 16:22
 */
public class ChartHandler implements Runnable{
    private Server server;
    private Socket socket;

    /**
     * 构造器初始化server、socket
     * @param server
     * @param socket
     */
    public ChartHandler(Server server, Socket socket) {
        this.server = server;
        this.socket = socket;
    }

    /**
     * 线程主要业务逻辑
     */
    @Override
    public void run() {
        try {
            //添加客户端socket
            server.addClient(socket);

            //获取客户端输入字符流
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()
            ));

            //阻塞获取客户端的消息
            String msg = null;
            while ((msg = reader.readLine()) != null) {
                String fwdmsg = "客户端[" + socket.getPort() + "] : " + msg + "\n";
                System.out.println(fwdmsg);

                //转发消息给所有客户端
                server.forwardMsg(socket,fwdmsg);

                //检测是否退出
                if(server.isQuit(msg)) {
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                server.removeClient(socket);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Client.java

package com.xiaojie.net.bio.chatRoom;

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

/**
 * @author Mrli
 * @date 2020/9/26 16:20
 * 多人聊天室客户端
 */
public class Client {
    private final String host = "127.0.0.1";
    private final int port = 6767;
    private final String QUIT = "quit";

    private Socket socket;
    private BufferedReader reader;
    private BufferedWriter writer;

    /**
     * 默认构造器
     */
    public Client() {

    }

    /**
     * 给服务端发送消息
     * @param msg
     */
    public void sendMsg(String msg) throws IOException {
        //socket的输出流未关闭
        if(!socket.isOutputShutdown()) {
            writer.write(msg+"\n");
            writer.flush();
        }
    }

    /**
     * 接收服务端消息
     */
    public String receive() throws IOException {
        String msg = null;

        //socket的输入流未关闭
        if(!socket.isInputShutdown()) {
            msg = reader.readLine();
        }
        return msg;
    }

    /**
     * 检测是否退出
     * @param msg
     * @return
     */
    public boolean isQuit(String msg) {
        return QUIT.equals(msg);
    }

    /**
     * 关闭socket、输出流
     */
    public void close() {
        try {
            writer.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 多人聊天室客户端启动
     */
    public void start() {
        try {
            //创建socket,与serverSocket简历连接
            socket = new Socket(host,port);

            //创建IO流
            reader = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()
            ));
            writer = new BufferedWriter(new OutputStreamWriter(
                    socket.getOutputStream()
            ));

            //创建线程处理,
            new Thread(new InputHandler(this)).start();

            //监听服务器转发的消息
            String msg = null;
            while ((msg = receive()) != null) {
                System.out.println(msg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            close();
        }
    }

    /**
     * 通过Client主线程启动客户端
     * @param args
     */
    public static void main(String[] args) {
        Client client = new Client();
        client.start();
    }
}

InputHandler.java

package com.xiaojie.net.bio.chatRoom;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author Mrli
 * @date 2020/9/26 16:22
 */
public class InputHandler implements Runnable{
    private Client client;

    /**
     * 初始化构造函数
     * @param client
     */
    public InputHandler(Client client) {
        this.client = client;
    }

    /**
     * 主要监听用户输入状态
     */
    @Override
    public void run() {
        //获取本地输入流,监听用户输入
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        while (true) {
            try {
                String msg = reader.readLine();
                if(msg != null) {
                    client.sendMsg(msg);
                }

                if(client.isQuit(msg)) {
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
}



BIO学习到这里,后面IO学习继续更新!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值