2010-05-08 3 views
3

Я работаю над веб-сервером на C#, и у меня он работает на асинхронных вызовах сокетов. Странно то, что по какой-то причине, когда вы начинаете загружать страницы, третий запрос - это то, где браузер не будет подключаться. Он просто продолжает говорить «Подключение ...» и никогда не останавливается. Если я удалю стоп. а затем обновится, он снова загрузится, но если я попробую еще раз после этого, он сделает то, на что он больше не загружается. И он продолжается в этом цикле. Я не совсем уверен, что заставляет это делать.Нечетная производительность с C# Асинхронный серверный сокет

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

Heres мой маленький класс Слушатель, который обрабатывает все

(. pastied here подумал, что может быть проще читать этот путь)

using System; 
using System.Collections.Generic; 
using System.Net; 
using System.Net.Sockets; 
using System.Text; 
using System.Threading; 
using irek.Request; 
using irek.Configuration; 
namespace irek.Server 
{ 
    public class Listener 
    { 
     private int port; 
     private Socket server; 
     private Byte[] data = new Byte[2048]; 
     static ManualResetEvent allDone = new ManualResetEvent(false); 
     public Config config; 

     public Listener(Config cfg) 
     { 
      port = int.Parse(cfg.Get("port")); 
      config = cfg; 
      ServicePointManager.DefaultConnectionLimit = 20; 
     } 

     public void Run() 
     { 
      server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
      IPEndPoint iep = new IPEndPoint(IPAddress.Any, port); 
      server.Bind(iep); 

      Console.WriteLine("Server Initialized."); 
      server.Listen(5); 
      Console.WriteLine("Listening..."); 
      while (true) 
      { 
       allDone.Reset(); 
       server.BeginAccept(new AsyncCallback(AcceptCon), server); 
       allDone.WaitOne(); 
      } 

     } 

     private void AcceptCon(IAsyncResult iar) 
     { 
      allDone.Set(); 
      Socket s = (Socket)iar.AsyncState; 
      Socket s2 = s.EndAccept(iar); 
      SocketStateObject state = new SocketStateObject(); 
      state.workSocket = s2; 
      s2.BeginReceive(state.buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(Read), state); 
     } 

     private void Read(IAsyncResult iar) 
     { 
      try 
      { 
       SocketStateObject state = (SocketStateObject)iar.AsyncState; 
       Socket s = state.workSocket; 

       int read = s.EndReceive(iar); 

       if (read > 0) 
       { 
        state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, read)); 

        SocketStateObject nextState = new SocketStateObject(); 
        nextState.workSocket = s; 
        s.BeginReceive(state.buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(Read), nextState); 
       } 
       if (state.sb.Length > 1) 
       { 
        string requestString = state.sb.ToString(); 
        // HANDLE REQUEST HERE 
        byte[] answer = RequestHandler.Handle(requestString, ref config); 
        // Temporary response 
        /* 
        string resp = "<h1>It Works!</h1>"; 
        string head = "HTTP/1.1 200 OK\r\nContent-Type: text/html;\r\nServer: irek\r\nContent-Length:"+resp.Length+"\r\n\r\n"; 
        byte[] answer = Encoding.ASCII.GetBytes(head+resp); 
        // end temp. 
        */ 
        state.workSocket.BeginSend(answer, 0, answer.Length, SocketFlags.None, new AsyncCallback(Send), s); 
       } 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine(e.Message); 
       Console.WriteLine(e.StackTrace); 
       return; 
      } 
     } 

     private void Send(IAsyncResult iar) 
     { 
      try 
      { 
       SocketStateObject state = (SocketStateObject)iar.AsyncState; 
       int sent = state.workSocket.EndSend(iar); 
       state.workSocket.Shutdown(SocketShutdown.Both); 
       state.workSocket.Close(); 
      } 
      catch (Exception) 
      { 

      } 
      return; 
     } 
    } 
} 

И мой SocketStateObject:

