在多线程控制台应用程序中进行Web爬网

时间:2014-11-12 13:43:43

标签: c# multithreading .net-4.0 console-application

多线程应用的新手。

我正在尝试创建一个控制台应用程序来检查给定的IP地址列表(Intranet)。任何给定IP地址的每个网页都包含一些显示在html表格中的统计信息,我需要收集这些统计信息。 我可以在一个线程中执行此操作:设置请求/响应序列,获取页面内容并解析它。

我现在正在努力的是使这个多线程,因为我必须处理4000个IP地址和单线程需要一些时间。我有列表或字符串数​​组中的IP列表;你知道如何设置线程吗?

假设我有一个处理响应的函数,比如说," ProcessResponse(字符串s)",并且想要从10个线程开始,我可以从以下内容开始:

public class PASSServer
{
    private string _ip;
    public string IPAddress
    {
        get;
        set;
    }

    public PASSServer()
    {
    }
}
static void Main(string[] args)
{
        int iNumThreads = 3;
        Thread[] threads = new Thread[iNumThreads];

        string[] sIPs = { "192.168.10.20", "192.168.10.21", "192.168.10.22" };

        for (int i = 0; i < threads.Length; i++)
        {
            ParameterizedThreadStart start = new ParameterizedThreadStart(Start);
            threads[i] = new Thread(start);
            PASSServer pserver = new PASSServer();
            pserver.IPAddress = sIPs[i];
            threads[i].Start(pserver);
        }
        Console.WriteLine("DONE");
        Console.ReadKey();
    }
    static void Start(object info)
    {
        PASSServer pserver = (PASSServer)info;
        crawl(pserver.IPAddress);
    }
    private static void crawl(string sUrl)
    {
        PASSData cData = new PASSData();
        string sRequestUrl = "http://" + sUrl.Trim() + "/cgi-bin/sysstat?";
        string sEncodingType = "utf-8";
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sRequestUrl);
        request.KeepAlive = true;
        request.Timeout = 15 * 1000;

        System.Net.HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        string sStatus = ((HttpWebResponse)response).StatusDescription;
        sEncodingType = GetEncodingType(response);
        System.IO.StreamReader reader = new System.IO.StreamReader(response.GetResponseStream(), Encoding.GetEncoding(sEncodingType));

        // Read the content.
        string responseFromServer = reader.ReadToEnd();
        Console.WriteLine(responseFromServer);
    }

非常感谢任何帮助。

我没有使用多线程,但搜索了主题并得到了一些想法,只是不确定如何最好地设置我的场景。

2 个答案:

答案 0 :(得分:2)

不要使用线程。使用异步HTTP请求。例如,使用HttpWebRequest.BeginGetResponseHttpWebRequest.GetResponseAsync。使用Semaphore限制并发请求的数量。

所以,如果你有一个URL列表(一个List<string>),并且你想要最多10个并发请求:

List<string> _urls = GetListOfUrls();
Semaphore _requestSemaphore = new Semaphore(10, 10);

foreach (var url in _urls)
{
    // wait for an available spot
    _requestSemaphore.WaitOne();
    // Now start an asynchronous request with this url
    var request = (HttpWebRequest)WebRequest.Create(url);
    request.BeginGetResponse(GetResponseCallback, request);
}

当您的清单为空时,您必须等待收到最终答复。你这样做的方法是等待信号量10次。如果你有10个,那么就不会有任何未完成的请求:

for (int i = 0; i < 10; ++i)
{
    _requestSemaphore.WaitOne();
}

您的回调,在收到回复时调用:

void GetResponseCallback(IAsyncResult ar)
{
    var request = (HttpWebRequest)ar.AsyncState;
    var response = (HttpWebResponse)request.EndGetResponse(ar);
    // process the response here.
    // when you're done processing the response, release the semaphore
    _requestSemaphore.Release();
}

答案 1 :(得分:0)

我会遍历您的IP地址列表并启动ThreadPool工作项。

foreach(string addr in IpAddresses)
   Threading.ThreadPool.QueueUserWorkItem(
      (string ipaddr) => 
      { 
            ResponseFromQuery resp = new ResponseFromQuery(); 
            this.BeginInvoke(new MethodInvoker(() => { UpdateTable(resp); }));
      }, addr);

*编辑:在上面,你需要调用BeginInvoke并创建一个methodinvoker,它在你的应用程序调用UpdateTable中回调一个新方法。您可以传递您的响应信息(无论它是什么类型,我都使用了一个组成的ResponseFromQuery类)。

您可以使用匿名函数,或者如果有很多代码并且您可以在其他地方使用它,您可以创建一个处理类和方法,您可以将其作为要执行的方法传递。

如果您想自己管理线程,可以创建一个Dictionary或List对象,并为集合中的每个项目添加一个线程:

Dictionary<string, Thread> _threads = new Dictionary<string, Thread>();

foreach (string addr in IpAddresses)
{
    _threads.Add(addr, new System.Threading.Thread(
        new System.Threading.ParameterizedThreadStart(
            (object ip) =>
            {
                // process ip. 
            }, addr)));
    _threads[addr].Start();
}