如何将经典的多线程服务器转换为java.nio非阻塞服务器?

时间:2019-04-21 10:42:30

标签: java networking tcp server nio

我想将当前的多线程阻塞服务器转换为非阻塞服务器。该服务器用作在线游戏服务器(TCP),并处理来自多个客户端/玩家的数据。我遵循了许多教程,展示了如何制作非阻塞式回显服务器,但是我仍然不知道如何将当前的阻塞式服务器转换为非阻塞式服务器。

我当前的服务器基本上是这样的: main.java文件接受新的客户端连接,并为每个连接创建一个新线程(ClientThread.java)。 ServerInformation.java创建一个新的游戏服务器,其他客户端可以加入该服务器。

我当前的多线程阻塞服务器为每个客户端连接创建一个新线程,这对于非阻塞服务器是可行的,因为否则我不知道如何处理ClientThread.java文件。

如果有人可以提供示例,教程或代码解决方案,那就太好了。

Main.java

import java.net.Socket;
import java.net.SocketAddress;
import java.net.InetSocketAddress;
import java.io.IOException;
import java.util.HashMap;
import java.net.ServerSocket;
import java.net.SocketTimeoutException;

public class Main
{
    static ServerSocket serverSocket_;
    static HashMap<String, ServerInformation> servers_;
    static int verboseLevel_;
    static int threadTimeout_;
    static int masterPort_;
    static int serverNumber_;
    static int socketTimeOut_;

    static {
        Main.serverSocket_ = null;
        Main.servers_ = new HashMap<String, ServerInformation>();
        Main.verboseLevel_ = 5;
        Main.threadTimeout_ = 10;
        Main.masterPort_ = 6510;
        Main.serverNumber_ = 1;
        Main.socketTimeOut_ = 6000;
    }

    public static void main(final String[] args) {
        try {
            setupServerAndCleanup(Main.masterPort_);
            while (true) {
                handleIncomingConnection();
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    static void setupServerAndCleanup(final int port) throws IOException {
        (Main.serverSocket_ = new ServerSocket()).setReuseAddress(true);
        Main.serverSocket_.bind(new InetSocketAddress(Main.masterPort_));
        System.out.println("Server socket up and running on port " + Main.masterPort_);
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                if (Main.serverSocket_ != null) {
                    try {
                        Main.serverSocket_.close();
                        System.out.println("Server socket closed, port released");
                    }
                    catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }));
    }

    static void handleIncomingConnection() throws IOException {
        final Socket clientSocket = Main.serverSocket_.accept();
        clientSocket.setSoTimeout(Main.socketTimeOut_);
        final ClientThread client = new ClientThread(clientSocket);
        client.start();
    }

}

ClientThread.java

import java.util.Iterator;
    import java.io.IOException;
    import java.io.Reader;
    import java.io.InputStreamReader;
    import java.util.regex.Pattern;
    import java.io.BufferedReader;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.net.SocketTimeoutException;

    public class ClientThread extends Thread
    {
    Socket clientSocket_;
    String clientIp_;
    String serverIp_;
    ServerInformation server_;
    PrintWriter out_;
    BufferedReader in_;
    boolean prepareTermination_;
    boolean terminated_;
    private static final Pattern numberPattern;

    static {
        numberPattern = Pattern.compile("\\d+");
    }

    public ClientThread(final Socket sock) {
        this.clientSocket_ = sock;
        this.clientIp_ = this.clientSocket_.getRemoteSocketAddress().toString();
        this.serverIp_ = null;
        this.server_ = null;
        this.prepareTermination_ = false;
        this.terminated_ = false;
    }

