Последовательный доступ к асинхронным сокетам - PullRequest
1 голос
/ 11 июня 2010

У меня есть сервер с несколькими клиентами C1 ... Cn, к каждому из которых установлено TCP-соединение.Количество клиентов составляет менее 10 000.

Протокол сообщений основан на запросе / ответе, когда сервер отправляет запрос клиенту, а затем клиент отправляет ответ.

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

Я делаюне хочет блокировать потоки от одновременной отправки запросов различным клиентам.

Например, если T1 отправляет запрос на C3, другой поток T2 не должен иметь возможность отправлять что-либо на C3, пока T1 не получит свой ответ.

Я думал об использовании простого оператора блокировки для сокета:

lock (c3Socket)
{
    // Send request to C3
    // Get response from C3
}

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

Monitor.Enter(c3Socket); // Before calling .BeginReceive()

И

Monitor.Exit(c3Socket); // In .EndReceive

Я беспокоюсь о том, что что-то пошло не так и не отпустит монитор и, следовательно, заблокирует весь доступ к клиенту.Я думаю, что мой поток сердцебиения может использовать Monitor.TryEnter () с таймаутом и выбрасывать сокеты, для которых он не может получить монитор.

Имеет ли смысл делать синхронные вызовы Begin и End синхроннымидля того, чтобы иметь возможность использовать оператор lock ()?Я знаю, что в этом случае я бы пожертвовал параллелизмом ради простоты, но, возможно, оно того стоит.

Я что-то здесь упускаю?Любой вклад приветствуется.

Ответы [ 2 ]

2 голосов
/ 11 июня 2010

Вы, конечно, не можете использовать подход блокировки, который вы описали.Поскольку ваша система в основном асинхронная, вы не можете знать, какие операции с потоками будут выполняться.Это означает, что вы можете вызвать Exit в неправильном потоке (и вызвать исключение SynchronizationLockException), или какой-то другой поток может вызвать Enter и выполнить его успешно, даже если этот клиент «используется», просто потому, что он получил тот же поток, что был Enterпервоначально вызвано.

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

2 голосов
/ 11 июня 2010

Мой ответ здесь будет конечный автомат на сокет. Состояния будут free и busy:

  • Если сокет равен free, поток отправителя пометит его как busy и начнет отправлять клиенту и ожидает ответа.
  • Возможно, вы захотите установить тайм-аут на это ожидание на случай, если клиент застрянет каким-либо образом.
  • Если состояние busy - поток спит, ожидая сигнала.
  • Когда истекает тайм-аут, связанный с клиентом - закройте сокет, клиент не работает.
  • Когда ответ успешно получен / проанализирован, снова отметьте сокет free и подайте сигнал / разбудите ожидающие потоки.
  • Только блокировка вокруг запроса состояния сокета и манипуляции, а не фактического сетевого ввода-вывода. Это означает блокировку на сокет плюс некоторый примитив ожидания, например условные переменные (извините, не помню, что действительно доступно в .NET)

Надеюсь, это поможет.

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