繁体   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