Java - Threads, Swing, and ServerSocket

2019-02-19 06:33发布

问题:

I know I know, already a million questions and answers on this everywhere. Tons of really detailed articles on it, several types of examples. I've spent the past few hours reading about it, but that's not doing the trick. The reason I'm asking this is because I still don't quiet understand what I need to do apparently because my code is still not working. I get the idea of how Swing works with the EDT, and if I'm going to use the ServerSocket's accept() method I'm going to need to start a new thread for Swing (I think?). When I run my code as it is the window just opens and freezes. My question is could someone look at my code, tell me what I'm doing wrong, and explain it to me like I only have half a brain?(Because that's what I feels like. :P )

Here are some of the places I've read already:

  • This question from SO.
  • This site.
  • And of course the Oracle's explanation of swing and threads.
  • And a few other random sites.

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"){

    }
}

}

--EDIT--- The new Main.class with SwingWorker

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) {

    }
}
}

回答1:

You are reading from your Socket on the EDT. This means that you block it. Calling invokeLater only causes your Runnable to be executed on the EDT. You are piling two calls on the EDT, one of them being your socket.

Consider moving your socket in a SwingWorker which returns progressively the values of your Socker to the GUI.



回答2:

Socket operations block. For instance, if you call accept, the call will not return until someone actually connects to your program, effectively blocking the thread it's running on. That's why they suggest to have the networking run on a secondary thread.

In your case, you do create a thread, but you don't call accept from it.

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.");
    }
}

This is why it freezes. The call to accept must be done from the secondary thread if you don't want to block the UI.