【问题标题】:Java - Threads, Swing, and ServerSocketJava - 线程、Swing 和 ServerSocket
【发布时间】:2012-05-21 11:50:29
【问题描述】:

我知道我知道,到处都有一百万个问题和答案。大量关于它的详细文章,几种类型的示例。在过去的几个小时里,我一直在阅读它,但这并没有起到作用。我问这个的原因是因为我仍然不明白我需要做什么显然因为我的代码仍然无法正常工作。我了解 Swing 如何与 EDT 一起工作,如果我要使用 ServerSocket 的 accept() 方法,我将需要为 Swing 启动一个新线程(我认为?)。当我运行我的代码时,窗口只是打开并冻结。 我的问题是有人可以看看我的代码,告诉我我做错了什么,然后像我只有半个大脑一样向我解释它吗?(因为那是我觉得。:P)

以下是我已经阅读过的一些地方:

Main.class

package com.sever.core;

import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;

import javax.swing.SwingUtilities;

public class Main { 

private SocketManager network;
public static void main(String[] args){

    SwingUtilities.invokeLater(new Runnable(){
        @Override
        public void run() {
            Window window = new Window();
            window.setVisible(true);
        }       
    });

    SwingUtilities.invokeLater(new Runnable(){
        @Override
        public void run() {
            Main main = new Main();
            main.run();
        }       
    });     


}

public void run(){
    network = new SocketManager(25595);

    while(true){
        try {
            network.setSocket(network.getServerSocket().accept());
            AddUser(network.getSocket());
            Thread x = new Thread(network);
            x.start();
        } catch (Exception e) {
            System.out.println("Failed to connect.");
        }
    }

}

public void AddUser(Socket s){
    try {
        Scanner input = new Scanner(s.getInputStream());
        network.addUser(input.nextLine());
    } catch (Exception e) {

    }
}
}

Window.class

package com.sever.core;

import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.*;

public class Window extends JFrame{

private int screenWidth = 800;
private int screenHeight = 600;
private Thread thread;

private JPanel window = new JPanel();
private JTextArea consle = new JTextArea("Server started....");
private JTextField input = new JTextField();
private JScrollPane consleinput = new JScrollPane(consle);

public Window(){
    this.setName("NAMEHERE - Server Software");
    setResizable(true);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(screenWidth,screenHeight);  
    window.setBackground(Color.DARK_GRAY);
    window.setLayout(new BoxLayout(window, BoxLayout.Y_AXIS));

    consleSetup();
    addComponets();
}

private void addComponets(){
    add(window);
    window.add(consleinput);
    window.add(input);
}

private void consleSetup(){
    consle.setEditable(false);
    consle.setLineWrap(true);
    consle.setBorder(BorderFactory.createEmptyBorder(3,3,3,3)); 
    consleinput.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
    input.setMaximumSize(new             Dimension(Integer.MAX_VALUE,input.getPreferredSize().height));
}

private void addListeners(){
    input.addActionListener(new ActLis());
    input.setActionCommand("input");
}

}

SocketManager.class

package com.sever.core;

import java.io.*;
import java.net.*;
import java.util.ArrayList;

public class SocketManager implements Runnable{

private Socket sock;
private ServerSocket sersoc;
private PrintWriter output;
private BufferedReader input;
private Thread thread;

public ArrayList<Socket> currentConnections = new ArrayList<Socket>();
public ArrayList<String> currentUsers = new ArrayList<String>();


public SocketManager(String ip, int port){
    try{
        sock = new Socket(ip,port);
        PrintWriter output = new PrintWriter(sock.getOutputStream());
        BufferedReader input = new BufferedReader(
                new InputStreamReader(sock.getInputStream()));
        System.out.println("Server: socket started.");
    }catch(Exception e){
        System.out.println("Server: Socket failed to connect.\n");
    }
}

public SocketManager(int port){
    try {
        sersoc = new ServerSocket(port);

    } catch (IOException e) {
        System.out.println("Server: Socket failed to connect.\n");
    }
}

public SocketManager(Socket socket){
    this.sock = socket;

    try{
        output = new PrintWriter(sock.getOutputStream());
        input = new BufferedReader(
                new InputStreamReader(sock.getInputStream()));
    }catch(Exception e){

    }
}

public synchronized void checkConnetion(){
    if(!sock.isConnected()){
        for(int x = 0; x <= currentConnections.size(); x++){
            if(currentConnections.get(x) == sock){
                currentConnections.remove(x);
                System.out.println("Server: Disconnecting from: " + currentConnections.get(x) + "\n");
            }
        }
    }
}

public synchronized Socket getSocket(){
    return sock;
}

public synchronized ServerSocket getServerSocket(){
    return sersoc;
}

public synchronized void setSocket(Socket s){
    System.out.println("Setting socket to: " + s.getInetAddress());
    sock = s;
}

public synchronized void addSocket(Socket s){
    currentConnections.add(s);
}

public synchronized void addUser(String u){
    currentUsers.add(u);
}

public synchronized ArrayList<Socket> getConnections(){
    return currentConnections;
}

public synchronized ArrayList<String> getUsers(){
    return currentUsers;
}

public synchronized void send(String data){
    try{
        output.println(data);
    }catch(Exception e){

    }
}

public synchronized void close(){
    try{
        sock.close();
    }catch(Exception e){

    }
    output = null;
    input = null;
    System.gc();
}

public synchronized boolean isConnected(){
    return (sock == null) ? false : (sock.isConnected() && !sock.isClosed());
}

@Override
public void run() {
    System.out.println("Is runing.");
    while(true){
        try {
            checkConnetion();
            if(input.readLine().isEmpty()){ 
                return;
            }

            output.println("Server: Recived your message.");
        } catch (Exception e) {

        } finally{
            try {
                sock.close();
            } catch (Exception e) {

            }
        }
    }

}

}