    @Override
    public void run() {
        try {
            this.out_ = new PrintWriter(this.clientSocket_.getOutputStream(), true);
            this.in_ = new BufferedReader(new InputStreamReader(this.clientSocket_.getInputStream()));

            long lastActionTime = System.currentTimeMillis();
            while (true) {

                if (this.in_.ready() || System.currentTimeMillis() - lastActionTime >= 1000 * Main.threadTimeout_) {
                    if (System.currentTimeMillis() - lastActionTime >= 1000 * Main.threadTimeout_) {
                        //this.logDebugMessage(3, "Thread was killed due to prolonged inactivity (" + Main.threadTimeout_ + " seconds)");
                        this.terminateThread();
                        return;
                    }

                    final String tempInputLine;
                    if(((tempInputLine = this.in_.readLine()) == null )){
                        this.terminateThread(); //end thread                        
                        return;             
                    }
                    else
                    {                   
                        lastActionTime = System.currentTimeMillis();                    
                        final String inputLine = tempInputLine.trim();
                        if (ClientThread.numberPattern.matcher(inputLine).matches()){
                        final int val = Integer.parseInt(inputLine);
                        switch (val) {
                          case 1: { //send data to other players
                                final int parseCount = Integer.parseInt(this.in_.readLine().trim());
                                final StringBuilder msg = new StringBuilder();
                                for (int j = 0; j < parseCount; ++j) {
                                    msg.append(String.valueOf(this.in_.readLine().trim()) + "|");
                                }
                                for (final ClientThread thread2 : this.server_.ipToClientThread_.values()) {
                                    if (thread2 != this) {
                                        thread2.out_.print(msg);
                                        thread2.out_.flush();
                                    }
                                }
                                //this.logDebugMessage(5, "Packet for others: '" + msg.toString() + "'");
                                break;
                            }   

                            case 2: { //remove game server
                                //this.logDebugMessage(1, "A game server has been deleted, ip: " + ipServer);
                                Main.servers_.remove(this.server_.ip_);
                                this.serverIp_ = null;
                                for (final ClientThread thread : this.server_.ipToClientThread_.values()) {
                                    thread.prepareTermination_ = true;
                                }
                                this.terminateThread();
                                return;
                            }
                            case 3: { //connect new client
                                final String ipServer = this.in_.readLine().trim();
                                final String ipClient = this.in_.readLine().trim(); 
                                this.logDebugMessage(1, "A client wishes to connect to a server, client: " + ipClient + ", server: " + ipServer);
                                final ServerInformation info = Main.servers_.getOrDefault(ipServer, null);
                                if (info == null) {
                                    System.out.println("Connection to the server failed, no such server in the server list");
                                   this.out_.print("*" + 1 + "|" + 1 + "~" + "|");
                                   this.out_.flush();                                   
                                break;
                                }
                                this.server_ = info;
                                this.server_.ipToClientThread_.put(ipClient, this);
                                this.logDebugMessage(1, "Connection success");
                                this.logDebugMessage(5,"Map: " + this.server_.ipToClientThread_);
                                    this.out_.print("*" + 1 + "|" + 2 + "~" + "|");
                                    this.out_.flush();
                                break;
                            }         
                            case 4: { //disconnect client
                                final String ipClient = this.in_.readLine().trim();
                                this.server_.ipToClientThread_.remove(ipClient);
                                this.logDebugMessage(1, String.valueOf(ipClient) + " disconnected from the server at " + this.server_.ip_);
                                this.serverIp_ = null;
                                this.terminateThread();
                                return;
                            }                   

                            case 5: { //host create new game
                                if (Main.serverNumber_ > 1000000) {
                                Main.serverNumber_ = 10;    
                                }
                                Main.serverNumber_ += 1;                                
                                final String ipServer = Integer.toString(Main.serverNumber_); //unique server number
                                final String ipHost =  this.in_.readLine().trim(); //host 
                                final String name = this.in_.readLine().trim(); //Server name
                                final String description = this.in_.readLine().trim(); //class
                                final String servervar1 = this.in_.readLine().trim(); //max players
                                final String servervar3 = this.in_.readLine().trim(); //current lap
                                final String servervar4 = this.in_.readLine().trim(); //total laps
                                final String servervar5 = this.in_.readLine().trim(); //status
                                final String servervar6 = this.in_.readLine().trim(); //Password
                                final String servervar7 = this.in_.readLine().trim(); //Online version
                                final String servervar8 = this.in_.readLine().trim(); //Game server
                                final long servervar9 = System.currentTimeMillis(); //server creation time
                                //this.logDebugMessage(1, "A game server has been registered, ip: " + ipServer + ", name: " + name + ", description: " + description + ", servervar1: " + servervar1);
                                final ServerInformation gameServer = new ServerInformation(name, servervar1, servervar3, servervar4, servervar5, servervar6, servervar7, servervar8, servervar9, ipHost, ipServer, this.clientSocket_, this.out_, this.in_);
                                gameServer.description_ = description;
                                gameServer.ipToClientThread_.put(ipHost, this);
                                this.server_ = gameServer;
                                Main.servers_.put(ipServer, gameServer);
                                this.serverIp_ = ipServer;
                                break;
                            }                               
                            default: {
                                this.logDebugMessage(0, "Unrecognized case: '" + inputLine + "', " + val);
                                break;
                            }
                        }
                    }
                    else if (inputLine.length() > 0) {
                        this.logDebugMessage(0, "Unformated '" + inputLine + "'");
                        if (this.server_ != null) {
                            this.server_.out_.print(inputLine);
                            this.server_.out_.flush();
                        }
                    }
                    if (this.prepareTermination_) {
                        this.terminateThread();
                        return;
                    }
                    continue;
                    }
                }
            }
        }
        catch (SocketTimeoutException e) {
            e.printStackTrace();
            try {
                this.terminateThread();
            }
            catch (IOException e2) {
                e2.printStackTrace();
            }
        }
        catch (IOException e3) {
            e3.printStackTrace();
            try {
                this.terminateThread();
            }
            catch (IOException e4) {
                e4.printStackTrace();
            }
        }
    }

