.NET 2.0:连接被主机中的软件中止,如何排查?
结论
这类异常通常对应 Winsock 错误 10053(WSAECONNABORTED),表示连接在本机发送数据时被中止。堆栈停在 Socket.Send 或 NetworkStream.Write,只能确定上传过程中的写入失败,无法据此判断 YouTube API 或业务代码本身有问题。
同一段代码在 Windows XP 开发机上运行正常,却在 Windows 2000 Server 上失败,应先检查服务器的防火墙、杀毒软件、代理、网关和 TLS 能力。排查时,先记录完整异常和 Winsock 错误码,再分别用小文件、直连和临时停用网络过滤组件进行对照测试。
取得完整异常信息
仅记录 Exception.Message 不够,还要保留异常类型、内部异常、HTTP 状态和响应内容。
try
{
YouTubeEntry createdEntry = service.Upload(newEntry);
}
catch (System.Net.WebException ex)
{
Console.WriteLine(ex.ToString());
Console.WriteLine("WebExceptionStatus: " + ex.Status);
System.Net.HttpWebResponse response =
ex.Response as System.Net.HttpWebResponse;
if (response != null)
{
Console.WriteLine("HTTP Status: {0} {1}",
(int)response.StatusCode,
response.StatusDescription);
using (System.IO.Stream stream = response.GetResponseStream())
using (System.IO.StreamReader reader =
new System.IO.StreamReader(stream))
{
Console.WriteLine(reader.ReadToEnd());
}
}
System.Exception current = ex;
while (current != null)
{
System.Net.Sockets.SocketException socketException =
current as System.Net.Sockets.SocketException;
if (socketException != null)
{
Console.WriteLine("SocketErrorCode: " +
socketException.SocketErrorCode);
Console.WriteLine("NativeErrorCode: " +
socketException.NativeErrorCode);
}
current = current.InnerException;
}
}
catch (System.Exception ex)
{
Console.WriteLine(ex.ToString());
}
如果 NativeErrorCode 为 10053,说明本机协议栈或本机的网络过滤软件终止了连接。“本机中止”不代表应用主动关闭了连接,防火墙、杀毒软件、代理驱动或底层网络错误都可能造成这种结果。
按顺序排查
1. 比较运行环境
确认开发机与服务器的配置是否一致,主要包括:
- YouTube 客户端库及其依赖程序集版本
.NET Framework版本和补丁级别- 上传地址、账号、认证方式及配置文件
- 是否经过 HTTP/HTTPS 代理
- 服务进程账号及其网络权限
- 系统时间和根证书状态
不能只比较 .NET 2.0 这个大版本。补丁、证书库和操作系统网络组件的差异也会影响 HTTPS 请求。
2. 检查服务器能否直接访问目标地址
先在服务器上测试 DNS 解析和 HTTPS 连接。Windows 2000 自带的工具有限,可以使用系统中已有的浏览器、telnet,也可以部署一个简单的 HttpWebRequest 测试程序。
System.Net.HttpWebRequest request =
(System.Net.HttpWebRequest)System.Net.WebRequest.Create(
"https://目标上传主机/");
request.Method = "GET";
request.Timeout = 30000;
using (System.Net.HttpWebResponse response =
(System.Net.HttpWebResponse)request.GetResponse())
{
Console.WriteLine((int)response.StatusCode);
}
测试地址应是上传库实际连接的主机,不能只测试网站首页。身份验证地址、元数据接口和视频上传地址可能分属不同主机。
如果简单的 HTTPS 请求也失败,问题通常出在 TLS、代理、防火墙或操作系统网络环境,与视频内容关系不大。
3. 检查防火墙和杀毒软件
10053 经常与本机的网络过滤组件有关。检查以下项目:
- 主机防火墙的出站规则
- 杀毒软件的 Web 扫描、HTTPS 扫描或邮件扫描模块
- 入侵防御、内容过滤和数据防泄漏软件
- 服务器上的代理客户端或网络过滤驱动
- 网络设备是否限制大文件或持续时间较长的 HTTP 请求
可以在维护窗口内临时停用相关组件,进行一次对照测试。退出管理界面未必会卸载过滤驱动,还要确认对应服务和网络模块已经停止。测试完成后,应恢复原有安全配置。
如果停用后可以正常上传,应查看相关产品的日志,并针对进程、目标主机或端口添加精确规则,不要长期关闭安全软件。
4. 使用小文件进行对照
分别上传一个很小的文件和原视频,根据结果缩小排查范围:
- 小文件成功、大文件失败:检查上传时长、代理超时、内容扫描、请求大小限制和连接空闲超时。
- 所有文件都立即失败:检查 DNS、代理、认证、TLS 和防火墙。
- 每次都在相近的时间后失败:可能存在固定超时。
- 每次失败的位置不同:继续检查网络稳定性、网卡驱动和过滤软件。
记录文件大小、开始时间、失败时间和已发送字节数,比只保存异常消息更便于定位问题。
5. 检查代理配置
服务器进程使用的代理设置可能与登录用户或开发机不同。可以先输出实际使用的代理:
System.Net.IWebProxy proxy =
System.Net.WebRequest.DefaultWebProxy;
System.Uri target = new System.Uri("https://目标上传主机/");
if (proxy == null)
{
Console.WriteLine("No default proxy.");
}
else
{
Console.WriteLine("Proxy: " + proxy.GetProxy(target));
Console.WriteLine("Bypassed: " + proxy.IsBypassed(target));
}
如果网络允许直连,可以让测试请求暂时不使用代理:
request.Proxy = null;
这项设置只用于诊断。服务器如果必须通过代理访问外网,应配置正确的代理地址和凭据,不能永久绕过代理。
6. 测试 Expect: 100-continue
较旧的代理、网关或服务器可能无法正确处理带有 Expect: 100-continue 的上传请求。可以在进程启动阶段进行一次对照测试:
System.Net.ServicePointManager.Expect100Continue = false;
如果关闭后上传变得稳定,说明中间设备或目标端在处理 100 Continue 时可能存在兼容问题。这个设置会影响当前进程后续创建的相关 HTTP 请求,修改前需要评估其他调用是否会受影响。
7. 用网络抓包确认断开方
如果应用日志仍无法定位问题,可以在服务器端或网关侧抓包,查看连接断开前是否出现 TCP RST 或 FIN:
- 数据包由服务器本机发出:检查本机协议栈、防火墙、杀毒软件和进程行为。
- 数据包由代理或网关发出:检查超时、内容过滤和上传限制。
- 数据包由远端发出:检查认证、请求格式、服务端限制和接口兼容性。
- 一直没有收到对端响应,随后本机超时:检查路由、丢包和中间设备。
还要查看同一时间点的 Windows 事件日志,以及防火墙、代理和杀毒软件日志。
Windows 2000 与现代 YouTube 接口的兼容性
如果现在重新运行这套旧程序,还需要考虑平台过时带来的兼容问题。Windows 2000、.NET Framework 2.0 和早期 YouTube 客户端库支持的 TLS、证书及 API,可能已经不符合现有服务的要求。旧版 YouTube 上传接口也可能已经停用。
在这种情况下,调整 KeepAlive、超时或重试通常不能解决根本问题。应迁移到仍受支持的 Windows 和 .NET 版本,并改用当前受支持的 YouTube API。具体兼容范围取决于实际使用的客户端库版本和目标接口要求。
不建议直接采用的处理方式
不要通过忽略证书验证来处理 HTTPS 失败。这样会掩盖证书或 TLS 问题,并带来中间人攻击风险。未确定原因前也不要无限重试上传。如果代理限制或安全软件拦截了请求,重试只会反复发送相同的数据。
排查时,先取得 Winsock 错误码和完整异常,再通过小文件测试、直连、停用过滤组件和抓包逐步缩小范围。单凭 Socket.Send 的调用堆栈,无法判断连接具体是在哪一层被关闭的。
备注:内容仅供参考。
