在单独的线程上获取套接字数据,然后将其传递给主线程

时间:2018-10-29 22:25:59

标签: java multithreading

编辑了我的问题以进行澄清和编码:

我的目标是将String数据从后台线程传递到我的主应用程序线程。任何帮助表示赞赏。

这是创建主后台线程的代码。这位于我的Server.java类中

public class Server {

boolean isConnected = false;
Controller controller = new Controller();

public void startHost() {
    Thread host = new Thread(() -> {
        Controller controller = new Controller();
        ServerSocket server = null;

        try {
            server = new ServerSocket(GeneralConstants.applicationPort);

        } catch (BindException e2) {
            System.out.println("Port Already in Use!");

        } catch (IOException e) {
            //do nothing

        }

        while (true) {
            if (server == null) { break; }

            try {
                Socket client = server.accept();

                System.out.println("Client Connected: " + isConnected);

                if (!isConnected) {
                    controller.createClientHandler(client);
                    isConnected = true;
                    System.out.println("Client Connected: " + isConnected);
                }

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();

            }
        }
    });

    host.setDaemon(true);
    host.start();

}

这是连接客户端后在我的Controller.java类中调用的代码。

    public synchronized void createClientHandler(Socket client) {
    boolean alreadyConnected = false;

    if (alreadyConnected) {
        //do NOT assign multiple threads for each client

    } else {
        ClientHandler handleClients = new ClientHandler("client", client);

    }

}

然后程序为我的客户端创建了两个后台线程,一个用于管理接收消息和发送消息。

public ClientHandler(String name, Socket s) {
    clientSocket = s;
    clientName = name;

    receiveThread = new Thread(this::receive);
    sendThread = new Thread(this::send);

    connected = clientSocket.isConnected();

    receiveThread.start();
    sendThread.start();

}

然后,线程成功创建输入流,并将对象传递给我的控制器。然后处理并抓取将其分配给变量的字符串

public synchronized void handleReceivedPacket(String name, BufferedReader in) {
    try {
        data = in.readLine();
        System.out.println("Successfully assigned data to: " + data);

    } catch (IOException e) {
        System.out.println("Unable to read result data");

    }

}

如何在不获取null的情况下从主线程访问我的String数据?

我也可以打电话(或类似的电话)

controller.returnData();

从我的主应用程序。从中它要么返回null(还没有数据),要么实际上返回我的数据。现在,它总是

编辑,这实际上是在调用controller.returnData(){

由于不想达到StackOverflow的代码限制,我不想粘贴大量的代码,所以这是我的应用程序结构。

我的JavaFX创建场景,并创建一个根网格窗格,然后调用一个方法,该方法根据指定的输入创建子网格窗格。又名,用户可以按“主菜单”,该方法调用我的方法setScene(),该方法将删除当前的“子根”网格窗格并创建一个“新”场景。现在,我有一个GameBoard.java类,它在按下按钮时调用controller.returnData()

PassOption.setOnAction(event -> {
        System.out.println(controller.returnData());


    });

除了测试之外,没有其他功能目的。如果我可以接收数据,则可以使用数据对此进行扩展。

1 个答案:

答案 0 :(得分:0)

开始考虑设计。在网络应用程序中,通常必须管理以下职责:

  1. 已连接的客户端及其状态(连接状态,心跳等)
  2. 从客户端收到的消息
  3. 要传送给客户端的消息

将这些职责分开以保持代码的清洁,可读和可维护是有意义的。

分离可以同时表示线程和类。

例如,您可以按以下方式实现它:

ClientAcceptor负责打开套接字并接受客户端。客户端连接后,它将进一步的工作委托给控制器,然后等待其他客户端:

public class ClientAcceptor implements Runnable {

    @Override
    public void run() {
        while (true) {
            ServerSocket server;
            try {
                server = new ServerSocket(1992);
                Socket client = server.accept();

                if (client.isConnected()) {
                    controller.createClientHandler(client);
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

然后,控制器可以创建一个处理程序(如果控制器决定这样做,例如它也可以拒绝该客户端)。 ClientHandler类的外观如下:

public class ClientHandler {
    private Thread receiveThread;
    private Thread sendThread;
    private boolean connected;
    private Socket clientSocket;
    private String clientName;
    private LinkedBlockingDeque<byte[]> sendQueue;

    public ClientHandler(String name, Socket s) {
        clientSocket = s;
        clientName = name;

        receiveThread = new Thread(() -> receive());
        sendThread = new Thread(() -> send());

        connected = clientSocket.isConnected();

        receiveThread.start();
        sendThread.start();
    }

    private void receive() {
        BufferedInputStream in = null;
        try {
            in = new BufferedInputStream(clientSocket.getInputStream());
        } catch (IOException e) {
            connected = false;
        }
        while (connected) {
            try {
                byte[] bytes = in.readAllBytes();
                if (bytes != null && bytes.length > 0) {
                    controller.handleReceivedPacket(clientName, bytes);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void send() {
        BufferedOutputStream out = null;
        try {
            out = new BufferedOutputStream(clientSocket.getOutputStream());
        } catch (IOException e) {
            connected = false;
        }
        while (connected) {
            byte[] toSend = sendQueue.getFirst();
            if (toSend != null && toSend.length > 0) {
                try {
                    out.write(toSend);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public void send(byte[] packet) {
        sendQueue.add(packet);
    }

    public void close() {
        connected = false;
    }
}

ClientHandler负责接收和发送数据。如果数据包到达,它会通知控制器,后者将解析该数据包。 ClientHandler还提供了一个公共API,用于发送数据(存储在队列中并由线程处理)并关闭连接。

以上代码示例未经测试,也不完整。以它为起点。