    //debug messages
    void logDebugMessage(final int requiredVerbose, final String msg) {
        if (Main.verboseLevel_ >= requiredVerbose) {
            System.out.println("[" + this.clientIp_ + "]  " + msg);
        }
    }

    //terminate thread
    void terminateThread() throws IOException {
        if (!this.terminated_) {
            if (this.serverIp_ != null) {
                Main.servers_.remove(this.serverIp_);
            }
            this.clientSocket_.close();
            this.in_.close();
            this.out_.close();
            this.logDebugMessage(3, "Cleanup successful");
            this.terminated_ = true;
        }
    }
}

ServerInformation.java

import java.util.HashMap;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.net.Socket;

public class ServerInformation
{
    public String name_; //Server name
    public String description_; //class
    public String servervar1_; //max players
    public long servervar2_; //server last update
    public String servervar3_; //current lap
    public String servervar4_; //total laps
    public String servervar5_; //status
    public String servervar6_; //password
    public String servervar7_;  //online version    
    public String servervar8_;  //game server   
    public long servervar9_;    //server creation time      
    public String ipHost_; //ip host
    public String ip_; //unique id server
    public Socket socket_;
    PrintWriter out_;
    BufferedReader in_;
    HashMap<String, ClientThread> ipToClientThread_;

    public ServerInformation(final String name, final String servervar1, final String servervar3, final String servervar4, final String servervar5, final String servervar6, final String servervar7, final String servervar8, final long servervar9, final String ipHost, final String ip, final Socket socket, final PrintWriter out, final BufferedReader in) {
        this.name_ = name;
        this.description_ = ""; //class
        this.servervar1_ = servervar1; //max players
        this.servervar2_ = System.currentTimeMillis(); //server last update
        this.servervar3_ = servervar3; //current lap
        this.servervar4_ = servervar4; //total laps
        this.servervar5_ = servervar5; //status
        this.servervar6_ = servervar6; //password
        this.servervar7_ = servervar7; //online version 
        this.servervar8_ = servervar8; //game server        
        this.servervar9_ = servervar9; //server creation time           
        this.ipHost_ = ipHost;
        this.ip_ = ip;
        this.socket_ = socket;
        this.out_ = out;
        this.in_ = in;
        this.ipToClientThread_ = new HashMap<String, ClientThread>();
    }
}

0 个答案:

没有答案