具有tcp / ip套接字的客户端服务器中的种族或死锁

问题描述 投票:0回答:1

我正在研究一个简单的Java客户端-服务器游戏。服务器和客户端都同时运行多个线程。在服务器上,有两个并发任务(可调用地在多个线程上运行),它们彼此随机地进行战斗。迟早,客户端连接到服务器后,它们会相互阻塞(不知道可能出现死锁,可能会发生争用),并且整个连接会冻结。我在这里检查了许多相关主题,在客户端和服务器上尝试了许多修改,设计和逻辑的许多更改。不幸的是尚未设法解决它。如果您需要其他课程,请随时提问。非常感谢您的帮助。

下面是这两个阻塞任务类(服务器端)。

public class ClientUpdateTask extends AbstractClientTask {

private final String location = "update task execute";

public ClientUpdateTask(String clientToken, ServerManager serverManager, 
        ReadWriteLock clientTaskLock) {
    super(clientToken, serverManager, GameConditions.UPDATE_TASK, clientTaskLock);
}

@Override
public Boolean call() throws /*Exception*/ IOException {
    if(clientTaskLock.readLock().tryLock()) {
    try {
        execute();
        return true;
    } catch(IOException e) {
        String exLocation = "update task execute ioex";
        ClientDisconnectedException cde = new ClientDisconnectedException(
                clientToken, exLocation, e.getMessage(), e);
        error = cde;
        throw cde;
        //throw new RuntimeException("ex updating clients", e);
        //throw e;
    } finally {clientTaskLock.readLock().unlock();}
    }
    return false;
}

@Override
protected void execute() throws IOException {
    if(!serverManager.areAtleastTwoConnected())
        return;
    Client client = serverManager.getClient(clientToken, location);
    String clientAddress = client.getSocket().getRemoteSocketAddress().toString();
    client.printLine(GameConditions.SERVER_MODE_SEND);
    String inSerEntsLenTxt = client.readLine();
    printClientPacket(clientAddress, "after 1st readl, ents len: "+inSerEntsLenTxt);
    if(inSerEntsLenTxt != null && !(inSerEntsLenTxt.isEmpty())) {
        int inSerEntsLen = Integer.parseInt(inSerEntsLenTxt);
        inStreamBuff = new char[inSerEntsLen];
        inStreamBuff = client.readCharsToArray(inStreamBuff, 0, inSerEntsLen);
        String inSerEnts = new String(inStreamBuff);
        printClientPacket(clientAddress, "in ser ents: "+inSerEnts);
        //List<Client> clients = serverManager.getClients();
        if(clientTaskLock.writeLock().tryLock()) {
        try {
        for(Client clientOther : serverManager.getClients()) {
            String clientOtherToken = clientOther.getToken();
            if(!clientOtherToken.equals(clientToken)) {
                clientOther.printLine(GameConditions.SERVER_MODE_RECEIVE);
                clientOther.printLine(inSerEntsLen);
                clientOther.writeChars(inSerEnts);
            }
        }
        } finally {clientTaskLock.writeLock().unlock();}
        }
    }
}

}