public class SocketStateObject 
{ 
    public Socket workSocket = null; 
    public const int BUFFER_SIZE = 1024; 
    public byte[] buffer = new byte[BUFFER_SIZE]; 
    public StringBuilder sb = new StringBuilder(); 
} 

** EDIT **

Я обновил код с помощью s ome предложения от Криса Тейлора.

+0

Проверьте свой ответ на дополнительные обновления. Я заметил, что у вас также есть несоответствие типа объекта в группе BeginSend/Send. –

ответ

1

Просто глядя на код быстро, я подозреваю, что вы можете остановить enquing ваши AsyncReads потому что s.Available возвращается 0, я ссылаюсь на следующий код

if (read > 0) 
{ 
    state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, read)); 
    if (s.Available > 0) 
    { 
     s.BeginReceive(state.buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(Read), state); 
     return; 
    } 
} 

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

if (read > 0) 
{ 
    state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, read)); 

    SocketStateObject nextState = new SocketStateObject(); 
    nextState.workSocket = s; 
    s.BeginReceive(state.buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(Read), nextState); 
} 

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

Update Я также заметил, что вы отправляете сокет в качестве государства в вызове BeginSend.

state.workSocket.BeginSend(answer, 0, answer.Length, SocketFlags.None, new AsyncCallback(Send), state.workSocket); 

Однако ваш обратный вызов Send бросает в AsyncState на SocketStateObject

SocketStateObject state = (SocketStateObject)iar.AsyncState; 

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

+0

Нет, к сожалению, он не изменился. Поэтому я не думаю, что это тоже моя проблема. Но это может помешать другим ошибкам: P –

+0

@ The.Anti.9, это странно. Я просто скопировал ваш оригинальный код и смог реплицировать вашу проблему. Затем я добавил изменение, которое я предложил, и с этой точки проблемы, когда прочь. Может быть, вы можете опубликовать обновленный код? –

+0

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

1

Полностью случайное предположение:

http://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.defaultconnectionlimit.aspx

Максимальное количество одновременных соединений, разрешенных посредством ServicePoint объекта. Значение по умолчанию равно 2.

+0

Это была хорошая мысль, но не она. В любом случае соединения, которые я пытаюсь, не являются параллельными. Они раздельны, один за другим. Хотя я действительно думал, что по какой-то причине соединения не закрывались, поэтому это имело смысл. Но, увы, не так:/ –

0

Вы также должны отметить, что в вашем коде есть состояние гонки.В Run(), вы ждете allDone перед повторным вызовом BeginAccept:

while (true) 
{ 
    allDone.Reset(); 
    server.BeginAccept(new AsyncCallback(AcceptCon), server); 
    allDone.WaitOne(); // <------ 
} 

Это прекрасно, однако в вашем AcceptConn обратного вызова, событие устанавливается в верхней части метода:

private void AcceptCon(IAsyncResult iar) 
{ 
    allDone.Set(); // <------ 

    Socket s = (Socket)iar.AsyncState; 
    Socket s2 = s.EndAccept(iar); 
    SocketStateObject state = new SocketStateObject(); 
    state.workSocket = s2; 
    s2.BeginReceive(state.buffer, 0, SocketStateObject.BUFFER_SIZE, 0, 
     new AsyncCallback(Read), state); 
} 

Обратный вызов будет выполняться случайным потоком из пула, но allDone будет установлен до того, как что-либо действительно будет выполнено. Вполне возможно, что цикл Run() снова запустится в первом потоке до завершения работы в AcceptCon. Это вызовет большие проблемы.

Вы должны установить allDone после того, как выполняется инициализация (и особенно после того, как вы обращались любой не потоки членами класса), например так:

private void AcceptCon(IAsyncResult iar) 
{ 

    Socket s = (Socket)iar.AsyncState; 
    Socket s2 = s.EndAccept(iar); 
    SocketStateObject state = new SocketStateObject(); 
    state.workSocket = s2; 

    allDone.Set(); // <------ 

    s2.BeginReceive(state.buffer, 0, SocketStateObject.BUFFER_SIZE, 0, 
     new AsyncCallback(Read), state); 

} 
Смежные вопросы