Java 多线程套接字client/server:发送和接收 Enummap 对象
Java Multi threaded socket client/server: sending and receiving Enummap objects
首先,它是一个具有经典 MVC 架构的 JavaFX
应用程序。此应用程序掷骰子并显示它们。目标是将它们显示给多个客户。我正在使用带有套接字的 多线程“回声”服务器 来处理客户端。由于在 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();
}
}
一个典型的场景是这样的:启动服务器 > 启动客户端(连接成功)> 用户按下滚动。
当用户按下滚动时,2个值存储在控制器创建的枚举映射中,控制器通过 send() 方法将此枚举映射转发给客户端线程;并且此方法将枚举映射写入对象输出流。
问题发生在下一步。服务器端处理客户端连接的线程从流
接收枚举映射
_arguments = (EnumMap<ARGS, String>) in.readObject();
但它似乎无法将其转换到 Enummap 并抛出异常
java.lang.ClassCastException: java.io.ObjectStreamClass cannot be cast to java.util.EnumMap
at java.lang.Thread.run(Unknown Source)
我做错了什么?
while(_s.getInputStream().read() != -1) // verifying if connection is still up
问题就在这里。它从输入流中消耗一个字节,因此序列化与发送者不同步。
它也没有完成评论中所述的 objective。正确的方法是捕获发送到断开的连接所导致的 IOException: connection reset
,或读取流结束后所导致的 EOFException
。
首先,它是一个具有经典 MVC 架构的 JavaFX
应用程序。此应用程序掷骰子并显示它们。目标是将它们显示给多个客户。我正在使用带有套接字的 多线程“回声”服务器 来处理客户端。由于在 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();
}
}
一个典型的场景是这样的:启动服务器 > 启动客户端(连接成功)> 用户按下滚动。 当用户按下滚动时,2个值存储在控制器创建的枚举映射中,控制器通过 send() 方法将此枚举映射转发给客户端线程;并且此方法将枚举映射写入对象输出流。
问题发生在下一步。服务器端处理客户端连接的线程从流
接收枚举映射_arguments = (EnumMap<ARGS, String>) in.readObject();
但它似乎无法将其转换到 Enummap 并抛出异常
java.lang.ClassCastException: java.io.ObjectStreamClass cannot be cast to java.util.EnumMap
at java.lang.Thread.run(Unknown Source)
我做错了什么?
while(_s.getInputStream().read() != -1) // verifying if connection is still up
问题就在这里。它从输入流中消耗一个字节,因此序列化与发送者不同步。
它也没有完成评论中所述的 objective。正确的方法是捕获发送到断开的连接所导致的 IOException: connection reset
,或读取流结束后所导致的 EOFException
。