public class ClientHeartbeatTask extends AbstractClientTask {

private final String location = "heartbeat task execute";

public ClientHeartbeatTask(String clientToken, ServerManager serverManager,
        ReadWriteLock clientTaskLock) {
    super(clientToken, serverManager, GameConditions.HEARTBEAT_TASK, clientTaskLock);
}

@Override
public Boolean call() throws /*Exception*/ IOException {
    if(clientTaskLock.readLock().tryLock()) {
    try {
        execute();
        return true;
    } catch(IOException e) {
        String exLocation = "heartbeat task execute ioex";
        ClientDisconnectedException cde = new ClientDisconnectedException(
                clientToken, exLocation, e.getMessage(), e);
        error = cde;
        throw cde;
        //throw new RuntimeException("ex listening client heartbeat", e);
        //throw e;
    } finally {clientTaskLock.readLock().unlock();}
    }
    return false;
}

@Override
protected void execute() throws IOException {
    Client client = serverManager.getClient(clientToken, location);
    String clientAddress = client.getSocket().getRemoteSocketAddress().toString();
    String inToken = "";
    //System.out.println("listening...");
    client.printLine(GameConditions.SERVER_MODE_HEARTBEAT); //1
    client.printLine(clientToken);
    inToken = client.readLine(); //2
    if(inToken != null) {
        Matcher tokenMatcher = GameConditions.TOKEN_PATTERN.matcher(inToken);
        //System.out.println("in token not null");
        if(tokenMatcher.matches()) {
            printClientPacket(clientAddress, "after 1st readl, token matched: " + inToken);
            serverManager.updateClient(clientToken, location);
        }
    } else {
        long idle = (long) (System.currentTimeMillis() - client.getLastHeartBeat());
        //System.out.println("idle: " + idle);
        if(idle > GameConditions.CLIENT_MAX_IDLE_TIME) {
            serverManager.disconnectClient(clientToken);
            throw new IOException("client disconnected, throw to "
                + "close resources and stop thread");
        }
    }
}

}

编辑Donno毫不客气地说,但是到目前为止,我设法做到的最好的是,客户端任务(与这两个任务相通)接收来自服务器的消息(直到此事开始)。但是服务器是静音的,没有收到消息。

java multithreading synchronization locking java.util.concurrent
1个回答
0
投票

我忘记发布解决方案了,就在这里。从更新和心跳任务中删除了直接输入/输出流访问。它们都在客户端和服务器上的多个线程上运行,并且相信这是阻塞线程和整个通信的原因。而是将直接流访问权限移至具有两个ArrayBlockingQueues的单独的类(客户端)并为套接字的流读写提供线程安全接口。所有传入消息都添加到输入队列,所有传出消息都添加到输出队列。现在,在客户端和服务器中又创建了两个任务,一个任务读取输入队列,另一个任务从输出队列写入。而且在此过程中,我的大部分同步和锁被证明是无用的(并且可能还会阻塞线程,并且肯定会降低性能)。

更改的更新任务:

public class ClientUpdateTask extends AbstractClientTask {

    private final String location = "update task execute";

    public ClientUpdateTask(String clientToken, ServerManager serverManager, 
            ReadWriteLock clientTaskLock) {
        super(clientToken, serverManager, GameConditions.UPDATE_TASK, clientTaskLock);
    }

    @Override
    public Boolean call() throws /*Exception*/ IOException {
        try {
            execute();
            return true;
        } catch(IOException e) {
            String exLocation = "update task execute ioex";
            ClientDisconnectedException cde = new ClientDisconnectedException(
                    clientToken, exLocation, e.getMessage(), e);
            error = cde;
            throw cde;
        }
    }

    @Override
    protected void execute() throws IOException {
        if(!serverManager.areAtleastTwoConnected())
            return;
        Client client = serverManager.getClient(clientToken, location);
        String clientAddress = client.getSocket().getRemoteSocketAddress().toString();
        String inMessage = client.peekInMessage();
        if(inMessage != null && !inMessage.isEmpty() && 
                inMessage.startsWith(GameConditions.SERVER_MODE_SEND)) {
            client.pollInMessage();
            String[] inMessageSplitArray = inMessage.split(
                    GameConditions.MESSAGE_FRAGMENT_SEPARATOR);
            String inSerEnts = inMessageSplitArray[1];
            for(Client clientOther : serverManager.getClients()) {
                String clientOtherToken = clientOther.getToken();
                if(!clientOtherToken.equals(clientToken)) {
                    String outMessage = GameConditions.SERVER_MODE_RECEIVE
                        + GameConditions.MESSAGE_FRAGMENT_SEPARATOR + inSerEnts;
                    clientOther.pushOutMessage(outMessage);
                }
            }
        }
        String outMessage = GameConditions.SERVER_MODE_SEND;
        client.pushOutMessage(outMessage);
    }
}

更改的心跳任务:

public class ClientHeartbeatTask extends AbstractClientTask {

