Java网络编程入门(二)

前言:这是本人在慕课网上学习 一站式学习Java网络编程 全面理解BIO/NIO/AIO 时所做的笔记,供本人复习之用,本文主要讲述BIO,并实现一个基于BIO的聊天室,不保证一定准确。

目录

第一章 一个简单的客户端、服务端通信的例子

第二章 BIO编程模型

2.1 基于BIO的聊天室

2.2 聊天室服务端

2.3 聊天室客户端

第三章 使用伪异步IO改进多人聊天室


第一章 一个简单的客户端、服务端通信的例子

懒得讲解了,直接上代码,应该都能看懂

1.1 服务端

package bio;

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

/**
 * @author ZhangChen
 **/
public class Server {
    public static void main(String[] args) {
        final String quit = "quit";
        final int port =8888;
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(port);
            System.out.println("启动了服务器,监听端口"+port);
            while(true){
                //等待客户端连接
                Socket socket = serverSocket.accept();
                System.out.println("客户端["+socket.getPort()+"]已连接");
                BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

                //读取客户端发送的消息
                //readline是读出行分隔符之前的所有信息
                String msg = null;
                while((msg=reader.readLine())!=null) {
                    System.out.println("客户端[" + socket.getPort() + "]: " + msg);
                    //回复客户发送的消息
                    writer.write("服务器: " + msg + "\n");
                    writer.flush();
                    if(msg.equals(quit)){
                        System.out.println("客户端["+socket.getPort()+"]已断开连接");
                        break;
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(serverSocket!=null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

1.2 客户端

package bio;

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

/**
 * @author ZhangChen
 **/
public class Client {
    public static void main(String[] args) {
        final String quit = "quit";
        final String host = "127.0.0.1";
        final int port = 8888;
        Socket socket = null;
        BufferedWriter writer =null;
        try {
            socket = new Socket(host,port);
            //创建IO流
            BufferedReader reader =  new BufferedReader(new InputStreamReader(socket.getInputStream()));
            writer =  new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            //等待用户输入消息
            while(true) {
                BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
                String input = consoleReader.readLine();
                //发送消息给服务器
                writer.write(input + "\n");
                writer.flush();
                //读取服务器
                String msg = reader.readLine();
                System.out.println(msg);
                if(input.equals(quit)){
                    break;
                }
            }

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

    }
}

第二章 BIO编程模型

在上面的代码中,当服务器处理一个客户端请求时,就没办法再处理第二个请求了,我们可以在服务器端再创建一个线程,比如我们将它称为handler,它处理和客户端各种数据的交换,这就是BIO编程模型。

2.1 基于BIO的聊天室

功能:就是每个客户发送的消息能被转发给其他的用户。

客户端需要两个线程:一个读服务器的,一个向服务器写

服务端需要获取线程输出流的集合以把消息进行转发

2.2 聊天室服务端

服务端主要是当客户端有请求过来的时候,就开一个新线程来处理请求,主线程循环等待客户端连接,采用主机的端口号来映射连接。

服务器:

package bio.chat;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ZhangChen
 **/
public class ChatServer {
    private int port = 8888;
    private final String quit = "quit";
    private ServerSocket serverSocket;
    private Map<Integer, Writer> connectedClients ;

    public ChatServer(){
        connectedClients = new HashMap<>();
    }

    public synchronized void addClient(Socket socket) throws IOException {
        if(socket!=null){
            int clientPort = socket.getPort();
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            connectedClients.put(clientPort,bufferedWriter);
            System.out.println("客户端["+clientPort+"]已连接到服务器");
        }
    }

    public synchronized void removeClient(Socket socket) throws IOException {
        if(socket!=null){
            int clientPort = socket.getPort();
            if(connectedClients.containsKey(port)){
                connectedClients.get(port).close();
            }
            connectedClients.remove(clientPort);
            System.out.println("客户端["+clientPort+"]已连断开连接 ");
        }
    }

    public synchronized void forwardMessage(Socket socket,String fwdMsg) throws IOException {
        for(Integer id:connectedClients.keySet()){
            if(!id.equals(socket.getPort())){
                Writer writer = connectedClients.get(id);
                writer.write(fwdMsg);
                writer.flush();
            }
        }
    }
    public boolean readyToQuit(String msg){
        return quit.equals(msg);
    }
    public synchronized void close(){
        if(serverSocket!=null){
            try {
                serverSocket.close();
                System.out.println("关闭serverSocket");
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }

    public void start(){
        //绑定监听端口
        try {
            serverSocket = new ServerSocket(port);
            System.out.println("启动服务器,监听端口: "+port+"...");
            while (true){
                //等待客户端连接
                Socket socket = serverSocket.accept();
                //创建ChatHandler线程
                addClient(socket);
                new Thread(new ChatHandler(socket,this)).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close();
        }
    }

    public static void main(String[] args) {
        ChatServer chatServer = new ChatServer();
        chatServer.start();

    }
}

处理线程:

package bio.chat;

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

/**
 * @author ZhangChen
 **/
public class ChatHandler implements Runnable{
    private ChatServer server;
    private Socket socket;
    public ChatHandler(Socket socket,ChatServer chatServer){
        this.server = chatServer;
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String msg = null;
            while((msg=reader.readLine())!=null){
                String fwdMsg  = "客户端["+socket.getPort()+"]: "+msg+"\n";
                System.out.print(fwdMsg);
                if(server.readyToQuit(msg)){
                    break;
                }
                server.forwardMessage(socket,fwdMsg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                server.removeClient(socket);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

 

 

2.3 聊天室客户端

客户端主要是两个线程,一个主线程监听服务器发来的消息,一个副线程处理用户的输入请求,将请求法向服务器。

主线程:

package bio.chat;

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

/**
 * @author ZhangChen
 **/
public class ChatClient {
    final String quit = "quit";
    final String host = "127.0.0.1";
    final int port = 8888;
    private Socket socket;
    private BufferedWriter writer;
    private BufferedReader reader;
    public ChatClient(){

    }
    //发送消息给服务端
    public void send(String msg) throws IOException {
        if(!socket.isOutputShutdown()){
            writer.write(msg+"\n");
            writer.flush();
        }
    }
    //从服务端接收消息
    public String receive() throws IOException {
        String msg = null;
        if(!socket.isInputShutdown()){
            msg = reader.readLine();
        }
        return msg;
    }

    public boolean readyToQuit(String msg){
        return quit.equals(msg);
    }
    public void start (){
        try {
            //创建socket
            socket = new Socket(host,port);
            //创建IO流
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            //处理用户输入
            new Thread(new UserInputHandler(this)).start();
            //读取服务器转发的消息
            String msg = null;
            while ((msg = receive())!=null){
                System.out.println(msg);
            }
        }  catch (IOException e) {
            e.printStackTrace();
        } finally {
            close();
        }
    }

    public void close(){
        if(writer!=null){
            try {
                System.out.println("关闭socket");
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    public static void main(String[] args) {
        ChatClient chatClient = new ChatClient();
        chatClient.start();
    }

}

 

监听用户输入的线程:

package bio.chat;

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

/**
 * @author ZhangChen
 **/
public class UserInputHandler implements Runnable{
    private ChatClient chatClient;
    public UserInputHandler(ChatClient chatClient){
        this.chatClient = chatClient;
    }

    @Override
    public void run() {
        //等待用户输入消息
        BufferedReader reader =null;
        try {
            reader = new BufferedReader(new InputStreamReader(System.in));
            String msg = null;
            while(true){
                msg = reader.readLine();
                chatClient.send(msg);
                if(chatClient.readyToQuit(msg)){
                    chatClient.close();
                    break;
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        } finally {
            if(reader!=null){
                try {
                    reader.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }

    }
}

第三章 使用伪异步IO改进多人聊天室

如果每来一个连接请求则开一个线程,那么有成千上百个请求的时候就会出问题,所以可以用线程池来解决这个问题。

public class ChatServer {
    private int port = 8888;
    private final String quit = "quit";
    private ServerSocket serverSocket;
    private Map<Integer, Writer> connectedClients ;
    private ExecutorService executorService;

    public ChatServer(){
        connectedClients = new HashMap<>();
        executorService = new ThreadPoolExecutor(10, 10, 0, TimeUnit.MILLISECONDS,,new LinkedBlockingQueue<Runnable>(),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
    }

    public synchronized void addClient(Socket socket) throws IOException {
        if(socket!=null){
            int clientPort = socket.getPort();
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            connectedClients.put(clientPort,bufferedWriter);
            System.out.println("客户端["+clientPort+"]已连接到服务器");
        }
    }

    public synchronized void removeClient(Socket socket) throws IOException {
        if(socket!=null){
            int clientPort = socket.getPort();
            if(connectedClients.containsKey(port)){
                connectedClients.get(port).close();
            }
            connectedClients.remove(clientPort);
            System.out.println("客户端["+clientPort+"]已连断开连接 ");
        }
    }

    public synchronized void forwardMessage(Socket socket,String fwdMsg) throws IOException {
        for(Integer id:connectedClients.keySet()){
            if(!id.equals(socket.getPort())){
                Writer writer = connectedClients.get(id);
                writer.write(fwdMsg);
                writer.flush();
            }
        }
    }
    public boolean readyToQuit(String msg){
        return quit.equals(msg);
    }
    public synchronized void close(){
        if(serverSocket!=null){
            try {
                serverSocket.close();
                System.out.println("关闭serverSocket");
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }

    public void start(){
        //绑定监听端口
        try {
            serverSocket = new ServerSocket(port);
            System.out.println("启动服务器,监听端口: "+port+"...");
            while (true){
                //等待客户端连接
                Socket socket = serverSocket.accept();
                //创建ChatHandler线程
                addClient(socket);
                executorService.execute(new ChatHandler(socket,this));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close();
        }
    }

    public static void main(String[] args) {
        ChatServer chatServer = new ChatServer();
        chatServer.start();

    }
}

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值