【问题标题】:Socket is not sending/receiving over multi-threaded java program套接字未通过多线程 java 程序发送/接收
【发布时间】:2017-01-05 21:06:52
【问题描述】:

我正在为在服务器和客户端系统上运行的游戏创建聊天,当然还有在服务器客户端系统上运行的游戏,但我无法让我的多线程程序运行,我将在下面解释如何.我可以说的是服务器客户端系统在单线程时工作。

首先,我的连接类:

public class Connection {

private Server server;
private Client client;
private boolean serverChosen;

public Connection(){
    server = new Server();
    this.serverChosen = true;
}

public Connection(String IP){
    client = new Client(IP);
    this.serverChosen = false;
}
public synchronized void sendObjects(Object obj) throws IOException{
    if(serverChosen){
        server.sendObjects(obj);
    }else{
        client.sendObjects(obj);
    }

}   
public synchronized Object receiveObjects() throws ClassNotFoundException, IOException{
    if(serverChosen){
        return server.receiveObjects();
    }else{
        return client.receiveObjects();
    }
}

public Object waitForObject() throws ClassNotFoundException, IOException{
    int i = 0;
    Object obj;
    while(true){
        obj = receiveObjects();
        i++;
        if(obj != null || i >= 100){
            return obj;
        }
    }
}
}

服务器和客户端类:

public class Server implements Serializable{



private ObjectOutputStream output;
private ObjectInputStream input;
private ServerSocket server;
private Socket connection;

//constructor
public Server(){

    try{
        server = new ServerSocket(8790, 10); //8798 is a dummy port for testing, this can be changed. The 100 is the maximum people waiting to connect.
            try{
                //Trying to connect and have conversation
                waitForConnection();
                setupStreams();
            }catch(EOFException eofException){
            }
    } catch (IOException ioException){
        ioException.printStackTrace();
    }
}

//wait for connection, then display connection information
private void waitForConnection() throws IOException{
    connection = server.accept();
}

//get stream to send and receive data
private void setupStreams() throws IOException{
    output = new ObjectOutputStream(connection.getOutputStream());
    output.flush();

    input = new ObjectInputStream(connection.getInputStream());

}


public ObjectOutputStream getOutput(){
    return output;
}
public ObjectInputStream getInput(){
    return input;
}

public void sendObjects(Object obj) throws IOException{
        output.writeObject(obj);
        output.flush();

}   
public Object receiveObjects() throws IOException, ClassNotFoundException{
        return input.readObject();
}

}

public class Client extends JFrame implements Serializable{


private static final long serialVersionUID = 1L;
private ObjectOutputStream output;
private ObjectInputStream input;

private String serverIP;
private Socket connection;
private boolean next = true;

//constructor
public Client(String host){

    serverIP = host;

    try{
        connectToServer();
        setupStreams();
    }catch(EOFException eofException){
    }catch(IOException ioException){
        ioException.printStackTrace();
    }
}

public Client(){

    serverIP = "127.0.0.1";

    try{
        connectToServer();
        if(next){
                setupStreams();
        }
    }catch(EOFException eofException){
        //t.append("Connection was terminated");
    }catch(IOException ioException){
        ioException.printStackTrace();
    }
}



//connect to server
private void connectToServer(){
    int i = 0;
    do {
        try {
            connection = new Socket(InetAddress.getByName(serverIP), 8790);
            next = true;
        } catch (IOException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, "Server was not found, The program will try again in 1 second; number of tries left: " + (10-i));
            next = false;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            i++;
        }
    }while(!next && i<=10);
    if(!next){
        JOptionPane.showMessageDialog(null, "Unable to connect to the server. Make sure the I.P. adress is correct, the ports are not blocked, or a firewall has not prevented connections.... IDK man... it's just me and all of this code... maybe the server isn't even running??");
    }
}

//set up streams
private void setupStreams() throws IOException{
    output = new ObjectOutputStream(connection.getOutputStream());
    output.flush();
    input = new ObjectInputStream(connection.getInputStream());
}



