Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java是等待的最佳方式&;从客户那里获取数据_Java_Multithreading_Sockets_Networking - Fatal编程技术网

Java是等待的最佳方式&;从客户那里获取数据

Java是等待的最佳方式&;从客户那里获取数据,java,multithreading,sockets,networking,Java,Multithreading,Sockets,Networking,我开始使用JDK中的主网络包学习网络,通过几个示例,它非常简单。但现在我有兴趣制作多客户端应用程序,比如聊天系统 到目前为止,我的结构想法如下: public class Chat implements Packet { @Override public void processPacket(Session c) { String message = readMessage(c); System.out.println("Message: " +

我开始使用JDK中的主网络包学习网络,通过几个示例,它非常简单。但现在我有兴趣制作多客户端应用程序,比如聊天系统

到目前为止,我的结构想法如下:

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;
    }
}
连接处理程序类,它处理传入的连接,并保存客户端列表。 如果找到新连接,创建一个新的客户端对象,启动它的线程(客户端对象将实现runnable,因此它将启动自己的循环服务,它将循环接收到的新数据包),并将其添加到列表中

我为每个客户机创建一个新线程,而不是在所有客户机之间循环,因为从客户机读取进程会停止整个执行,并等待客户机发送数据,这有点让我恼火,这是我的问题

我已经创建了一个简单的控制台应用程序,可以从客户端接收消息,但现在我想检测断开的连接。我读到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");
}
这就是我读取字节的方式:

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;
}
好的,我的问题是:

添加is disconnected检测后,当我发送消息时,什么也没有发生。这可能是由于.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);
    }   
}
我的问题是:在不让应用程序等待并每次实际循环的情况下,从客户机读取数据的更好方法是什么?或者我应该有一个新的线程来检查用户是否在线,这样每个客户端就有2个线程

如果有人需要我的会话对象(客户端对象):

公共类会话扩展线程实现可运行{
专用套接字会话;
私人客户;
私人打印输出;
中的私有缓冲区读取器;
私有包处理程序包;
私有数据输入流数据输入;
私有连接处理器连接;
私人最终整数检查时间=1600;
私有最终整数最大时间=22000;
私人长期支票;
公开会话(套接字会话){
this.session=会话;
this.client=新客户机(this);
试一试{
这是setStream();
}捕获(IOE异常){
e、 printStackTrace();
}
this.packets=新的PacketHandler(this);
System.out.println(“[创建的新会话]:”+session.getRemoteSocketAddress());
}   
public void setConnectionHandler(ConnectionHandler c){
这个连接=c;
}
公开募捐{
试一试{
这个.startClientService();
}捕获(IOE异常){
e、 printStackTrace();
}
}
public void setStream()引发IOException{
this.out=新的PrintWriter(this.session.getOutputStream());
this.in=new BufferedReader(new InputStreamReader(this.session.getInputStream());
this.dataIn=新的DataInputStream(this.session.getInputStream());
}
公共客户端getClient(){
将此文件退还给客户;
}
公共字节[]读取字节(){
内伦;
字节[]数据=null;
试一试{
len=this.dataIn.readInt();
数据=新字节[len];
如果(len>0){
此.dataIn.readFully(数据);
}
}捕获(IOE异常){
e、 printStackTrace();
}       
返回数据;
}
公共字符串readMessage(){
试一试{
返回此.in.readLine();
}捕获(IOE异常){
e、 printStackTrace();
}
返回null;
}
公共布尔值未连接(){
试一试{
this.in.read();
this.lastCheck=System.currentTimeMillis();
返回true;
}捕获(IOE异常){
如果(!inConnection()){
System.out.println(“用户断开连接”);
试一试{
这是destruct();
}捕获(IOE1异常){
e1.printStackTrace();
}
}
}
返回false;
}
私有布尔连接(){
返回系统.currentTimeMillis()-lastCheck600){
System.out.println(System.currentTimeMillis()-this.checkTime);
if(this.isConnected()){
int packetType=this.dataIn.readInt();
packets.getPacket(packetType);
}
}
}
}
public void destruct()引发IOException{
this.session.close();
this.connection.removeClient(this);
System.out.println(“会话终止”);
}
}

谢谢

虽然我没有时间查看所有的代码,但这里有两件事可以帮助您

1) 使用已定义的消息头。定义客户端将发送到服务器的每条消息的X字节数。使用这些字节定义消息的长度以及消息的类型。服务器知道此头的长度和布局,并使用它以特定的方式处理消息。示例可以是一个字节的头。值1可能是“我已连接”消息。2可能是我即将断开连接。3可能是我现在不在,4可能是收到的聊天信息

2) 有两种方法可以处理输入。首先是使用阻塞IO,并创建一个单独的线程来接收m
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");
    }

}