Опрос серверов на одном порту - Threads и Java - PullRequest
1 голос
/ 29 мая 2010

В настоящее время я занят работой над инструментом запрета IP для ранних версий Call of Duty 1. (Очевидно, такая функция не была реализована в этих версиях).

Я закончил однопоточное приложение, но оно не будет работать достаточно хорошо для нескольких серверов, поэтому я пытаюсь реализовать многопоточность.

Прямо сейчас каждый сервер имеет свой собственный поток. У меня есть класс Networking, у которого есть метод; «GetStatus» - этот метод синхронизирован. Этот метод использует DatagramSocket для связи с сервером. Поскольку этот метод является статическим и синхронизированным, я не должен попадать в неприятности и получать целую кучу исключений «Адрес уже используется».

Однако у меня есть второй метод с именем «SendMessage». Этот метод должен отправить сообщение на сервер. Как я могу убедиться, что SendMessage не может быть вызван, если в GetStatus уже есть поток, и наоборот? Если я сделаю оба синхронизированных, у меня все еще будут проблемы, если Поток A открывает сокет на порту 99999 и вызывает «SendMessage», в то время как Поток B открывает сокет на том же порту и вызывает «GetStatus»? (Игровые серверы обычно размещаются на одних и тех же портах)

Полагаю, что на самом деле я ищу способ синхронизировать весь класс, так что один поток может одновременно вызывать и запускать только один метод.

Надеюсь, что то, что я пытаюсь достичь / избежать, разъясняется в этом тексте.

Любая помощь очень ценится.

Ответы [ 3 ]

2 голосов
/ 29 мая 2010

Прямо сейчас каждый сервер имеет свой собственный поток.

Зачем вам два сервера в одном приложении?!?Если вы разделите оба сервера на отдельные приложения, у вас все равно возникнет одна и та же проблема, если они оба попытаются использовать один и тот же порт ... т.е. вам нужно будет назначить порт для каждого сервера.Если это действительно проблема с многопоточностью, прочитайте ниже идеи, как это исправить.

Невозможно, чтобы два потока выполнили правильно синхронизированных методов одного и того же класса ...если у вас правильная синхронизация , то нет способа испытать то, что вы описываете.Вот как должен выглядеть ваш класс:

class Networking 
{
    public synchronized Status getStatus() {
        Status stat =  new Status();
        // ...
        // Get status logic
        // ...
        return stat;// return the status
    }

    public synchronized void sendMessage(Message msg) {
        // ...
        // Send the message logic
        // ...
    }
}

Так что, пока вы вызываете эти методы в одном и том же Networking экземпляре (т.е. у вас нет отдельногоэкземпляр класса Networking для каждого потока), то вы не должны видеть никаких проблем.Вот что делает для вас ключевое слово synchronized:

Во-первых, невозможно выполнить два вызова синхронизированных методов одного и того же объекта для чередования.Когда один поток выполняет синхронизированный метод для объекта, все другие потоки, которые вызывают синхронизированные методы для того же блока объекта (приостанавливают выполнение), пока первый поток не завершится с объектом.

Во-вторых, при выходе из синхронизированного метода он автоматически устанавливает отношение «до и после» с любым последующим вызовом синхронизированного метода для того же объекта.Это гарантирует, что изменения состояния объекта видны всем потокам. (ref)

Если вы хотите синхронизировать методы во всех экземплярах класса Networking, вам нужно использовать операторы синхронизации:

class Networking 
{
    private static final Object lock = new Object();

    public synchronized Status getStatus() {
        synchronized(lock){
            Status stat =  new Status();
            // ...
            // Get status logic
            // ...
            return stat;// return the status
        }
    }

    public synchronized void sendMessage(Message msg) {
        synchronized(lock){
            // ...
            // Send the message logic
            // ...
        }
    }
}
2 голосов
/ 30 мая 2010

будет ли [I] по-прежнему иметь проблемы, если Поток A открывает сокет на порту 99999 и вызывает «SendMessage», в то время как Поток B открывает сокет на том же порту и вызывает «GetStatus»?

Здесь есть два отдельных вопроса.(за исключением того факта, что 99999 не является действительным портом #)

UDP по своей природе предназначен для мультиплексной связи типа один ко многим.Вы можете открыть один сокет и использовать этот сокет для связи с любым количеством серверов.Вам не нужно беспокоиться о синхронизации в том смысле, что один поток отправляет, а другой получает один и тот же сокет или два потока пытаются отправить одновременно, потому что операции чтения и записи в сокет являются атомарными с точки зрения приложений.Когда вы отправляете через сокет UDP, вы вызываете системный вызов, который копирует N байтов данных из пространства памяти приложения в буфер в пространстве памяти ядра ОС, и ядро ​​собирает эти данные в пакет UDP, который помещается в очередь.для отправки - все в манере, которая выглядит атомарно для приложения.То же самое происходит при чтении из сокета UDP, за исключением обратного;в буфере приема в ядре существуют отдельные пакеты, и когда ваше приложение читает сокет, данные из этих пакетов атомарно копируются из буфера ядра в буфер вашего приложения, один пакет на операцию чтения.

Вторая проблема заключается в том, чтоуправление входящими и исходящими данными для конкретных серверов.Похоже, вы хотите иметь один поток на сервер, который поддерживает состояние / статус этого сервера.При отправке вам не нужно беспокоиться о синхронизации вообще.Все потоки могут отправлять из одного и того же сокета, и синхронизация эффективно обрабатывается ядром ОС.

Однако получение - это совсем другая проблема.Я бы порекомендовал иметь один поток, единственной задачей которого является чтение входящих пакетов из сокета и демультиплексирование их.Каждый серверный поток будет иметь потокобезопасную очередь, в которую поток считывателя будет копировать входящие пакеты.Тогда каждому серверному потоку не нужно беспокоиться ни о чем, кроме чтения пакетов из собственной очереди входящих пакетов - ему вообще не нужно заниматься чтением из сокета.

0 голосов
/ 29 мая 2010

Я думаю, вы, возможно, неправильно понимаете, как работают сокеты, и путаете стороны сокетов клиента и сервера. Если вы отправляете сообщение, это обычно делается из клиентского сокета. Они не привязаны к статическому номеру порта - это сокет сервера (тот, который вы называете accept() на), который привязан к определенному порту.

Вы можете иметь столько клиентов, сколько вам нужно (до некоторого разумного предела - максимально 60 000 клиентских подключений) с любого одного сетевого интерфейса.

Для ознакомления с сокетами на стороне клиента и сервера см. Урок Sun: Все о сокетах

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...