Java并发网络问题

时间:2011-08-26 03:28:02

标签: java networking concurrency

我是一个尝试学习网络编程和并发的Java新手,我想我会尝试编写一个简单的聊天服务器,客户端的输入会响应所有客户端。那没有发生。我添加了几个打印语句,以便程序宣布它正在等待连接并且每次收到连接。我在本地使用Telnet连接到我机器上的端口。

该程序宣布第一个和第二个并发连接成功但后来没有宣布后续连接成功直到我关闭所有连接。因此,例如,我将从五个独立的终端连接,程序将宣布“连接1”和“连接2”,但在关闭所有终端之前不会发布“连接3”,4和5。

我正在寻找帮助,找出我的错误所在,以及如何调试这种情况的一般建议。

简而言之,我的计划已经

  1. 一个Main类,启动其他三个线程
  2. ClientListener类,它使用SocketReader侦听连接并将套接字输入流和输出流存储在两个集合中。
  3. MessageReader,它迭代输入流。如果找到消息,则将其置于SynchronousQueue中并等待
  4. MessageWriter将其删除。 MessageWriter将消息发送到所有输出流。
  5. 代码如下。谢谢你的帮助!

    public class Main {
    
        public static void main(String[] args) {
            ClientListener clientListener = new ClientListener();
            Thread clientListenerThread = new Thread(clientListener);
            clientListenerThread.setPriority(Thread.MAX_PRIORITY);
            clientListenerThread.start();
    
            MessageReader messageReader = new MessageReader(clientListener);
            Thread messageReaderThread = new Thread(messageReader);
            messageReaderThread.setPriority(Thread.MIN_PRIORITY);
            messageReaderThread.start();
    
            MessageWriter messageWriter = new MessageWriter(messageReader, clientListener);
            Thread messageWriterThread = new Thread(messageWriter);
            messageWriterThread.setPriority(Thread.NORM_PRIORITY);
            messageWriterThread.start();
        }
    }
    
    public class ClientListener implements Runnable {
        private static final int DEFAULT_PORT = 5000;
    
        private Set<Scanner> clientIn = Collections.synchronizedSet(
                new LinkedHashSet<Scanner>());
        private Set<PrintWriter> clientOut = Collections.synchronizedSet(
                new LinkedHashSet<PrintWriter>());
    
        public Set<Scanner> getClientIn() {
            return clientIn;
        }
    
        public Set<PrintWriter> getClientOut() {
            return clientOut;
        }
    
        @Override
        public void run() {
            try {
                ServerSocket server = new ServerSocket(DEFAULT_PORT);
                System.out.println("Listening for connections...");
                int connectionNum = 0;
    
                while(true) {
                    Socket socket = server.accept();
                    connectionNum++;
                    System.out.format("Connection %s%n", connectionNum);
    
                    Scanner in = new Scanner(socket.getInputStream());
                    PrintWriter out = new PrintWriter(socket.getOutputStream());
                    clientIn.add(in);
                    clientOut.add(out);
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    public class MessageReader implements Runnable {
        private ClientListener clientListener;
        private BlockingQueue<String> messages = new SynchronousQueue<String>();
    
        public MessageReader(ClientListener clientListener) {
            this.clientListener = clientListener;
        }
    
        @Override
        public void run() {
            while(true) {
                Set<Scanner> clients = clientListener.getClientIn();
                synchronized (clients) {
                    for(Scanner client: clients) {
                        if(client.hasNext()) {
                            try {
                                messages.put(client.next());
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }
    
        public String getMessage() throws InterruptedException {
            return messages.take();
        }
    }
    
    
    public class MessageWriter implements Runnable {
        private ClientListener clientListener;
        private MessageReader messageReader;
    
        public MessageWriter(
                MessageReader messageReader, 
                ClientListener clientListener) {
            this.messageReader = messageReader;
            this.clientListener = clientListener;
        }
    
        @Override
        public void run() {
            try {
    
                while(true) {
                    String message = messageReader.getMessage();
    
                    Set<PrintWriter> clients = clientListener.getClientOut();
                    synchronized (clients) {
                        for(PrintWriter client: clients) {
                            client.println(message);
                        }
                    }
                }
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

1 个答案:

答案 0 :(得分:2)

我不是线程专家,但在MessageReader类中有这一行

if(client.hasNext())

Scanner.hasNext()的Javadoc说“在等待输入扫描时,此方法可能会阻塞。扫描程序不会超过任何输入。”

如果扫描仪仍在等待,则同步方法永远不会继续并阻止所有其他输入。正如我在之前的评论中所说的那样,clientIn.add(in);ClientListener中的行可能会被阻止,因为它是一个同步的Set,但由于打印语句是在它之前写的,它可能会给人一种连接的印象2成功建立。