简体   繁体   English

使用C#异步服务器套接字的奇怪性能

[英]Odd performance with C# Asynchronous server socket

I'm working on a web server in C# and I have it running on Asynchronous socket calls. 我正在使用C#中的Web服务器,并且它在异步套接字调用上运行。 The weird thing is that for some reason, when you start loading pages, the 3rd request is where the browser won't connect. 奇怪的是,由于某种原因,当您开始加载页面时,第三个请求是浏览器无法连接的位置。 It just keeps saying "Connecting..." and doesn't ever stop. 它只是一直说“连接......”并且永远不会停止。 If I hit stop. 如果我点击停止。 and then refresh, it will load again, but if I try another time after that it does the thing where it doesn't load again. 然后刷新,它会再次加载,但是如果我再尝试一次,它就会再次加载它。 And it continues in that cycle. 它继续在那个循环中。 I'm not really sure what is making it do that. 我不确定是什么让它做到这一点。

The code is kind of hacked together from a couple of examples and some old code I had. 代码是从几个例子和我的一些旧代码一起被黑客攻击的。 Any miscellaneous tips would be helpful as well. 任何杂项提示也会有所帮助。

Heres my little Listener class that handles everything 继承我的小听众课程,处理一切

( pastied here . thought it might be easier to read this way) 在这里粘贴 。认为用这种方式阅读可能更容易)

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

And my SocketStateObject: 和我的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 ** **编辑**

I have updated the code with some suggestions from Chris Taylor. 我已经用克里斯泰勒的一些建议更新了代码。

Completely random guess: 完全随机猜测:

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

The maximum number of concurrent connections allowed by a ServicePoint object. ServicePoint对象允许的最大并发连接数。 The default value is 2. 默认值为2。

Just looking at the code quickly, I suspect that you might stop enquing your AsyncReads because s.Available is returning 0, I am refering to the following code 只是快速查看代码,我怀疑你可能会停止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; 
    } 
}

To confirm, change the above to the following 要确认,请将以上内容更改为以下内容

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

This is not the complete correction of the code, but it will confirm if this is the problem. 这不是代码的完整更正,但它将确认这是否是问题。 You need to make sure that you are closing your sockets correctly etc. 您需要确保正确关闭插座等。

Update I also noticed that you are sending the socket in as the state in the call to BeginSend. 更新我还注意到您正在将套接字作为调用BeginSend的状态发送。

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

However, your callback Send is casting the AsyncState to SocketStateObject 但是,您的回调SendAsyncState强制AsyncStateSocketStateObject

SocketStateObject state = (SocketStateObject)iar.AsyncState; 

This will be raising InvalidCastExceptions which you are just hiding by adding the empty catch . 这将通过添加空catch来提高您隐藏的InvalidCastExceptions I am sure others will agree, this is exceptionally bad practice having empty catches it hides so much info that you could be using to debug your problem. 我相信其他人会同意,这是非常糟糕的做法,空的捕获它隐藏了很多信息,你可以用来调试你的问题。

You should also note that there is a race condition in your code. 您还应注意代码中存在竞争条件。 In Run(), you wait for allDone before calling BeginAccept again: 在Run()中,在再次调用BeginAccept之前等待allDone

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

This is fine, however in your AcceptConn callback, the event is set at the top of the method: 这很好,但是在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);
}

The callback will executed by a random thread from the pool, but allDone will be set before anything is actually done. 回调将由池中的随机线程执行,但allDone将在实际完成任何操作之前设置。 It's entirely possible for your Run() loop to run again in the first thread before the work in AcceptCon actually completes. 在AcceptCon中的工作实际完成之前,您的Run()循环完全可以在第一个线程中再次运行。 This will cause you big problems. 这会给你带来很大的问题。

You should set allDone after you've performed your initialization (and especially after you've accessed any non-threadsafe class members), like so: 您应该在执行初始化之后设置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);

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM