代码之家  ›  专栏  ›  技术社区  ›  The.Anti.9

使用C异步服务器套接字时性能异常

  •  3
  • The.Anti.9  · 技术社区  · 15 年前

    我在C中的一个Web服务器上工作,我让它在异步套接字调用上运行。奇怪的是,由于某种原因,当您开始加载页面时,第三个请求是浏览器无法连接的地方。它一直在说“连接……”,而且永远不会停止。如果我按停止键。然后刷新,它将再次加载,但如果我在这之后再试一次,它将在不再加载的地方执行操作。它在这个循环中继续。我真的不知道是什么让它这么做的。

    代码有点像是从几个例子和我以前的一些代码中拼凑出来的。任何其他的提示也会有帮助。

    这是我的小听众类,它处理一切

    ( 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();
    }
    

    **编辑**

    我已经用ChrisTaylor的一些建议更新了代码。

    3 回复  |  直到 15 年前
        1
  •  1
  •   Chris Taylor    15 年前

    只需快速查看代码,我怀疑您可能会停止异步读取,因为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);
    }
    

    这不是代码的完整更正,但它将确认这是否是问题所在。您需要确保正确关闭插座等。

    更新 我还注意到您正在将套接字作为调用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 . 我相信其他人也会同意,这是一个非常糟糕的做法,空捕获隐藏了太多的信息,您可以使用这些信息来调试您的问题。

        2
  •  1
  •   Brian    15 年前

    完全随机猜测:

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

    最大并发数 服务点允许的连接 对象。默认值为2。

        3
  •  0
  •   jscharf    14 年前

    您还应该注意,您的代码中存在竞争条件。在run()中,您等待 全部完成 再次调用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将在实际执行任何操作之前设置。在acceptcon中的工作实际完成之前,run()循环完全可以在第一个线程中再次运行。这会给你带来大麻烦。

    在执行初始化之后(尤其是在访问任何非线程安全类成员之后),应该设置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);
    
    }