2017-02-06 2 views
0

У меня есть несколько классов, которые «связаны» через различные события. В основном a TCPListener, ClientSession и Server.Поднятие события в задаче приводит к сбою метода подписчика

TCPListener пузыри новые соединения вплоть до Server и затем Server создает новый ClientSession.

TCPListener передает новый разъем через событие, ClientConnected.

Метод повышения события выглядит следующим образом:

//Processes a new connection and immediately puts the listen socket back in a receiving state 
    private void ProcessAccept(SocketAsyncEventArgs e) 
    { 
     ClientConnected(this, e));    

     StartAccept(); 
    } 

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

Вот пересмотренный метод:

//Processes a new connection and immediately puts the listen socket back in a receiving state 
    private void ProcessAccept(SocketAsyncEventArgs e) 
    { 
     Task.Run(() => ClientConnected(this, e));    

     StartAccept(); 
    } 

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

Вот OnClientConnected метод Server:

/// <summary> 
    /// An event that is fired when a client connects. 
    /// </summary> 
    /// <param name="sender">The Listener that accepted the connection</param> 
    /// <param name="e">The SocketAsyncEventArgs </param> 
    protected virtual void OnClientConnected(object sender, EventArgs e) 
    { 
     ClientSession Session = ClientSessionPool.Pop(); 

     Session.Socket = ((SocketAsyncEventArgs)e).AcceptSocket; 

     string WelcomeMessage = "Connected"; 

     Session.SendAsync(Encoding.Default.GetBytes(WelcomeMessage)); 

     this.ClientSessions.Add(Session); 

     Console.Write($"\rConnected clients:{ClientSessions.Count}"); 
    } 

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

Что давало мне покоя, что SocketAsyncEventArgs, который передается через событие кажется правильным, независимо от использования Task или нет, и что это единственная точка сбоя я могу думать.

SocketAsyncEventArgs представляется совершенно неправильным при использовании версии метода Task.

Я подозреваю, что это мое понимание стека, выделенного для нового потока, вызывающего мое замешательство. Может ли кто-нибудь увидеть дыру в моей логике? Благодаря!

PS

Я знаю, что моя текущая реализация в ClientSessions списка НЕ ​​поточно, но в моем тестировании, я только когда-либо соединяться с одним клиентом одновременно. I будет в конечном итоге исправить это.

PPS Вот StartAccept метод в случае полезно:

//Puts the accepting TCP socket back into an accepting state 
    public void StartAccept() 
    { 
     // socket must be cleared since the context object is being reused 
     m_SocketEventArgs.AcceptSocket = null; 

     bool willRaiseEvent = m_Socket.AcceptAsync(m_SocketEventArgs); 
     if (!willRaiseEvent) 
     { 
      ProcessAccept(m_SocketEventArgs); 
     } 
    } 
+0

Вы не просто участвуете в гонке с StartAccept? – dlatikay

+0

@dlatikay Не думайте так.'StartAccept' не затрагивает ничего, что' ClientConnected' делает asides из 'SocketAsyncEventArgs', но даже копируется в новый стек' Task' (я думаю). Я добавлю «StartAccept» на вопрос, дайте мне знать. –

+1

@dlatikay OH SNAP! Я вижу это сейчас ... 'm_SocketEventArgs.AcceptSocket = null;' ... Вот и все, не так ли? –

ответ

2

О решении было бы поднять событие в потоке пользовательского интерфейса, но запустить обработку событий асинхронно.

private async void OnClientConnected(object sender, EventArgs e) 
{ 
    await Task.Run(() => HandleClientConnected(object, e)); 
} 

protected abstract Task HandleClientConnected(object sender, EventArgs e); 
+0

Не уверен, что консольная программа по-прежнему имеет поток «UI». Я не совсем понимаю, чего это достигнет ... Разве это не будет блокироваться до тех пор, пока «Задача» не будет закончена, по существу, делая этот метод синхронным и непараллельным? –

+0

@ Connel.O'Donnell Я думаю, что вижу. Он просто ждет * создания * задачи и * не * ее выполнения? –

+0

Точно. Или вы можете просто обработать обработчик как команду огня и забывания, не ожидая этого. –