简体   繁体   English

Java使用客户端套接字的多线程

[英]Java Multiple threads using a client socket

I'm creating a server based chat program that has multiple worker threads each handling a client on a socket. 我正在创建一个基于服务器的聊天程序,该程序具有多个工作线程,每个工作线程都在套接字上处理客户端。 It has a single server socket that passes off the client to the worker thread. 它具有一个服务器套接字,该套接字从客户端传递到工作线程。

    public static void main(String[] args) {
    ExecutorService executor = Executors.newFixedThreadPool(5);
    ServerSocket serverSocket = null;
    try {
        serverSocket = new ServerSocket(4001);
        System.out.println("Listening server");
        while (true) {
            Socket socket = serverSocket.accept();
            System.out.println("Connected");
            Random rand= new Random();
            int port=4001+rand.nextInt(5);
            Worker worker = new Worker(port);
            executor.execute(worker);
            System.out.println("Thread started");
            new PrintWriter(socket.getOutputStream(), true).println(port);
            socket.close();
            System.out.println("Closed");
            //  break;
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

}

public class Worker implements Runnable {

private int port;
public Worker(int i) {
    port=i;
}
@Override
public void run() {
    worker();
}
private static Socket socket;
private static PrintWriter out;
private static BufferedReader in;

private void worker() {
    try {
        ServerSocket serverSocket = new ServerSocket(port);
        socket = serverSocket.accept();
        out = new PrintWriter(socket.getOutputStream(), true);
        in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String line;
        while ((line = in.readLine()) != null) {
            System.out.println("Received: " + line);
            switch (line) {
                case ("Button Press"):
                    System.out.println("Handled button");
                    out.println("Button acknowledged");
                    break;
                case ("Give me some data"):
                    System.out.println("Handled data");
                    out.println("Have some data");
                    break;
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            out.close();
            in.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

This works fine, however the issue is when I have an automated request by the client to check for messages and the user provides some input at the same type. 这可以正常工作,但是问题是当我有一个客户端自动请求检查消息并且用户以相同类型提供一些输入时。 This causes conflict as the actual methods take a couple of seconds to run and if more input is received then the request won't be handled because its in the middle of the method. 这会导致冲突,因为实际方法需要花费几秒钟才能运行,并且如果接收到更多输入,则该请求将被处理,因为该请求位于方法的中间。 For example: 例如:

private static BufferedReader in;
private static PrintWriter out;
public static void main(String[] args) {
    Main main=new Main();
    main.createWindow();
    try {
        Socket init = new Socket(InetAddress.getLocalHost(), 4001);
        int port
             =Integer.parseInt
                (new BufferedReader
                    (new InputStreamReader(init.getInputStream())).readLine());
        init.close();
        Socket socket=new Socket(InetAddress.getLocalHost(), port);
        out = new PrintWriter(socket.getOutputStream(), true);
        in = new BufferedReader(new InputStreamReader(socket.getInputStream()));


    while(true){
        try {
            Thread.sleep(5000);
            out.println("Give me some data");
            if(in.readLine().equals("Have some data")){
                System.out.println("Data recieved");
            }else{
                System.out.println("Data not recieved");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
    } catch (IOException e) {
        e.printStackTrace();
    }
}


private void createWindow(){
    JFrame frame =new JFrame("This is a button");
    Container pane=getContentPane();
    JButton button=new JButton("This is a button");
    button.addActionListener(this);
    pane.add(button);
    frame.setTitle("Messaging");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setContentPane(pane);
    frame.setVisible(true);
    frame.setSize(400, 350);
}

@Override
public void actionPerformed(ActionEvent e) {
    System.out.println("Button press");
    try {
        out.println("Button Press");
        if(in.readLine().equals("Button acknowledged")){
            System.out.println("Button complete");
        }else{
            System.out.println("Button fail");
        }
    } catch (IOException e1) {
        e1.printStackTrace();
    }
}

If the button is pressed whilst the server is fetching data it conflicts and the wrong response is sent. 如果在服务器获取数据时按下按钮,则会发生冲突,并发送错误的响应。 So how would I deal with this? 那么我该如何处理呢? If I create a separate socket to just handle automated checks that's double the amount of threads the server has to cope with. 如果我创建一个单独的套接字来处理自动检查,那是服务器要处理的线程数量的两倍。 Is there a better solution? 有更好的解决方案吗? And please can you try to explain as this is a new area for me. 并且请您尝试解释一下,因为这对我来说是一个新领域。

Your problem is that you have two threads interacting with the socket, your main thread and the Swing event handling thread. 您的问题是您有两个线程与套接字交互,即主线程和Swing事件处理线程。 This means that the two threads can queue two different things, and the responses may be picked up by the wrong thread. 这意味着两个线程可以排队两个不同的事物,并且错误的线程可能会拾取响应。 The simplest way to get where you want to be is to put all the socket interaction in one thread. 获得所需位置的最简单方法是将所有套接字交互都放在一个线程中。

There are two ways to do this. 有两种方法可以做到这一点。 One would be for the main thread to queue the periodic automated checks to the Swing event handling thread. 一种是让主线程将定期的自动检查排队到Swing事件处理线程中。 However, that's a bit complicated and also possibly buggy as the actual Swing threading model is different from what's documented. 但是,这有点复杂,并且还可能存在错误,因为实际的Swing线程模型与所记录的模型不同。

The other way to do it would be for the Swing event handling thread to queue button presses to the main thread. 另一种方法是让Swing事件处理线程将按钮的按下排队到主线程中。 In this case, you would queue the button press to the main thread using proper synchronization. 在这种情况下,您将使用适当的同步将按下按钮排队到主线程上。 The main loop of the main thread would check for button presses and send them through the socket and wait for the proper response. 主线程的主循环将检查是否有按钮按下,并通过套接字发送它们,然后等待正确的响应。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM