NIO聊天应用程序无法正常运行多个客户端

时间:2015-11-06 08:33:47

标签: java nio socketchannel

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

现在,我正处于工作的中间阶段,我已经完成了相当完整的客户类(以及他们的GUI部分)和服务器,但我和我偶然发现了一个问题,我无法在任何地方找到任何解决方案。也就是说,如果我在我的控制台(一个用于客户端,一个用于服务器)中运行服务器的实例和一个客户端实例,我会看到一个很好的预期会话。但是,在添加其他客户端后,这个新创建的客户端无法从服务器获得响应 - 第一个仍然具有有效连接。

我没有考虑向所有客户端广播消息,现在我想解决我的每个客户端和服务器之间缺乏正确通信的问题,因为我认为广播如果沟通良好,不应该这么大。

我还要补充一点,我已经尝试了许多其他实例化客户端的方法:在一个线程中,首先实例化客户端然后在其上应用方法,我尝试使用{来自invokeLater的{​​1}},因为这是启动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可读类型,但我不知道为什么。此外,我认为创建客户端的顺序并不重要,因为我测试过:与服务器正确对话的客户端始终是首先开始通信的客户端。 我在下面发布了我的SelectionKeyServer课程代码,希望你们帮助我解决这个问题。

首先,Client类:

Server

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); } } } 类:

Client

我会向你们提供帮助。

编辑: 试图向所有客户端广播echo的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

似乎有两个客户,我想知道为什么他们没有得到服务器的正确回复。

1 个答案:

答案 0 :(得分:1)

read()

这里存在一个主要问题。如果SocketChannel返回-1,则应关闭{{1}},如果它返回-1 零,则应该退出循环。