Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/317.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.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 NIO聊天应用程序无法为多个客户端正常工作_Java_Nio_Socketchannel - Fatal编程技术网

Java NIO聊天应用程序无法为多个客户端正常工作

Java NIO聊天应用程序无法为多个客户端正常工作,java,nio,socketchannel,Java,Nio,Socketchannel,我一直在开发一个基于NIO的聊天应用程序,其逻辑非常简单:任何客户端发送的任何消息都应该对其他用户可见 现在,我在工作的中间,我有相当完整的客户端类(以及他们的代码> GUI 部分)和服务器,但是我遇到了一个问题,我在任何地方都找不到解决方案。也就是说,如果我运行一个服务器实例和一个客户端实例,在我的控制台(一个用于客户端,一个用于服务器)中,我会看到一个很好的预期对话。但是,在添加额外的客户端之后,这个新创建的客户端不会从服务器获得响应——第一个客户端仍然具有有效的连接 我还没有考虑向所有客户

我一直在开发一个基于NIO的聊天应用程序,其逻辑非常简单:任何客户端发送的任何消息都应该对其他用户可见

现在,我在工作的中间,我有相当完整的客户端类(以及他们的代码> GUI <代码>部分)和服务器,但是我遇到了一个问题,我在任何地方都找不到解决方案。也就是说,如果我运行一个服务器实例和一个客户端实例,在我的控制台(一个用于客户端,一个用于服务器)中,我会看到一个很好的预期对话。但是,在添加额外的客户端之后,这个新创建的客户端不会从服务器获得响应——第一个客户端仍然具有有效的连接

我还没有考虑向所有客户机广播消息,现在我想解决我的每个客户机和服务器之间缺乏适当通信的问题,因为我认为如果通信良好,广播不应该有什么大不了的

我还想补充一点,我已经尝试了许多其他实例化客户机的方法:在一个线程中,首先实例化客户机,然后在客户机上应用方法,我尝试了使用
invokeLater
from
SwingUtilities
,因为这是启动
GUI
的正确方法。不幸的是,这两种方法都不奏效

我应该改变什么来实现客户端和服务器之间的正确通信?我做错了什么

这是来自客户端控制台的日志:

Awaiting message from: client2...
Awaiting message from: client1...

after creating the clients - before any action

1 Message: client1 :: simpleMess1
2 started pushing message from: client1
3 Server response on client side: ECHO RESPONSE: client1 :: simpleMess1
4 Message: client2 :: simpleMessage from c2
5 started pushing message from: client2
6 
7 -- No response from client2. AND next try from client2 shows no log at all (!)
8 
9 Message: client1 :: simple mess2 from c1
10 started pushing message from: client1
11 Server response on client side: ECHO RESPONSE: client1 :: simpleMess1
以及服务器端控制台中的日志:

1 Server started...
2   S: Key is acceptable
3   S: Key is acceptable
4
5 -- after creating the clients before any action   
6 S: Key is readable.
控制台输出清楚地显示服务器从两个客户端接收可接受的密钥,但它也表明只有一个
SocketChannel
具有可读类型的
SelectionKey
,但我不知道为什么。此外,我认为创建客户机的顺序并不重要,因为正如我测试的那样:与服务器正常对话的客户机总是首先开始通信的客户机。 下面我发布了我的
服务器
客户端
类代码,希望你们能帮我整理一下

首先,
服务器
类:

import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

