所以这是从>[在C#中使用并行处理来测试站点承受DDOS 的能力
我用这篇MSKB文章作为我的例子的基础,只是我不想通过点击按钮来运行它,而是一个控制台脚本,它只是启动并启动"Attack"方法,该方法循环通过同一URL的X no,并在执行时返回。这个来自MS的示例只向Microsoft URLS发出了3个请求。
为了让它更像KB文章,我刚刚用控制台应用程序运行时调用的Main程序替换了按钮点击,该程序反过来调用Attack((,这是ONLY(目前只是试图获得3个URLS(。在实际的代码中,我有一个循环,但我需要让第一部分工作起来,这样我才能理解我做错了什么。
然而,当我在命令提示符下运行它时,我得到的只是…
C:UsersXXX>"C:UsersXXXDocumentsVisual Studio 2017ProjectsDOSBotDOSBot
binReleaseDOSBot.exe" "https://www.google.com" 1000
10/05/2020 00:00:00: starting script
然后它结束了,没有从我期望的HTTP请求返回消息。
控制台脚本的代码如下:
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.ComponentModel.DataAnnotations;
namespace AsyncExample_MultipleTasks
{
class Program
{
// Replaced the MS KB article example of hitting a button with just a Main constructor that runs from the console. I am passing arguments in at the moment URL and no of Requests to make but not currently using them as I want to understand why the MS KB example of making 3 requests is not working
public static void Main(string[] args)
{
if (args.Length > 0)
{
string url = args[0];
int no = Convert.ToInt32(args[1]);
ShowDebug("starting script");
// Attack DOS = new Attack(url,no);
Attack DOS = new Attack();
// Moved from the Attack() constructor but made no difference
DOS.StartAttack();
// The job just seems to end with no error message and DOS.StartAttack() seems to be skipped over
}
}
public static void ShowDebug(string msg)
{
string debugmsg = DateTime.Now.Date.ToString() + ": " + msg;
Console.WriteLine(debugmsg);
}
}
public class Attack
{
private int Counter = 0; // will hold no of actual HTTP requests completed
private string URL; // URL to hit
private int ReqNo; // No of HTTP request to make
public Attack()//string url, int reqNo=100)
{
this.URL = "http://www.google.com";// url;
this.ReqNo = 100; //reqNo;
//Tried calling this from here but now from the Main Program Constructor but makes no difference
//this.StartAttack();
}
// Tried calling this from the constructor above (commented out), and now from the main Program but neither do anything different
public async void StartAttack()
{
await CreateMultipleTasksAsync();
}
// This would be replaced by my loop with one URL to request and X no of times to request it.
private async Task CreateMultipleTasksAsync()
{
// Declare an HttpClient object, and increase the buffer size. The
// default buffer size is 65,536.
HttpClient client =
new HttpClient() { MaxResponseContentBufferSize = 1000000 };
// Create and start the tasks. As each task finishes, DisplayResults
// displays its length.
Task<int> download1 =
ProcessURLAsync("https://msdn.microsoft.com", client);
Task<int> download2 =
ProcessURLAsync("https://msdn.microsoft.com/library/hh156528(VS.110).aspx", client);
Task<int> download3 =
ProcessURLAsync("https://msdn.microsoft.com/library/67w7t67f.aspx", client);
// Await each task.
int length1 = await download1;
int length2 = await download2;
int length3 = await download3;
int total = length1 + length2 + length3;
// Display the total count for the downloaded websites.
Program.ShowDebug("rnrnTotal bytes returned: {total}rn");
}
async Task<int> ProcessURLAsync(string url, HttpClient client)
{
var byteArray = await client.GetByteArrayAsync(url);
DisplayResults(url, byteArray);
return byteArray.Length;
}
// Why is this not firing on return of the HTTP request?
private void DisplayResults(string url, byte[] content)
{
// Display the length of each website. The string format
// is designed to be used with a monospaced font, such as
// Lucida Console or Global Monospace.
var bytes = content.Length;
// Strip off the "https://".
var displayURL = url.Replace("https://", "");
Program.ShowDebug($"n{displayURL,-58} {bytes,8}");
}
}
}
所以DisplayResults方法根本没有被调用,从我所读到的内容来看,理论上,当HTTP响应返回时,它应该被触发,但我根本看不到它在运行。
我是不是错过了推荐信?我在Visual Studio 2017中的64位Windows笔记本电脑上使用.NET 4.6.1。
异步编程有几个重要的原则需要理解:
-
异步!=平行的您的代码中没有任何"并行"的内容。
- "并行"意味着两行代码同时执行。这只能通过多个线程来完成。这一切都是关于如何运行
- "异步"意味着当一段代码正在等待来自外部(网络请求、文件系统等(的回复时,您可以继续执行代码,而不是闲置地等待。这一切都是关于事情如何等待
-
由于您在等待的同时继续执行代码,因此需要某种方式来了解请求何时实际完成。这就是
Task
对象的作用。这就是为什么任何异步方法都应该返回一个Task
。如果它不返回Task
,您将永远无法知道它何时完成,或者它是否真的成功完成。 -
这就引出了
await
的作用。await
关键字作用于Task
对象。当await
作用于不完整的Task
时,方法返回。如果方法签名指示它应该返回Task
,那么它将返回Task
。如果方法签名是void
,则不会返回任何内容,但方法仍然返回。该方法的其余部分被注册为"continuation",这意味着它将在等待完成后运行(根据情况,它启动的线程何时可用(。
了解了所有这些,让我们来了解一下您的程序中发生了什么:
Main()
调用DOS.StartAttack()
- CCD_ 14运行并调用CCD_
CreateMultipleTasksAsync()
运行- 调用CCD_ 17。一旦发送了网络请求,就会返回一个不完整的
Task
。Task
对象将在收到回复时告诉您 - 对
ProcessURLAsync()
的另外两个调用也会发生同样的情况 - 在
await download1
处,因为download1
是不完整的Task
,所以CreateMultipleTasksAsync()
返回其自己的不完整Task
- 执行返回到
StartAttack()
await
关键字看到从CreateMultipleTasksAsync()
返回的不完整的Task
并返回。因为方法签名是void
,所以它不返回任何内容- 执行返回到
Main()
。由于没有其他可运行的程序,因此程序结束
这里的问题是,因为StartAttack()
就是void
,所以您无法知道异步请求何时完成。要解决此问题:
-
更改
StartAttack()
以返回Task
而不是void
。 -
Main()
中的await DOS.StartAttack()
-
将
Main()
的签名更改为:
public static async Task Main(string[] args)
也就是说,只要您使用的是C#7.1或更高版本。这时Main
方法可以开始返回Task
。
微软有一些写得很好的文章,介绍异步和等待异步编程。它们值得一读。这个链接只是第一篇文章。你会在那页左边的目录中找到其余的。