一站式学习Java网络编程-学习手记

Posted 爱上口袋的天空

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一站式学习Java网络编程-学习手记相关的知识,希望对你有一定的参考价值。

1. 概念图解

  • BIO模型:客户端每有一个请求,服务端都要有一个线程来单独处理这个请求,典型的一请求一应答,java 1.4版本之前
  • 对于聊天室服务器,它有多个线程,其中一个为图上的Acceptor线程(ChatServer),它实现的就是对来自客户端的请求不断响应,创建分配处理线程;对应客户端的每一个请求,都有一个单独的处理线程
  • 对于客户端来说,它有两个线程,其中一个线程与服务器建立连接,并接收来自服务器的消息;另一个线程则用来处理用户的输入,并将消息发送到服务器

1.1 伪异步的优化

 

 

  • 使用线程池的原因是因为防止一大批用户的响应,造成服务器过载,实现线程的复用,减少不停的创建删除线程造成的资源郎芬,这样也实现了BIO的伪异步IO通信

2. 聊天服务器中的要点

2.1 字段

2.2 转发消息方法

 

2.3 添加客户方法

 

2.4 移除客户方法

 

 2.5 服务器主线程任务

2.6 服务器处理线程任务

  • 实现Runnable接口,之后执行的时候将其传入到线程池中执行。 


3. 聊天室客户端要点

3.1 向服务端发送消息,让服务器转发给其他人

 

3.2 接收服务端的消息 

3.3 主线程任务

 

3.4 处理线程任务

  • 实现Runnable接口,创建线程时启动 


4. 测试结果 

服务器端信息

 


5. 完整代码

5.1 服务器

package Server;

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.ExecutorService;
import java.util.concurrent.Executors;

public class ChatServer {
    private int DEFAULT_PORT = 8888;
    private final String QUIT = "quit";

    private ServerSocket serverSocket;
    //存储已经连接的客户们
    private Map<Integer, Writer> connectedClients;
    private ExecutorService executorService;

    public ChatServer() {
        this.connectedClients = new HashMap<>();
        this.executorService = Executors.newFixedThreadPool(10);
    }

    //添加客户端
    public synchronized void addClient(Socket socket) throws IOException {
        if(socket != null){
            int key = socket.getPort();
            Writer value = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream())
            );
            connectedClients.put(key,value);
            System.out.println("客户" + key + ":已经连接");
        }
    }

    //移除客户端
    public synchronized void removeClient(Socket socket) throws IOException {
        if(socket != null){
            int port = socket.getPort();
            if(connectedClients.containsKey(port)){
                //移除用户的时候要进行关闭
                connectedClients.get(port).close();
                connectedClients.remove(port);
                System.out.println("客户端" + port + ":已经断开连接");
            }
        }
    }

    //将消息转发给其他用户
    public synchronized void forwardMessage(Socket socket,String fwdMsg) throws IOException {
        for(Integer port : connectedClients.keySet()){
            //不转发给自己
            if(!port.equals(socket.getPort())){
                Writer writer = connectedClients.get(port);
                writer.write(fwdMsg);
                writer.flush();
            }
        }
    }

    //检查是否退出
    public boolean readyToQuit(String msg){
        return QUIT.equals(msg);
    }

    public void close(){
        if(serverSocket != null){
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //启动
    public void start(){
        try {
            //绑定监听端口
            serverSocket = new ServerSocket(DEFAULT_PORT);
            System.out.println("聊天室服务器已经成功启动!");

            while (true){
                Socket socket = serverSocket.accept();

                //为每个socket创建一条单独的线程进行处理
                //new Thread(new ChatHandler(socket,this)).start();
                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();
    }
}

package Server;

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

public class ChatHandler implements Runnable {

    private Socket socket;
    private ChatServer chatServer;

    public ChatHandler(Socket socket, ChatServer chatServer) {
        this.socket = socket;
        this.chatServer = chatServer;
    }

    @Override
    public void run() {
        try {
            //添加对象
            chatServer.addClient(socket);

            //读取用户发送的信息
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(socket.getInputStream())
            );
            String msg = null;
            //必须要读取到换行符
            while ((msg = reader.readLine()) != null){
                String fwdMsg ="客户端" + socket.getPort() + ":" + msg + "\\n";
                chatServer.forwardMessage(socket,fwdMsg);
                System.out.print(fwdMsg);

                //检查是否退出
                if(chatServer.readyToQuit(msg)){
                    break;
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                chatServer.removeClient(socket);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

5.2 客户端

package Client;

import Server.ChatServer;

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

public class ChatClient {
    private String DEFAULT_SERVER_HOST = "127.0.0.1";
    private int DEFAULT_PORT = 8888;
    private final String QUIT = "quit";

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

    //发送消息给服务器
    public void sendMsg(String msg) throws IOException {
        //输出流没有关闭的情况
        if(!socket.isOutputShutdown()){
            writer.write(msg + "\\n");
            writer.flush();
        }
    }

    //接受消息从服务器
    public String receiveMsg() throws IOException {
        if(!socket.isInputShutdown()){
            return reader.readLine();
        }
        return null;
    }

    //检查是否退出
    public boolean readyToQuit(String msg){
        return QUIT.equals(msg);
    }

    //关闭
    public void close(){
        try {
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //启动开关
    public void start(){
        try {
            //创建socket
            socket = new Socket(DEFAULT_SERVER_HOST,DEFAULT_PORT);
            //创建io流
            writer = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream())
            );
            reader = new BufferedReader(
                    new InputStreamReader(socket.getInputStream())
            );

            //处理用户的输入的线程
            new Thread(new UserInputHandler(this)).start();

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


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

package Client;

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

public class UserInputHandler implements Runnable{
    private ChatClient chatClient;

    public UserInputHandler(ChatClient chatClient) {
        this.chatClient = chatClient;
    }


    @Override
    public void run() {
        //等待用户输入信息
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(System.in)
        );

        while (true){
            try {
                String msg = reader.readLine();
                //向服务器发送消息
                chatClient.sendMsg(msg);
                if(chatClient.readyToQuit(msg))
                    break;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

以上是关于一站式学习Java网络编程-学习手记的主要内容,如果未能解决你的问题,请参考以下文章

一站式学习Java网络编程-学习手记

一站式学习Java网络编程-学习手记

一站式学习Java网络编程-学习手记

一站式学习Java网络编程-学习手记

一站式学习Java网络编程

Python网络爬虫学习手记——爬虫基础