public ObjectOutputStream getOutput(){
    return output;
}
public ObjectInputStream getInput(){
    return input;
}

public void sendObjects(Object obj) throws IOException{
        output.writeObject(obj);
        output.flush();

}

public Object receiveObjects() throws ClassNotFoundException, IOException{
        return input.readObject();
}

}

我运行一切的类是 Frame 类:

public class Frame implements ActionListener{

private int width;
private int height;
private JFrame jframe;
private Board board;
private JTextArea textBox;
private JScrollPane pane;
private Connection connection;
private JTextArea userText;
private JScrollPane userPane;
private JButton send;
private NetworkReceiver net;


public Frame(int width, int height, Connection connection, Board player, Board opponent){
this.width = width;
this.height = height;
this.connection = connection;

board = new Board(player, opponent);
init();
textBox = new JTextArea("CHAT WINDOW");
textBox.setWrapStyleWord(true);
textBox.setLineWrap(true);
textBox.setBackground(Color.GRAY);
textBox.setBounds(height, 0, width - (height) - 20, height-40);
textBox.setEditable(false);

userText = new JTextArea("Enter Messages Here");
userText.setWrapStyleWord(true);
userText.setLineWrap(true);
userText.setBackground(Color.WHITE);
userText.setBounds(height, height-40, width - (height) - 20, 40);

pane = new JScrollPane(textBox, 
        ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
pane.setBounds(height, 0, width - (height), height-40);

userPane = new JScrollPane(userText, 
        ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
userPane.setBounds(height, height-40, width - (height) - 20, 40);

send = new JButton();
send.setIcon(Utility.getImageIcon(Utility.getBufferedImage(Assets.send)));
send.setBounds(width - 20, height - 40, 20, 40);
send.addActionListener(this);
send.setBackground(Color.WHITE);


jframe = new JFrame();
jframe.setBackground(Color.DARK_GRAY);
jframe.getContentPane().setPreferredSize(new Dimension(width, height));
jframe.setLayout(null);
jframe.pack();

jframe.setLocationRelativeTo(null);
jframe.add(pane);
jframe.add(userPane);
jframe.add(send);

for(Space[] s: board.getSpaces()){
    for(Space space: s){
        jframe.add(space);
    }
}
for(Space[] s: board.getSpaces()){
    for(Space space: s){
        space.addActionListener(this);
    }
}

for(Space[] s: board.getSpaces()){
    for(Space space: s){
        if(space.getTile() != null){
            space.setBackground(space.getTile().getColor());
        }
    }
}
for(Space[] s: board.getSpaces()){
    for(Space space: s){
        if(space.getPiece() != null){
            space.setIcon(Utility.getImageIcon(space.getPiece().getImage()));
        }
    }
}

jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setVisible(true);

net = new NetworkReceiver(connection, this);
net.start();
}

private void init(){        
  //stuff
}

@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == send){
    send();
}

}

private synchronized void send() {
String message = "YOU- " + userText.getText();
userText.setText("");
String totalMessage = textBox.getText();
textBox.setText(totalMessage + "\n" + message);
new NetworkSender(connection, message).start();
}

public synchronized void showMessage(String s){
String totalMessage = "Opponent- " + textBox.getText();
textBox.setText(totalMessage + "\n" + s);
}

我不想在上面的构造函数中删除更多内容,以防它以某种方式导致问题(我对此表示怀疑,但因为我找不到比抱歉更安全的问题)

这里是 NetworkSender 和 NetworkReceiver 类:

public class NetworkSender extends Thread{

private Connection c;
private String msg;

public NetworkSender(Connection c, String msg){
this.c = c;
this.msg = msg;
}

public void run(){
try {
    System.out.println("Trying to send");
    c.sendObjects(msg);
    System.out.println("Sent");
} catch (IOException e) {
    e.printStackTrace();
}
}

}

public class NetworkReceiver extends Thread{

private Connection c;
private boolean running = true;
private Frame f;

public NetworkReceiver(Connection c, Frame f){
this.c = c;
this.f = f;
}

public void run(){
while(running){
    System.out.println("running");
    Object obj = null;
    try {
        obj = c.receiveObjects();
        System.out.println("received");
    } catch (ClassNotFoundException e) {
        System.out.println("failed - class exception");
        e.printStackTrace();
    } catch (IOException e) {
        System.out.println("failed - IO exception");
        e.printStackTrace();
        running = false;
    }
    if(obj != null){
        if(obj instanceof String){
            f.showMessage((String)obj);
        }
    }
}
}

public void kill(){
running = false;
}

}

确切的中断点在 NetworkSender 类中。在控制台中,我收到“尝试发送”但从未“发送”。这让我认为我设置了错误的线程,线程不知何故无法相互通信(发送者和接收者线程),或者服务器/客户端系统未正确完成多线程。

我已经广泛地查找了这个问题,但在任何地方都找不到有效的解决方案,所以如果有我看过的解决方案,我深表歉意。如果一切正常,我可以在需要时发布我的连接、服务器和客户端类,但我假设它们是正常的,因为它们在 EDT 上工作。由于我在网上搜索了将近 4 个小时,我问如果您认为这是一个重复的问题,请先评论一个链接,因为我几乎可以肯定我已经看过它了,哈哈。

【问题讨论】:

