【发布时间】:2015-11-03 03:13:58
【问题描述】:
我有一个服务器,它为连接到它的每个客户端创建一个线程。主服务器类称为“Server”,而每个客户端由源自 Server 的“ClientManager”类的线程管理。服务器有一个静态的棋子数组列表(用于麻将游戏),当每个客户端绘制时,ClientManager 会从数组列表中删除那么多的棋子。我有执行此同步的方法(可能不正确),但是当我运行程序时,就好像第一个玩家没有正确地从列表中删除图块。当我查看调试列表时,它显示“玩家 1 绘制,剩余 144 个图块”,而它应该显示为 131。如果我在调试模式下运行程序,它工作得非常好。如果我将 Thread.sleep 添加到处理所有这些的 Server 中的 main 方法中,它可以工作,但我不喜欢令人难以置信的长时间等待,并且希望数组列表正确同步并正确更新。第一次运行应该使列表下降 13,然后下一个字符绘制 13,依此类推。之后,他们将各抽 1 分,但移除并没有反映在游戏中。
这是来自客户端、服务器和客户端管理器的相关代码
主要:
public class Main {
static GamePanel gameGUI = new GamePanel();
static Client client;
static Player me = new Player();
//!!? is used to mark a username command
@SuppressWarnings("unchecked")
public static void main(String[] args) throws IOException{
String host = "107.199.245.55";
//String host = "107.199.244.144";
//String host = "localhost";
boolean created = false;
ArrayList<?> Temp = new ArrayList<Object>();
ArrayList<Tile> TileStack = new ArrayList<Tile>();
Object object = null;
int tiles = 0;
String username = "!!?" + JOptionPane.showInputDialog("Username:");
if(username.substring(3, username.length()).equals("null")){return;}
while(username.substring(3, username.length()).isEmpty()){
username = "!!?" + JOptionPane.showInputDialog("Must have an actual name\nUsername:");
}
int port = 27016;
//int port = 2626;
//int port = 4444;
client = new Client(host, port, username);
client.send(username);
gameGUI.initialize();
gameGUI.lblPlayerNameYou.setText(username.substring(3, username.length()));
waitForPlayers();
while(true)
{
try {
object = client.receive();
if(object instanceof ArrayList<?>)
{
Temp = (ArrayList<?>) object;
if(Temp.get(0) instanceof String)
{
setUpNames(username, Temp);
}
else if(Temp.get(0) instanceof Tile)
{
if(!created){
TileStack = (ArrayList<Tile>) Temp;
created = true;
for (int i = 0; i < 13; i++){me.drawOneTile(TileStack);}
client.send(13);
}
else if(created){
me.drawOneTile(TileStack);
client.send(1);
}
gameGUI.displayHand(me.hand);
}
}
else if(object instanceof Integer){
tiles = (int) object;
gameGUI.tilesRemaining.setText("Remaining Tiles: " + tiles);
}
} catch (IOException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "You were disconnected. Exiting game.");
gameGUI.dispose();
break;
}
}
}
服务器:
public class Server {
public static ArrayList<ObjectOutputStream> ConnectionArray = new ArrayList<ObjectOutputStream>();
public ArrayList<String> CurrentUsers = new ArrayList<String>();
public static ArrayList<Socket> ConnectionArraySOCKET = new ArrayList<Socket>();
public ArrayList<Tile> TileStack = new ArrayList<Tile>();
public static ServerGUI serverGUI;
public int port;
public ServerSocket SERVERSOCK;
public static GameLoop game = new GameLoop();
public static Server server;
public Server(int port){
this.port = port;
try {
SERVERSOCK = new ServerSocket(port);
} catch (IOException e) {
System.out.println("Unable to start");
e.printStackTrace();
}
serverGUI = new ServerGUI();
serverGUI.initialize();
}
public ArrayList<String> getCurrentUsers(){return CurrentUsers;}
public ArrayList<Tile> getTileStack(){return TileStack;}
public void waitForClients(ServerSocket SERVERSOCK)
{
serverGUI.addText(serverGUI.getDebugPane(), "Waiting for clients...");
while(ConnectionArray.size() < 4 && CurrentUsers.size() < 4)
{
try{
if (!(ConnectionArray.size() == 0)) shareToAll(ConnectionArray.size());
Socket client = SERVERSOCK.accept();
ConnectionArray.add(new ObjectOutputStream(client.getOutputStream()));
ConnectionArraySOCKET.add(client);
Thread t = new Thread(new ClientManager(client));
t.start();
if (!(ConnectionArray.size() == 0)) shareToAll(ConnectionArray.size());
}
catch(IOException e){e.printStackTrace();}
}
}
public static void shareToAll(Object o){
for(ObjectOutputStream stream : ConnectionArray)
{
try{
Thread.sleep(100);
stream.writeObject(o);
stream.reset();
stream.flush();
}
catch(Exception e){
e.printStackTrace();
}
}
}
public static void distributeTileStack(Object o, int playerNum){
try {
ConnectionArray.get(playerNum).writeObject(o);
ConnectionArray.get(playerNum).reset();
ConnectionArray.get(playerNum).flush();
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean checkConnection()
{
if(ConnectionArray.size() < 4) return false;
else return true;
}
public static void main(String[] args) throws InterruptedException
{
server = new Server(Integer.parseInt(JOptionPane.showInputDialog("Port:")));
ArrayList<Tile> temp = new ArrayList<Tile>();
while(!serverGUI.started){
System.out.println("LOOP");
}
server.waitForClients(server.SERVERSOCK);
Server.shareToAll(server.getCurrentUsers());
game.createStack();
server.TileStack = game.getStack();
for (int i = 0; i <= 3; i++){
serverGUI.addText(serverGUI.getDebugPane(), "Player " + (i + 1) + " drawing tiles.");
temp = server.getTileStack();
Server.distributeTileStack(server.TileStack, i);
serverGUI.addText(serverGUI.getDebugPane(), "Tilestack size: " + server.getTileStack().size());
Server.shareToAll(server.getTileStack().size());
}
while(checkConnection()){
// game logic here
}
JOptionPane.showMessageDialog(null, "Player disconnected. The server will now close.");
serverGUI.btnStopServer.doClick();
serverGUI.dispose();
}
}
客户经理:
public class ClientManager extends Thread implements Runnable {
Socket SOCK;
String username;
public ClientManager(Socket SOCK)
{
this.SOCK = SOCK;
}
public void run(){
boolean working = true;
try{
ObjectInputStream inStream = new ObjectInputStream(SOCK.getInputStream());
while(working){
working = handle(inStream);
}
}
catch(SocketException e){
e.printStackTrace();
System.out.println("Cannot get inputstream");
}
catch(IOException e){
e.printStackTrace();
}
}
public boolean handle(ObjectInputStream inStream){
Object object = null;
String string;
try{
object = inStream.readObject();
if(object instanceof String)
{
string = (String)object;
if(string.startsWith("!!?")){
username = string.substring(3, string.length());
synchronized (Server.class){
Server.server.CurrentUsers.add(username);
}
Server.serverGUI.addText(Server.serverGUI.getDebugPane(), "User connected: " + username + SOCK.getRemoteSocketAddress());
Server.serverGUI.addText(Server.serverGUI.getUsersPane(), username);
Server.serverGUI.addText(Server.serverGUI.getDebugPane(), "Number of users: " + Server.server.CurrentUsers.size());
}
}
if (object instanceof Integer)
{
synchronized(Server.class){
for (int i = 0; i < (int) object; i++)
Server.server.TileStack.remove(0);
}
}
}
catch(ClassNotFoundException ce){ce.printStackTrace();}
catch(IOException e){
e.printStackTrace();
for(int i = Server.ConnectionArray.size() - 1; i >= 0; i--)
{
if(Server.ConnectionArraySOCKET.get(i).equals(SOCK))
{
Server.serverGUI.addText(Server.serverGUI.getDebugPane(), "User " + Server.server.CurrentUsers.get(i) + SOCK.getRemoteSocketAddress() + " disconnected");
Server.serverGUI.clearText(Server.serverGUI.getUsersPane());
Server.server.CurrentUsers.remove(i);
Server.serverGUI.addText(Server.serverGUI.getDebugPane(), "Number of users: " + Server.server.CurrentUsers.size());
Server.ConnectionArraySOCKET.remove(i);
Server.ConnectionArray.remove(i);
for (int i2 = 1; i2 <= Server.server.CurrentUsers.size(); i2++){
Server.serverGUI.addText(Server.serverGUI.getUsersPane(), Server.server.CurrentUsers.get(i2-1));
}
if (!(Server.ConnectionArray.size() == 0)) Server.shareToAll(Server.ConnectionArray.size());
}
}
return false;
}
return true;
}
}
【问题讨论】:
-
您是否考虑过使用
Collections.synchronizedList将原始List包装在同步包装器中 -
我试过一次,它似乎没有解决任何问题。我在我的 Server 类中创建了一个 Public Static List
CurrentUsers = Collections.synchronizedList(new ArrayList ) ,所有其他类都访问了 CurrentUsers 但它没有改变任何东西。 -
我去看看。谢谢。
标签: java multithreading networking synchronization server