当前位置: 首页 > 知识库问答 >
问题:

JavaNIO-SocketChannel.write()在服务器上多次,但客户端只收到一次结果

艾英范
2023-03-14

我正在做一个练习,需要使用Java非阻塞IO制作一个服务器-客户端聊天程序。目前,程序的工作方式很简单:当客户端向服务器发送消息时,服务器(已经跟踪所有客户端)会将消息回显给所有客户端。

这是我的部分服务器端代码:

public static ByteBuffer str_to_bb(String msg) {
    try {
        return encoder.encode(CharBuffer.wrap(msg));
    } catch(Exception e) {
        e.printStackTrace();
    }
    return null;
}

private static void broadcastMessage(String nickname, String message) {
    System.out.println(">clientSocketChannels size " + clientSocketChannels.size());
    Iterator clientSocketChannelsIterator = clientSocketChannels.iterator();
    while (clientSocketChannelsIterator.hasNext()) {
        SocketChannel sc = (SocketChannel) clientSocketChannelsIterator.next();
        try {
            ByteBuffer bb = str_to_bb(message);
            System.out.println("bufferRemaining: " + bb.remaining()); // returns 2048
            int writeResult = sc.write(bb);
            System.out.println("writeResult: " + writeResult); // returns 2048
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

以下是我的客户端代码:

import javax.swing.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

/**
 * Created by ThaiSon on 7/6/2015.
 */
public class ChatRoomClientGUI {
    private JTextArea textAreaMessages;
    private JTextField textFieldMessage;
    private JButton buttonSendMsg;
    private JPanel jPanel1;
    private JLabel txtFieldInfo;

    private static InetAddress inetAddress;
    private static final int PORT = 1234;
    private static Socket socket = null;
    private static Scanner input = null;
    private static PrintWriter output = null;

    private static ChatRoomClientGUI singleton;

    public ChatRoomClientGUI() {
        singleton = this;
        buttonSendMsg.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                if (e.getButton() == MouseEvent.BUTTON1) {
                    String message = textFieldMessage.getText();
                    output.println(message);
                    textFieldMessage.setText("");
                }
            }
        });
    }

    public static void main(String[] args) {
        JFrame promptFrame = new JFrame();
        Object nickname = JOptionPane.showInputDialog(promptFrame, "Enter your nickname:");
        promptFrame.dispose();

        JFrame frame = new JFrame("ChatRoomClientGUI");
        frame.setContentPane(new ChatRoomClientGUI().jPanel1);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);

        System.out.println("> Client with nickname " + nickname);

        try {
            inetAddress = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        accessServer(nickname.toString());
    }

    private static void accessServer(String nickname) {
        try {
            socket = new Socket(inetAddress, PORT);
            input = new Scanner(socket.getInputStream());
            output = new PrintWriter(socket.getOutputStream(), true);
            output.println(nickname); // Register nickname with the server

            //TODO update the txtFieldInfo content

            // Create a new thread to listen to InputStream event
            InputStreamEvent inputStreamEvent = new InputStreamEvent(socket);
            inputStreamEvent.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void handleInputStream(){
        String response = input.nextLine();
        System.out.println("TODO " + response);
        singleton.textAreaMessages.append(response + "\n");
    }

    static class InputStreamEvent extends Thread{
        Socket socket;
        public InputStreamEvent(Socket socket){
            this.socket = socket;
        }
        public void run(){
            try {
                InputStream inputStream = socket.getInputStream();
                byte[] buffer = new byte[2048];
                int read;
                while (true){
                    if(inputStream.available() > 0){
                        handleInputStream();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

我现在面临的问题是,当我使用客户端(与旧的多线程服务器合作得很好)向服务器发送消息时,客户端只返回它发送的第一条消息。服务器的所有下一个响应都是空的(服务器确实发回了,但只有一条空消息)。

所以我的调试尝试包括:

  • 检查来自客户端的消息是否已到达服务器。是的

希望你们能帮我。

共有2个答案

施俊哲
2023-03-14

我提到了EJP在这个链接上解释的代码JavaNIO服务器/客户端聊天应用程序-仅通过关闭套接字发送数据

它解决了我的问题。使用此代码

import java.nio.channels.SocketChannel;
import java.nio.channels.Selector;
import java.nio.ByteBuffer;
import java.io.IOException;
import java.util.Scanner;
import java.nio.channels.SelectionKey;
import java.net.InetSocketAddress;
public class Client {
    public static void main(String args[]) {
        try {
            ByteBuffer buf = ByteBuffer.allocate(200);
            Scanner scanner = new Scanner(System.in);
            Selector selector = Selector.open();
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_CONNECT|SelectionKey.OP_READ|SelectionKey.OP_WRITE);
            boolean isConnected = socketChannel.connect(new InetSocketAddress("localhost", 5000));
            if(isConnected) {
                System.out.println("Connected, de-registering OP_CONNECT");
            }
            new Thread(new Runnable(){
                private SocketChannel socketChannel;
                private Selector selector;
                public Runnable init(SocketChannel socketChannel, Selector selector) {
                    this.socketChannel = socketChannel;
                    this.selector = selector;
                    return this;
                }
                public void run() {
                    try {
                        ByteBuffer buf = ByteBuffer.allocate(200);
                        while(!Thread.interrupted()) {
                            int keys = selector.select();
                            if(keys > 0) { 
                                for(SelectionKey key : selector.selectedKeys()) {
                                    if(key.isConnectable()) {
                                        boolean finishConnectResult = socketChannel.finishConnect();
                                        socketChannel.register(this.selector, SelectionKey.OP_WRITE|SelectionKey.OP_READ);
                                        System.out.println("Finished Connect : " + finishConnectResult);
                                    }
                                    if(key.isReadable()) {
                                        int bytesRead = 0;
                                        while((bytesRead = socketChannel.read(buf)) > 0) {
                                            buf.flip();
                                            while(buf.hasRemaining()) {
                                                System.out.print((char)buf.get());
                                            }
                                            buf.clear();
                                        }
                                        if(bytesRead == -1) {
                                            key.channel().close();
                                        }
                                    }
                                }
                            }
                            Thread.sleep(10);
                        }
                    } catch(IOException e) {
                        e.printStackTrace();
                    } catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }.init(socketChannel, selector)).start();

            while(true) {
                while(scanner.hasNextLine()) {
                    buf.clear();
                    buf.put(scanner.nextLine().getBytes());
                    buf.flip();
                    socketChannel.write(buf);
                    buf.flip();
                }
            }
        } catch(IOException e) {
            e.printStackTrace();
        }
    }
}

<代码>

我设置这个标志键时出错了。兴趣(SelectionKey.OP_READ);) 而不是下面。使用这个socketChannel。寄存器(this.selector、SelectionKey.OP|u WRITE、SelectionKey.OP|u READ)

锺离赤岩
2023-03-14

>

  • 这是:

    if(inputStream.available() > 0){
    

    摆脱这个测试。有了它,你的客户正在抽CPU。如果没有它,它将按照上帝的意愿在readLine()中阻塞。

    您确定您的服务器仍然在发送带有行终止符的行吗?如果不是,readLine()将永远阻止查找一个,直到流结束或出现异常。

  •  类似资料:
    • 我正在开发一个具有多个客户端的标准java RMI服务器。这些客户机有一个菜单,在那里他们可以调用服务器为他们做各种事情。 一种方法涉及一个队列,他们可以在其中将作业发送到队列并等待它得到处理。RMI服务器自动为所有客户端处理线程,但当涉及到此方法和队列时,我如何阻止此请求,例如: 首先调用客户端1,然后再调用客户端2(此处客户端1应首先从服务器接收消息,客户端2应等待服务器处理客户端1请求所需的

    • 我已经使用java nio创建了一个客户端-服务器应用程序,它工作正常,但我的问题是,当服务器有许多连接到服务器的客户端时,服务器会响应错误的客户端,而不是请求客户端。例如,如果客户端A请求第一个人的信息,服务器将第一个人的信息返回给客户端B而不是客户端A。我已经尝试同步对象,但仍然无法正常工作,可能是什么问题。这是我的服务器示例代码

    • 我想在一些计算机之间建立点对点连接,这样用户就可以在没有外部服务器的情况下聊天和交换文件。我最初的想法如下: 我在服务器上制作了一个中央服务器插座,所有应用程序都可以连接到该插座。此ServerSocket跟踪已连接的套接字(客户端),并将新连接的客户端的IP和端口提供给所有其他客户端。每个客户端都会创建一个新的ServerSocket,所有客户端都可以连接到它。 换句话说:每个客户端都有一个Se

    • 我已经看到了很多与我试图做的事情有关的问题,但我还没有找到任何解决方案。 我尝试编写两个套接字程序(一个客户端和一个服务器),以便服务器能够向客户端发送任何类型的文件(按字节)。我不确定如何构建/协调以下内容: > (在服务器中)读取语句从文件中获取数据(我使用fread()) (在服务器中)将数据发送到客户端的write语句(我使用的是write()) (在客户端)读取语句从服务器接收数据(我使

    • 但还是没用。有什么帮助吗