ActLis.class

package com.sever.core;

import java.awt.event.*;
import javax.swing.*;

public class ActLis implements ActionListener{

private JTextField actionField;
public ActLis(){

}

public ActLis(JTextField t){
    actionField = t;
}

@Override
public void actionPerformed(ActionEvent e) {
    // TODO Auto-generated method stub
    String cmd = e.getActionCommand();

    if(cmd == "input"){

    }
}

}

--编辑--- 带有 SwingWorker 的新 Main.class

package com.sever.core;

import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;

import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class Main { 

private SocketManager network;
public static void main(String[] args){

    SwingUtilities.invokeLater(new Runnable(){
        @Override
        public void run() {
            Window window = new Window();
            window.setVisible(true);
        }       
    });

    SwingWorker server = new SwingWorker(){
        @Override
        protected Object doInBackground() throws Exception {
            Main main = new Main();
            main.run();
            return null;
        }

    };
    server.run();

}

public void run(){
    network = new SocketManager(25595);

    while(true){
        try {
            network.setSocket(network.getServerSocket().accept());
            AddUser(network.getSocket());
            Thread x = new Thread(network);
            x.start();
        } catch (Exception e) {
            System.out.println("Failed to connect.");
        }
    }

}

public void AddUser(Socket s){
    try {
        Scanner input = new Scanner(s.getInputStream());
        network.addUser(input.nextLine());
    } catch (Exception e) {

    }
}
}

【问题讨论】:

  • 您的套接字正在 EDT 上运行,因为您使用了 invokeLater。您应该使用 SwingWorker 执行 Main.run()。
  • @GuillaumePolet 好的,谢谢。我读到了这个,但是当我读到的时候,我不确定这是否是我想要的。我会试一试并报告。

标签: java multithreading swing sockets client-server


【解决方案1】:

您正在从 EDT 上的 Socket 读取数据。这意味着你阻止它。调用 invokeLater 只会导致您的 Runnable 在 EDT 上执行。你在 EDT 上堆积了两个调用,其中一个是你的套接字。

考虑将您的套接字移动到 SwingWorker 中,它将逐步将您的 Socker 的值返回到 GUI。

【讨论】:

  • 好的,我已经做到了。它现在可以正常工作而不会冻结,但现在由于某种原因 accept() 仍然没有运行。我编辑了我的问题,并在底部添加了一个 SwingWorker 的更改。
  • @Zexanima 您在 SwingWorker 上调用 run() 而不是 execute()。但这不是唯一的问题,不要在 Main.run() 中创建线程,SwingWorker 将为您创建该线程。最后,不要返回 null,而是返回来自 Socket 的输入。另外,添加 SwingWorker 缺少的类型 ,这样会更干净。
  • 好的,我会这样做的。为什么我要返回我的套接字的输入呢?我不会使用它的返回值(据我所知。)所以它只是为了让我的代码更干净吗?
  • @Zexanima 然后您可以覆盖 process() 方法并使用套接字的输出来更新您的 GUI,因为进程在 EDT 上运行。
  • 这是有道理的,实际上回答了我一直试图弄清楚的另一个问题,即如何从我的套接字中获取我的信息以更新我的 GUI。非常感谢,你真的帮了我很多。可能不会是我关于这个的最后一个问题。 :)
【解决方案2】:

套接字操作块。例如,如果您调用accept,调用将不会返回,直到有人真正连接到您的程序,实际上阻塞它正在运行的线程。这就是为什么他们建议让网络在辅助线程上运行。

在您的情况下,您确实创建了一个线程,但您没有从中调用accept。

while(true) {
    try {
        // this is called on the EDT--beware!
        network.setSocket(network.getServerSocket().accept());
        AddUser(network.getSocket());
        // ... and then, everything called from x will be on the secondary thread
        Thread x = new Thread(network);
        x.start();
    } catch (Exception e) {
        System.out.println("Failed to connect.");
    }
}

这就是它冻结的原因。如果您不想阻塞 UI,则必须从辅助线程调用 accept。

【讨论】:

  • 他在SwingUtilities.invokeLater() 内部打电话给accept 对吧?那不在“主线程”中,而是在 AWT 调度程序线程中。他的程序正在“冻结”,因为他正在阻止 AWT 调度程序线程与接受一起运行。
  • @Gray,我猜是的。我将获得最多动作的线程称为“主线程”。我会编辑它。
  • IMO 术语不正确。我的语言中的主线程是调用static void main()的线程。
  • 你编辑了你的帖子,伙计。你之前有“主线程”这个词。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-31
  • 1970-01-01
  • 1970-01-01
  • 2011-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多