public class Server {

private ServerSocketChannel serverSocketChannel = null;
private Selector selector = null;
private StringBuffer messageResponse = new StringBuffer();

private static Charset charset = Charset.forName("ISO-8859-2");
private static final int BSIZE = 1024;
private ByteBuffer byteBuffer = ByteBuffer.allocate(BSIZE);
private StringBuffer incomingClientMessage = new StringBuffer();
Set<SocketChannel> clientsSet = new HashSet<>();



public Server(String host, int port) {
    try {
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.socket().bind(new InetSocketAddress(host, port));
        selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }
    catch (Exception exc) {
        exc.printStackTrace();
        System.exit(1);
    }
    System.out.println("Server started...");
    serviceConnections();
}

private void serviceConnections() {
    boolean serverIsRunning = true;

    while (serverIsRunning) {
        try {
            selector.select();
            Set keys = selector.selectedKeys();

            Iterator iter = keys.iterator();
            while (iter.hasNext()) {

                SelectionKey key = (SelectionKey) iter.next();
                iter.remove();

                if (key.isAcceptable()) {
                    System.out.println("\tS: Key is acceptable");

                    SocketChannel incomingSocketChannel = serverSocketChannel.accept();
                    incomingSocketChannel.configureBlocking(false);
                    incomingSocketChannel.register(selector, SelectionKey.OP_READ);
                    clientsSet.add(incomingSocketChannel);

                    continue;
                }

                if (key.isReadable()) {
                    System.out.println("\tS: Key is readable.");

                    SocketChannel incomingSocketChannel = (SocketChannel) key.channel();
                    serviceRequest(incomingSocketChannel);
                    continue;
                }
            }
        }
        catch (Exception exc) {
            exc.printStackTrace();
            continue;
        }
    }
}

private void serviceRequest(SocketChannel sc) {
    if (!sc.isOpen()) return;

    incomingClientMessage.setLength(0);
    byteBuffer.clear();
    try {
        while (true) {
            int n = sc.read(byteBuffer);
            if (n > 0) {
                byteBuffer.flip();
                CharBuffer cbuf = charset.decode(byteBuffer);
                while (cbuf.hasRemaining()) {
                    char c = cbuf.get();
                    if (c == '\r' || c == '\n') break;
                    incomingClientMessage.append(c);
                }
            }
            writeResp(sc, "ECHO RESPONSE: " + incomingClientMessage.toString());
        }


    }
    catch (Exception exc) {         
        exc.printStackTrace();
        try {
            sc.close();
            sc.socket().close();
        }
        catch (Exception e) {
        }
    }
}

private void writeResp(SocketChannel sc, String addMsg)
        throws IOException {
    messageResponse.setLength(0);
    messageResponse.append(addMsg);
    messageResponse.append('\n');
    ByteBuffer buf = charset.encode(CharBuffer.wrap(messageResponse));
    sc.write(buf);
}
//second version - with an attempt to acomlish broadcasting
private void writeResp(SocketChannel sc, String addMsg)
        throws IOException {
    messageResponse.setLength(0);
    messageResponse.append(addMsg);
    messageResponse.append('\n');
    ByteBuffer buf = charset.encode(CharBuffer.wrap(messageResponse));

    System.out.println("clientsSet: " + clientsSet.size());
    for (SocketChannel socketChannel : clientsSet) {
        System.out.println("writing to: " + socketChannel.getRemoteAddress());
        socketChannel.write(buf);
        buf.rewind();
    }
}

public static void main(String[] args) {
    try {
        final String HOST = "localhost";
        final int PORT = 5000;
        new Server(HOST, PORT);
    }
    catch (Exception exc) {
        exc.printStackTrace();
        System.exit(1);
    }
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {
private ClientView clientView;
private String hostName;
private int port;
private String clientName;
private Socket socket = null;
private PrintWriter printWriterOUT = null;
private BufferedReader bufferedReaderIN = null;

public Client(String hostName, int port, String clientName) {
    this.hostName = hostName;
    this.port = port;
    this.clientName = clientName;

    initView();
}

public void handleConnection() {

    try {
        socket = new Socket(hostName, port);
        printWriterOUT = new PrintWriter(socket.getOutputStream(), true);
        bufferedReaderIN = new BufferedReader(new InputStreamReader(socket.getInputStream()));

        waitForIncomingMessageFromClientView();


        bufferedReaderIN.close();
        printWriterOUT.close();
        socket.close();

    }
    catch (UnknownHostException e) {
        System.err.println("Unknown host: " + hostName);
        System.exit(2);
    }
    catch (IOException e) {
        System.err.println("I/O err dla");
        System.exit(3);
    }
    catch (Exception exc) {
        exc.printStackTrace();
        System.exit(4);
    }
}


public void initView() {
    clientView = new ClientView(clientName);
}

public void waitForIncomingMessageFromClientView() {
    System.out.println("Awaiting message from: " + clientName + "...");
    while (true) {
        if (clientView.isSent) {
            System.out.println("Message: " + clientView.getOutgoingMessage());
            pushClientViewMessageToServer();
            clientView.setIsSent(false);
        }
    }
}

public void pushClientViewMessageToServer() {
    String clientViewMessage = clientView.getOutgoingMessage();
    System.out.println("started pushing message from: " + clientView.getClientName());

    try {
        printWriterOUT.println(clientViewMessage);
        String resp = bufferedReaderIN.readLine();
        System.out.println("Server response on client side: " + resp);
    }
    catch (IOException e) {
        e.printStackTrace();
    }
}


public static void main(String[] args) {

    Thread thread1 = new Thread(new Runnable() {

        @Override
        public void run() {
            Client c1 = new Client("localhost", 5000, "client1");
            c1.handleConnection();
        }
    });
    thread1.start();

    Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            Client c2 = new Client("localhost", 5000, "client2");
            c2.handleConnection();
        }
    });
    thread2.start();

}

}
我会感谢你们的帮助

编辑: 第二个版本的
writeResp
方法试图向所有客户端广播echo,会生成这样的日志:

Server started...
clientsSet: 2
writing to: /127.0.0.1:63666
writing to: /127.0.0.1:63665
clientsSet: 2
writing to: /127.0.0.1:63666
writing to: /127.0.0.1:63665
似乎有两个客户端,我想知道为什么他们没有从服务器得到正确的回复

while (true) {
    int n = sc.read(byteBuffer);
    if (n > 0) {
        byteBuffer.flip();
        CharBuffer cbuf = charset.decode(byteBuffer);
        while (cbuf.hasRemaining()) {
            char c = cbuf.get();
            if (c == '\r' || c == '\n') break;
            incomingClientMessage.append(c);
        }
    }

这里有一个大问题。如果
read()
返回-1,你应该关闭
SocketChannel
,如果它返回-1或零,你应该跳出循环。

非常感谢你,所以在
If
语句之后,我应该放置一个
else
子句,像这样关闭频道:
sc.close()
,是吗?但为什么我也要单独使用这个条件呢?{…?你们能告诉我它背后的逻辑的内部结构吗?叹气。若
n<0
对等方已经关闭了连接,你们也应该这样做。若
n<0
n==0
,表示为
n好的,若我理解正确:
while(true){int n=sc.read(byteBuffer);if(n>0){byteBuffer.flip();CharBuffer cbuf=charset.decode(byteBuffer);while(cbuf.haslaining()){char c=cbuf.get();if(c='\r'| c='\n')break;incomingClientMessage.append(c);}else{/*如果n为(1)No,它将执行。请再次阅读我所写的内容。(2)您可以自己看到,在注释中发布的代码完全难以辨认。(2)很抱歉,我以前从未做过这件事,因为我认为不应该再做一次。(1)我已经读了好几遍了,我真的很抱歉,但是我没有理解正确:在
while(true)
循环中,我应该添加两个条件来检查
n
的大小,根据它的值,我应该执行
sc.close()
还是
break;
while
操作?