我运行以下代码时,发现在默认超时时间100s后,出现一堆
System.WebException: 操作超时
的错误。加追踪后发现,ID为2、4、6等的线程均出错,而0 1 3 5等则正常完成下载。代码如下(无追踪版):
public class ThreadDownload {
private volatile Array<bool> DownloadEndFlag;
private volatile Array<Array<byte>> DownloadBuffer;
private long DownloadEndBytes = 0;
public ThreadDownload(string URL,uint Threads = 32,string Path = null) {
HttpsStarts();
Path = Path==null ? GetURLFileName(URL) : Path;
Threads += 1;
DownloadEndFlag = new ((int)Threads);
DownloadBuffer = new ((int)Threads);
long Length = GetFileLength(URL);
long ThreadDownloadLong = (Length - Length % (Threads - 1)) / (Threads - 1);
if (CanAddRange(URL)) {
Thread PushThread = new (info => {
PushInfo Info = (PushInfo)info;
if (File.Exists(Info.Path)) {
File.Delete(Info.Path);
}
uint DownloadEndKey = 0;
for (;DownloadEndKey < Info.Threads;) {
if (DownloadEndFlag[(int)DownloadEndKey]) {
using (var write = new FileStream(Path,FileMode.Append,FileAccess.Write)) {
write.Write(DownloadBuffer[(int)DownloadEndKey],0,DownloadBuffer[(int)DownloadEndKey].Length);
}
++DownloadEndKey;
} else {
Thread.Sleep(150);
}
}
});
Array<Thread> DownThread = new Thread[Threads];
PushThread.Start(new PushInfo(Length,Threads,Path));
for (int i = 0;i < DownThread.Length;++i) {
DownloadInfo t = new DownloadInfo(
ThreadDownloadLong*i,
i==DownThread.Length-1 ? Length : ThreadDownloadLong*(i+1),
(uint)i,
URL);
DownThread[i] = new Thread(info => {
DownloadInfo Info = (DownloadInfo)info;
DownloadBuffer[(int)Info.ID] = new ((int)(Info.End-Info.Start+1));
var t = (HttpWebRequest)HttpWebRequest.Create(Info.URL);
t.AddRange(Info.Start,Info.End);
var t3 = t.GetResponse();//报错位置
var t2 = t3.GetResponseStream();
ReadAllData(t2,DownloadBuffer[(int)Info.ID],(uint)(Info.End-Info.Start+1));
t3.Close();
DownloadEndFlag[(int)Info.ID] = true;
});
DownThread[i].Start(t);
}
PushThread.Join();
} else {
new Download(URL,Path);
}
}
private struct DownloadInfo {
public long Start;
public long End;
public uint ID;
public string URL;
public DownloadInfo(long s,long e,uint i,string u) {
Start = s;
End = e;
ID = i;
URL = u;
}
}
private struct PushInfo {
public long Length;
public uint Threads;
public string Path;
public PushInfo(long l,uint t,string p) {
Length = l;
Threads = t;
Path = p;
}
}
}
internal static class DownloadHelperClass {
public static bool CanAddRange(string URL) {
var readreturntmp1 = ((HttpWebRequest)WebRequest.Create(URL));
readreturntmp1.AddRange(0,1);
var ret = ((HttpWebResponse)readreturntmp1.GetResponse()).Headers;
for (int i = 0;i < ret.AllKeys.Length;++i) {
if (ret.AllKeys[i].Equals("Accept-Ranges") && ret[i].Equals("bytes")) {
return true;
} else if (ret.AllKeys[i].Equals("Accept-Ranges") && ret[i].Equals("none")) {
return false;
}
}
return false;
}
public static void HttpsStarts() {
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.SystemDefault | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
}
public static string GetURLFileName(string URL) {
var t = new Uri(URL).Segments;
return t[t.Length-1];
}
public static void ReadAllData(Stream read,byte[] array,uint DataLong) {
array = new byte[DataLong];
for (uint key = 0;key < DataLong;) {
key += (uint)read.Read(array,(int)key,(int)(DataLong-key));
}
}
public static long GetFileLength(string URL) {
long length = 0;
var req = (HttpWebRequest)WebRequest.Create(URL);
req.Method = "HEAD";
var res = (HttpWebResponse)req.GetResponse();
if (res.StatusCode == HttpStatusCode.OK) {
length = res.ContentLength;
}
res.Close();
return length;
}
}
还在弄这个。其实问题早清楚了。
只不过我们没明说(因为我们知道不能跟喜欢园子风格的人谈这些,他们不会听的)
不过你弄了这么长时间,我们就不得不说了
2.有关分块,你是分线程,使用32线程,64线程控制。我给你的代码是分块,每块4M。因为我除了需要控制同时使用闸道的人以外,我还需要控制他们的使用时间,毕竟前面策略是出一个进一个,那么进去的这个不能太占时间,还是抢公交,假设进去的5个人吵起来了,他们不出去,后面的就得等了。所以我们限制每块4M---别问我们4M怎么来的,这个是经验参数。绝大部分网络参数设置默认就是4M,这是前面的前辈们调优的经验参数。如果换成固定32线程,大小就不固定了。也许你一个线程就128M,那么他占用的时间就比4M要长的多了
3.线程执行没有顺序,你可以多运行几次。他是系统调度,所以ID为2、4、6等的线程均出错是随机的,而非固定的。