簡體   English   中英

Java是等待和從客戶端獲取數據的最佳方法

[英]Java the best way of waiting & getting data from your client

我開始使用JDK中的主要網絡軟件包學習網絡,在舉幾個例子之后,這非常簡單。 但是現在,我對制作像聊天系統這樣的多客戶端應用程序很感興趣。

到目前為止,我的結構思想是這樣的:

連接處理程序類,用於處理傳入的連接並保存客戶端列表。 如果找到了新的連接,則創建一個新的客戶端對象,啟動它的線程(Client對象將實現可運行,因此它將啟動它自己的循環服務,它將為收到的新數據包循環),並將其添加到列表中。

我為每個客戶端創建一個新線程,而不是遍歷所有客戶端,因為從客戶端進程中讀取將停止整個執行過程,並且將等待客戶端發送數據,這讓我很煩,這是我的問題。

我創建了一個簡單的控制台應用程序,可以接收來自客戶端的消息,但是現在我想檢測斷開連接。 我讀到的是,如果用戶未連接,則bufferedReader .read()方法將返回-1,因此我認為我可以循環執行此操作,並且每隔數秒便對每個客戶端進行一次操作,但事實是,客戶端必須向發送一個數據包。 read(),所以,如果您執行.read(),它將等待並停止整個線程,直到接收到數據包為止(我認為)。

這是我當前從客戶端獲取消息的代碼:

public boolean isConnected() {
    try {
        this.in.read();
        this.lastCheck = System.currentTimeMillis();
        return true;
    } catch (IOException e) {
        if (!inConnection()) {
            System.out.println("User disconnected");
            try {
                this.destruct();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }
    return false;
}

private boolean inConnection() {
    return System.currentTimeMillis() - lastCheck < this.maxTime;
}

public void startClientService() throws IOException {
    while(!this.session.isClosed()) {
        if (System.currentTimeMillis() - this.checkTime > 600) {
            System.out.println(System.currentTimeMillis() - this.checkTime);
            if (this.isConnected()) {
                int packetType = this.dataIn.readInt();
                packets.getPacket(packetType);
            }
        }
    }
}

public void destruct() throws IOException {
    this.session.close();
    this.connection.removeClient(this);
    System.out.println("Session killed");
}

基本上發生了什么,我從客戶端發送一個打包的整數,我可能有很多事情要做,因此我可以設置許多唯一的數據包ID,因此,如果我想接收和處理聊天消息,則數據包ID為216,客戶端發送一個int 216,服務器讀取該數據包,進入所有數據包ID的切換循環,並檢測其是否真正為216,如果是,它將獲取處理消息的打包類的實例並獲取接收到的消息的字節,如下所示:

public class Chat implements Packet {

    @Override
    public void processPacket(Session c) {
        String message = readMessage(c);
        System.out.println("Message: " + message);
    }

    private String readMessage(Session c) {
        byte[] data = c.readBytes();
        String message = null;
        try {
            message = new String(data, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return message;
    }
}

這就是我讀取字節的方式:

public byte[] readBytes() {
    int len;
    byte[] data = null;
    try {
        len = this.dataIn.readInt();
        data = new byte[len];
        if (len > 0) {
            this.dataIn.readFully(data);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }       
    return data;
}

好的,我的問題是:

添加“斷開連接檢測”后,當我發送消息時,什么也沒有發生。 這可能是由於.read()停止並正在等待響應。 但是,如果我再次寫一條消息,我將在服務器中收到該消息。

這是我臨時的,丑陋的客戶:

public class Client {

    public static void main(String[] args) throws UnknownHostException, IOException {
        Socket socket = new Socket("127.0.0.1", 43594);
        Scanner r = new Scanner(System.in);
        PrintWriter out = new PrintWriter(socket.getOutputStream());
        String input;
        while(true) {
            input = r.next();
            if (input != null) {
                sendMessage(input, out);
            }
        }

    }

    public static void sendMessage(String message, PrintWriter out) {
        byte[] encoded = encode(message);
        out.write(0);
        out.println(encoded + "\n");
        out.flush();
    }

    public static byte[] encode(String s) {
        return DatatypeConverter.parseBase64Binary(s);
    }

    public static String decode(byte[] s) {
        return DatatypeConverter.printBase64Binary(s);
    }   
}

我的問題是:有什么更好的方法從客戶端讀取數據,而無需讓應用程序等待數據並實際上每次循環? 或者,也許我應該有一個新線程來檢查用戶是否在線,所以每1個客戶端2個線程?

如果有人需要我的會話對象(客戶端對象):

public class Session extends Thread implements Runnable {

    private Socket session;
    private Client client;
    private PrintWriter out;
    private BufferedReader in;  
    private PacketHandler packets;
    private DataInputStream dataIn;
    private ConnectionHandler connection;

    private final int checkTime = 1600;
    private final int maxTime = 22000;
    private long lastCheck;

    public Session(Socket session) {
        this.session = session;
        this.client = new Client(this);
        try {
            this.setStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.packets = new PacketHandler(this);
        System.out.println("[New session created]: " + session.getRemoteSocketAddress());
    }   

    public void setConnectionHandler(ConnectionHandler c) {
        this.connection = c;
    }

    public void run() {
        try {
            this.startClientService();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void setStream() throws IOException {
        this.out = new PrintWriter(this.session.getOutputStream());
        this.in = new BufferedReader(new InputStreamReader(this.session.getInputStream()));
        this.dataIn = new DataInputStream(this.session.getInputStream());
    }

    public Client getClient() {
        return this.client;
    }

    public byte[] readBytes() {
        int len;
        byte[] data = null;
        try {
            len = this.dataIn.readInt();
            data = new byte[len];
            if (len > 0) {
                this.dataIn.readFully(data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }       
        return data;
    }

    public String readMessage() {
        try {
            return this.in.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public boolean isConnected() {
        try {
            this.in.read();
            this.lastCheck = System.currentTimeMillis();
            return true;
        } catch (IOException e) {
            if (!inConnection()) {
                System.out.println("User disconnected");
                try {
                    this.destruct();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
        return false;
    }

    private boolean inConnection() {
        return System.currentTimeMillis() - lastCheck < this.maxTime;
    }

    public void startClientService() throws IOException {
        while(!this.session.isClosed()) {
            if (System.currentTimeMillis() - this.checkTime > 600) {
                System.out.println(System.currentTimeMillis() - this.checkTime);
                if (this.isConnected()) {
                    int packetType = this.dataIn.readInt();
                    packets.getPacket(packetType);
                }
            }
        }
    }

    public void destruct() throws IOException {
        this.session.close();
        this.connection.removeClient(this);
        System.out.println("Session killed");
    }

}

謝謝!

雖然我沒有時間查看所有代碼,但是有兩點可以幫助您。

1)使用定義的消息頭。 定義客戶端將發送到服務器的每個消息的X字節數。 使用這些字節來定義消息將持續多長時間以及消息的類型。 服務器知道此標頭的長度和布局,並使用它以特定方式處理消息。 示例可能是一個字節的標頭。 值1可能是我已連接的消息。 2可能是我即將斷開連接。 3可能是我當前不在,4可能是傳入的聊天消息。

2)有兩種方法可以處理輸入。 首先是使用阻塞IO,並創建一個單獨的線程來接收來自每個客戶端的消息。 我相信這就是您當前正在做的事情。 第二個是使用非阻塞IO,並在打開的套接字上迭代一個單獨的線程並進行讀取。 非阻塞將檢查是否有要讀取的數據,但如果沒有,線程將繼續執行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM