Позвольте мне показать вам, что я считаю еще лучшим способом многообразий. Если вы хотите, чтобы Action
s были подключаемыми, и пусть кто-нибудь их добавляет, часто бывает полезно предоставить простой Module
для кого-то, кто будет использовать эту шкуру, нуждающуюся в создании экземпляра Multibinder
. Вот пример:
public abstract class ActionModule extends AbstractModule {
private Multibinder<Action> actionBinder;
@Override protected void configure() {
actionBinder = Multibinder.newSetBinder(binder(), Action.class);
configureActions();
}
/**
* Override this method to call {@link #bindAction}.
*/
protected abstract void configureActions();
protected final LinkedBindingBuilder<Action> bindAction() {
return actionBinder.addBinding();
}
}
Теперь почему это лучше? Это позволяет кому-то использовать ActionModule
из любого места, чтобы добавить больше Action
s через стандартный API привязки. Я думаю, что это более читаемо. Вот пример использования:
public final class MyStandardActionModule extends ActionModule() {
@Override protected void configureActions() {
bindAction().to(FooAction.class);
bindAction().to(BarAction.class);
// If you need to instantiate an action through a Provider, do this.
bindAction().toProvider(BazActionProvider.class);
// You can also scope stuff:
bindAction().to(MySingletonAction.class).in(Singleton.class);
}
}
Этот шаблон использования Module
, чтобы скрыть многострочный бит, используется в коде Guice. Это немного работает впереди, но держит вещи в чистоте. Вы также можете сделать что-то подобное для MapBinder
, если вам нужно. Имейте в виду, что вы можете создать столько ActionModule
, сколько хотите.
Существует 2 предложения, которые я сделал бы, учитывая код и что я предполагаю, Ваш дизайн. Однако я хотел бы указать сначала, что необходимо действительно использовать неблокирующиеся обратные вызовы ввода-вывода при работе с вводом-выводом как сетевые или файловые системы. Это далеко , более эффективный FAR и Ваше приложение будет работать намного лучше, хотя они более тверды к программе. Я кратко покрою предложенную модификацию дизайна в конце.
***, что необходимо действительно включить вызов TcpClient в использование () {} блок для обеспечения того TcpClient. Расположите () или TcpClient. Близко () методы называют даже в случае исключения. Поочередно можно вставить это наконец блок попытки {} наконец {} блок.
существует 2 вещи, я вижу, что Вы могли сделать. 1 то, что при запуске этого потока TcpListener от другого можно просто назвать Поток. Прервите метод экземпляра для потока, который будет заставлять threadabortexception быть брошенным в блокирующемся вызове и идти по стеку.
вторая недорогая фиксация должна была бы использовать слушателя. Ожидание () метод для реализации модели опроса. Вы тогда использовали бы Поток. Сон для "ожидания" прежде, чем видеть, находится ли новое соединение на рассмотрении. Как только у Вас есть незаконченное соединение, которое Вы назвали бы AcceptTcpClient, и это выпустит незаконченное соединение. Код выглядел бы примерно так.
while (listen){
// Step 0: Client connection
if (!listener.Pending())
{
Thread.Sleep(500); // choose a number (in milliseconds) that makes sense
continue; // skip to next iteration of loop
}
TcpClient client = listener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleConnection));
clientThread.Start(client.GetStream());
client.Close();
}
Наконец я рекомендовал бы действительно переместиться в не блокирующуюся методологию для приложения. Под покрытиями платформа будет использовать Перекрытый ввод-вывод и порты завершения ввода-вывода для реализации не блокирующегося ввода-вывода от асинхронных вызовов. Это не ужасно трудно также, это просто требует взглядов о Вашем коде немного по-другому.
В основном Вы запустили бы свой код с метода BeginAcceptTcpClient и отслеживали бы IAsyncResult, что Вы возвращаетесь. Вы указываете на это на метод чей ответственный за получение TcpClient и передачу его прочь НЕ к новому потоку, но к потоку прочь ThreadPool. QueueUserWorkerItem, таким образом, Вы не вращаетесь и закрываете новое обсуждение для каждого клиентского запроса (Отмечают Вас, возможно, должен использовать Ваш собственный пул потоков, если у Вас есть особенно долговечные запросы, потому что пул потоков совместно используется и если Вы монополизируете все потоки, другие части Вашего приложения, реализованного системой, могут быть исчерпаны ресурсы). Как только метод прослушивателя начал Ваш новый TcpClient к своему собственному запросу ThreadPool, который это называет BeginAcceptTcpClient снова и указывает на делегата назад на себя.
Эффективно Вы просто разбиваете свой существующий метод в 3 различных метода, которые тогда назовут различные части. 1. загружать все, 2. чтобы быть целью для вызова EndAcceptTcpClient начните TcpClient к своему собственному потоку и затем назовите себя снова, 3. обработать клиентский запрос и закрыть его по окончании.
Не используйте цикл. Вместо этого назовите BeginAcceptTcpClient () без цикла. В обратном вызове просто издайте другой приказ к BeginAcceptTcpClient (), если Ваш слушать флаг все еще установлен.
Для остановки слушателя, так как Вы не заблокировались, Ваш код может просто звонить Близко () на нем.
Сокеты обеспечивают мощные асинхронные возможности. Смотрите на Используя Асинхронный Сокет Сервера
, Вот несколько примечаний по коду.
Используя вручную созданные потоки в этом случае могут быть издержки.
код ниже подвергается условиям состязания - TcpClient. Близко () закрывает сетевой поток, Вы проходите через TcpClient. GetStream (). Рассмотрите заключительный клиент, где можно определенно сказать, что это больше не необходимо.
clientThread.Start(client.GetStream());
client.Close();
TcpClient. Остановите () завершения, лежащие в основе сокета. TcpCliet. AcceptTcpClient () использует Сокет. Примите () метод на базовом сокете, который бросит SocketException, как только это закрывается. Можно назвать его от различного потока.
Так или иначе я рекомендую асинхронные сокеты.
Вероятно, лучше всего использовать асинхронное функция BeginAcceptTcpClient. Тогда можно просто назвать Остановку () на слушателе, поскольку она не будет блокироваться.
Только для добавления еще большей причины использовать асинхронный подход я - вполне уверенный Поток. Аварийное прекращение работы не будет работать, потому что вызов заблокирован в стеке TCP уровня ОС.
Также..., если Вы называете BeginAcceptTCPClient в обратном вызове для прислушиваний к каждому соединению, но первое, стараться удостовериться, что поток, который выполнил начальный BeginAccept, не завершается или иначе слушатель, будет автоматически расположено платформой. Я предполагаю, что это - функция, но на практике это является очень раздражающим. В настольных приложениях это обычно не проблема, но в сети Вы могли бы хотеть использовать пул потоков, так как те потоки действительно никогда не завершаются.
Некоторые изменения, чтобы сделать ответ Питера Олерта идеальным. Потому что до 500 миллисекунд слушатель снова блокируется. Чтобы исправить это:
while (listen)
{
// Step 0: Client connection
if (!listener.Pending())
{
Thread.Sleep(500); // choose a number (in milliseconds) that makes sense
continue; // skip to next iteration of loop
}
else // Enter here only if have pending clients
{
TcpClient client = listener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleConnection));
clientThread.Start(client.GetStream());
client.Close();
}
}
listener.Server.Close ()
из другого потока прерывает блокирующий вызов.
A blocking operation was interrupted by a call to WSACancelBlockingCall