    private final String location = "heartbeat task execute";

    public ClientHeartbeatTask(String clientToken, ServerManager serverManager,
            ReadWriteLock clientTaskLock) {
        super(clientToken, serverManager, GameConditions.HEARTBEAT_TASK, clientTaskLock);
    }

    @Override
    public Boolean call() throws /*Exception*/ IOException {
        try {
            execute();
            return true;
        } catch(IOException e) {
            String exLocation = "heartbeat task execute ioex";
            ClientDisconnectedException cde = new ClientDisconnectedException(
                    clientToken, exLocation, e.getMessage(), e);
            error = cde;
            throw cde;
        }
    }

    @Override
    protected void execute() throws IOException {
        Client client = serverManager.getClient(clientToken, location);
        String clientAddress = client.getSocket().getRemoteSocketAddress().toString();
        String inMessage = client.peekInMessage(); //2
        if(inMessage != null && !inMessage.isEmpty() && 
                inMessage.startsWith(GameConditions.SERVER_MODE_HEARTBEAT)) {
            client.pollInMessage();
            String[] inMessageSplitArray = inMessage.split(
                    GameConditions.MESSAGE_FRAGMENT_SEPARATOR);
            String inToken = inMessageSplitArray[1];
            Matcher tokenMatcher = GameConditions.TOKEN_PATTERN.matcher(inToken);
        }
        String outMessage = GameConditions.SERVER_MODE_HEARTBEAT + 
            GameConditions.MESSAGE_FRAGMENT_SEPARATOR + clientToken;
        client.pushOutMessage(outMessage);
        long idle = (long) (System.currentTimeMillis() - client.getLastHeartBeat());
        if(idle > GameConditions.CLIENT_MAX_IDLE_TIME) {
            System.out.println("client idle time expired");
            serverManager.disconnectClient(clientToken);
            throw new IOException("client disconnected, throw to "
                + "close resources and stop thread");
        }
    }

}

新输入阅读器任务:

public class ClientInputListenerTask extends AbstractClientTask {

    private final String location = "input listener task execute";

    public ClientInputListenerTask(String clientToken, ServerManager serverManager, 
            ReadWriteLock clientTaskLock) {
        super(clientToken, serverManager, GameConditions.UPDATE_TASK, clientTaskLock);
    }

    @Override
    protected void execute() throws IOException {
        Client client = serverManager.getClient(clientToken, location);
        String inputLine = client.readLine();
        if(inputLine != null && !inputLine.isEmpty()) {
            client.pushInMessage(inputLine);
            System.out.println(client.getToken()+", in message: "+inputLine
                +", last heartbeat: "+client.getLastHeartBeat());
            serverManager.updateClient(client.getToken(), location);
        }
    }

    @Override
    public Boolean call() throws Exception {
        try {
            execute();
            return true;
        } catch(IOException e) {
            String exLocation = "input listener task execute ioex";
            ClientDisconnectedException cde = new ClientDisconnectedException(
                    clientToken, exLocation, e.getMessage(), e);
            error = cde;
            throw cde;
        }
    }
}

新的输出编写器任务:

public class ClientOutputPrinterTask extends AbstractClientTask {

    private final String location = "input listener task execute";

    public ClientOutputPrinterTask(String clientToken, ServerManager serverManager, 
            ReadWriteLock clientTaskLock) {
        super(clientToken, serverManager, GameConditions.UPDATE_TASK, clientTaskLock);
    }

    @Override
    protected void execute() throws IOException {
        Client client = serverManager.getClient(clientToken, location);
        String outputLine = client.pollOutMessage();
        if(outputLine != null)
            client.printLine(outputLine);
    }

    @Override
    public Boolean call() throws Exception {
        try {
            execute();
            return true;
        } catch(IOException e) {
            String exLocation = "input listener task execute ioex";
            ClientDisconnectedException cde = new ClientDisconnectedException(
                    clientToken, exLocation, e.getMessage(), e);
            error = cde;
            throw cde;
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.