  • 这一切看起来都很可疑,但你没有展示最重要的东西:连接。您可能在连接类中使用粗略同步使线程死锁(例如,NetworkReceiver 在 Connection 上持有监视器阻止其他所有内容)。整个代码对我来说看起来很可疑,扩展了 Thread 并为每条消息创建了一个新的 NetworkSender,所有的消息都敲响了警钟。
  • @Durandal,我会发布我的连接和服务器/客户端类。死锁听起来是可能的,但我对套接字的了解还不够,无法确定。我希望新代码可以帮助你帮助我哈哈
  • 很明显,您在同一个对象(连接)上同步发送和接收。这意味着当 NetworkReceiver 等待接收数据时,什么都没有可以发送。我会重新考虑整个设计,特别是仔细考虑在哪个对象上同步的内容。还要重新考虑放置方法 - 我有理由期望 Connection 类代表一个连接的 endpoint,在客户端/服务器之间没有切换逻辑。如果您将 Connection 建立在 Socket 对象的基础上,那么差异的需求就消失了。
  • 连接要么是服务器,要么是客户端。它永远不会在它们之间切换。我是这样实现的,因为这样我就不需要制作同一个游戏的 2 个不同版本。

标签: java multithreading sockets server


【解决方案1】:

直接原因是您在 Connection 对象本身上同步接收和发送。这意味着当一个线程在receiveObjects() 中时,没有其他线程可以进入send 或receiveObjects()。因此,您的接收线程会永久阻止您发送。

您可以通过从连接类的方法中删除同步来使其工作,而是使客户端和服务器上的相应方法同步所选的不同对象,例如相应的输入/输出-流。

尽管如此,它还是一个设计灾难(这里不废话)。您最好的做法可能是重新考虑设计 - 您对抽象的选择(或更确切地说是缺乏抽象)以及您在分配职责方面所做的选择留有改进的空间。

【讨论】:

  • 潜在的即时修复很好,但是应该如何以正确的方式进行呢?您提到缺乏抽象,应该进行哪些抽象?我很欣赏关于为什么以及在哪里出现问题的知识,现在我知道要寻找什么,我会更多地研究它。但是,如果您能澄清您提出的一些观点,我将非常感激。
  • 这不是我不想告诉你,问题是要澄清我需要编写大量带有类存根的文本所需的一切。它基本上是关于多个主题的教程,例如 API 设计、套接字编程、线程/同步和泛型。
  • 不管我欣赏这些主题。我会调查他们,看看我能做些什么。
猜你喜欢
  • 2016-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-21
  • 2013-06-18
  • 1970-01-01
  • 1970-01-01
  • 2020-03-28
相关资源
最近更新 更多