所以这是来自> [Using Parallel Processing in C# to test a site's ability to withstand a DDOS
的随身物品我使用此MS KB文章作为示例的基础,只是我不想通过单击按钮运行它,而只是启动并随后触发“攻击”方法的控制台脚本,该方法循环遍历X URL,并在返回时返回。 example from MS刚刚向Microsoft URLS发出了3个请求。
为了使它更像KB文章,我将按钮单击替换为在控制台应用程序运行时调用的Main程序,该程序依次调用Attack(),而后者仅(目前仅尝试获取3 URLS)。在真实的代码中,我有一个循环,但是我需要使这第一点起作用,这样我才能理解我在做什么错。
但是我在命令提示符下运行它时得到的却是...。
C:\Users\XXX>"C:\Users\XXX\Documents\Visual Studio 2017\Projects\DOSBot\DOSBot
\bin\Release\DOSBot.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("\r\n\r\nTotal bytes returned: {total}\r\n");
}
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
,则不会返回任何内容-但该方法仍会返回。
了解所有这些,让我们逐步了解程序中发生的事情:
[Main()
呼叫DOS.StartAttack()
。
StartAttack()
运行并调用CreateMultipleTasksAsync()
。CreateMultipleTasksAsync()
运行。ProcessURLAsync()
被调用。发送网络请求后,将返回不完整的Task
。当收到回复时,Task
对象将告诉您。ProcessURLAsync()
的其他两个调用发生相同的事情。await download1
,因为download1
是不完整的Task
,所以CreateMultipleTasksAsync()
返回其自己的不完整Task
。StartAttack()
。await
关键字看到从Task
返回的不完整CreateMultipleTasksAsync()
并返回。由于方法签名为void
,因此不返回任何内容。Main()
。由于没有其他可运行的程序,因此程序结束。StartAttack()
是void
,所以您不知道何时完成异步请求。要解决此问题:StartAttack()
以返回Task
而不是void
。[await DOS.StartAttack()
中的Main()
将Main()
的签名更改为:
public static async Task Main(string[] args)
也就是说,只要您使用的是C#7.1或更高版本。那时Main
方法可以开始返回Task
。
Microsoft在Asynchronous programming with async and await上有一些写得很好的文章。他们值得一读。该链接只是第一篇文章。您将在该页面左侧的目录中找到其余的内容。