【问题标题】:Java Multi threaded socket client/server: sending and receiving Enummap objectsJava 多线程套接字客户端/服务器:发送和接收 Enummap 对象
【发布时间】:2015-07-18 14:24:54
【问题描述】:

首先,它是一个具有经典 MVC 架构的JavaFX 应用程序。此应用程序掷骰子并显示它们。目标是将它们展示给几个客户。我正在使用带有套接字的多线程“echo”服务器来处理客户端。由于在 JavaFX 中我们不能直接通过套接字发送节点,所以我决定发送由客户端生成的 arguments,然后客户端将它们发送到服务器,以便它可以将参数回显给所有连接的客户端.

这是它的工作原理:

首先,创建主服务器线程。它创建一个ServerSocket 和一个while 循环创建一个新线程来处理连接的客户端。这个主服务器线程有 3 个方法:2 个将跟踪连接的客户端,1 个将传入的参数发送到所有连接的客户端。

public class DiceRollServerThread implements Runnable
{
    private Server _server;
    private Server_C controller;
    private Vector<ObjectOutputStream> tabClients = new Vector<ObjectOutputStream>(); // Contain all outpput streams to connected clients
    private Thread t;
    private ServerSocket diceSS;
    
    public DiceRollServerThread(Server server) throws IOException
    {
        _server = server;
        
        controller = _server.getController();

        String port = "2000";
        String ip = "127.0.0.1";
        
        controller.setConsole("IP : "+ip+"\n"+"Port : "+port);
        
        diceSS = new ServerSocket(Integer.parseInt(port), 0, InetAddress.getByName(null));

        t = new Thread(this);
        t.start();
    }
    
    @Override
    public void run()
    {
        while (true) // bloquing on ss.accept
        {
            try
            {
                new DiceRollThread(diceSS.accept(), this);
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }
    
    synchronized public void sendAll(EnumMap<ARGS, String> arguments) throws IOException
    {
        ObjectOutputStream out;
        
        for (int i = 0; i < tabClients.size(); i++) // browsing connected clients
        {
            out = (ObjectOutputStream) tabClients.elementAt(i);
            if (out != null)
            {
                out.writeObject(arguments);
                out.flush();
            }
        }
    }
    
    synchronized public void delClient(int i)
    {
        if (tabClients.elementAt(i) != null) // If element exist ...
        {
            tabClients.removeElementAt(i); // ... delete it
            System.out.println("delClient");
        }
    }
    
    synchronized public int addClient(ObjectOutputStream out)
    {
        tabClients.addElement(out); // Adding new output stream to vector
        System.out.println("addClient");    
        return tabClients.size()-1; // return client number (size-1)
    }
    
    public Server get_server()
    {
        return _server;
    }
}

这里是线程处理客户端:

public class DiceRollThread implements Runnable
{
    private Thread t;
    private Socket _s;
    private ObjectOutputStream out;
    private ObjectInputStream in;
    private DiceRollServerThread _serverThread; // to use main thread methods
    private int numClient=0;
    private Server _server;
    private EnumMap<ARGS,String> _arguments;
    
    DiceRollThread(Socket s, DiceRollServerThread serverThread)
    {
        _s = s;
        _serverThread = serverThread;
        _server = _serverThread.get_server();
        
        try
        {
            out = new ObjectOutputStream(_s.getOutputStream());
            in = new ObjectInputStream(_s.getInputStream());
            numClient = _serverThread.addClient(out);
            _server.getController().setConsole("Client n°"+numClient+" connected.");
        }
        catch (IOException e)
        {
            
        }
        
        t = new Thread(this);
        t.start();
    }
    
    @Override
    public void run()
    {
        try
        {
            while(_s.getInputStream().read() != -1) // verifying if connection is still up
            {
                _arguments = (EnumMap<ARGS, String>) in.readObject(); // Problem is here
                
                if(_arguments != null)
                {
                    System.out.println(_arguments);
                    _serverThread.sendAll(_arguments);
                }
            }
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally // Usually happens when client disconnect
        {
            try
            {
                _server.getController().setConsole("Client n°"+numClient+" disconnected.");
                _serverThread.delClient(numClient); // deleting from the vector
                _s.close(); // Closing socket if not done by upper exception
            }
            catch (IOException e)
            {
                
            }
        }
    }

现在在客户端。主应用程序有一个控制器,它将获取两个字段的值并将它们存储在以下Enummap

public class Arguments
{
    public enum ARGS {Couleur,Valeur};
}

然后将其发送到下面的线程处理连接到服务器。

private void roll()
{
    arguments.put(ARGS.Couleur, box_couleur.getValue().toString());
    arguments.put(ARGS.Valeur, randInt(1,Integer.parseInt(box_de.getValue())));

    diceRollThread.send(arguments); // the thread gives his reference to the controller when created
}

客户端线程(连接到服务器)

public class DiceRollThread implements Runnable
{
    private DiceRoll_C _controller;
    private Socket s;
    private ObjectOutputStream out;
    private ObjectInputStream in;
    private Thread t;
    private EnumMap<ARGS,String> _arguments;
            
    DiceRollThread(DiceRoll diceroll) throws IOException
    {   
        _controller = diceroll.getController();
        _controller.setDiceRollThread(this);

        String port = "2000";
        String ip = "127.0.0.1";
        
        try
        {
            s = new Socket(InetAddress.getByName(ip),Integer.parseInt(port));
            out = new ObjectOutputStream(s.getOutputStream());
            in = new ObjectInputStream(s.getInputStream());
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        
        t = new Thread();
        t.start();
    }
    
    @Override
    public void run()
    {
        try
        {
            _arguments = (EnumMap<ARGS, String>) in.readObject();
            
            if(_arguments != null)
            {
                _controller.addDice(_arguments); // Creating dice from received arguments
            }
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            try
            {
                s.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }
    
    public void send(EnumMap<ARGS,String> arguments) // arguments received from the controller, sends them to the server
    {
        try
        {
            out.writeObject(arguments);
            out.flush();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

一个典型的场景是这样的:启动服务器>启动客户端(连接成功)>用户按下滚动。 当用户按下roll时,控制器创建的一个enummap中存储了2个值,控制器通过send()方法将此enummap转发给客户端线程;此方法将枚举映射写入对象输出流。

问题发生在下一步。服务器端处理客户端连接的线程从流中接收枚举映射

_arguments = (EnumMap<ARGS, String>) in.readObject();

但它似乎无法将其强制转换到 Enummap 并引发异常

java.lang.ClassCastException: java.io.ObjectStreamClass 不能转换为 java.util.EnumMap

在 java.lang.Thread.run(未知来源)

我做错了什么?

【问题讨论】:

    标签: java sockets casting javafx-8 enum-map


    【解决方案1】:
    while(_s.getInputStream().read() != -1) // verifying if connection is still up
    

    问题就在这里。它消耗输入流中的一个字节,因此使序列化与发送方不同步。

    它也没有实现评论中所述的目标。正确的做法是捕获由于发送到断开的连接而导致的IOException: connection reset,或由于读取流结束而导致的EOFException

    【讨论】:

    • 改成while(true)就没有错误了。谢谢!
    猜你喜欢
    • 2012-12-14
    • 1970-01-01
    • 2011-10-22
    • 2020-07-10
    • 2013-11-22
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    • 2019-06-03
    相关资源
    最近更新 更多