.NET报https请求报传输流收到意外的 EOF 或 0 个字节

本文解决了在使用较低版本的.NET Framework进行HTTPS请求时遇到的“基础连接已关闭”错误。通过调整TLS协议版本至1.2,问题得以解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前段时间我有项目是用vs2005 winform开发的,有个https请求报错,提示“System.Net.WebException: 基础连接已经关闭: 发送时发生错误。 —> System.IO.IOException: 从传输流收到意外的 EOF 或 0 个字节。eceived an unexpected EOF or 0 bytes from the transport stream”,其他的https请求都正常,通过浏览器直接访问也正常。网上搜索,都说是.net framework版本太低,改为.net 4.5以上版本即可。于是使用vs2017环境测试了下,使用.net framework4.5版本确实可以正常返回结果,低于这个版本就报错。 但我的项目环境版本低,无法升级。后来通过fiddler抓包发现需要TLS协议版本是1.2的,而framework4.5以下版本SecurityProtocolType定义的TLS协议是1.0的,4.5以上是有1.2版本。后来在发送HTTPS请求前加入下行代码解决问题 ServicePointManager.SecurityProtocol = (SecurityProtocolType)192 | (SecurityProtocolType)768 | (SecurityProtocolType)3072。

<template> <view class="content"> <button @click="selectFile()">打开安卓文件管理器</button> <view> <text>单选URL</text> {{ filePath }} </view> <view><text>多选</text></view> <view v-for="(item, index) in fileList">{{ item }}</view> </view> </template> <script> export default { data() { return { filePath: '', fileList: [] }; }, onLoad() {}, methods: { selectFile() { let that = this; let main = plus.android.runtimeMainActivity(); let Intent = plus.android.importClass('android.content.Intent'); let intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType('*/*'); intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); //关键!多选参数 intent.addCategory(Intent.CATEGORY_OPENABLE); main.startActivityForResult(intent, 200); // 获取回调 main.onActivityResult = function(requestCode, resultCode, data) { let Activity = plus.android.importClass('android.app.Activity'); let ContentUris = plus.android.importClass('android.content.ContentUris'); let Cursor = plus.android.importClass('android.database.Cursor'); let Uri = plus.android.importClass('android.net.Uri'); let Build = plus.android.importClass('android.os.Build'); let Environment = plus.android.importClass('android.os.Environment'); let DocumentsContract = plus.android.importClass('android.provider.DocumentsContract'); let InputStream = plus.android.importClass('java.io.InputStream'); let contentResolver = main.getContentResolver(); plus.android.importClass(contentResolver); if (resultCode == Activity.RESULT_OK) { if (data.getData() != null) { let uri = data.getData(); let path = getPath(this, uri); that.filePath = path; // 获取文件大小 let fileSize = getFileSize(contentResolver, uri); console.log("文件大小: " + fileSize + " bytes"); // 获取文件内容 let fileContent = getFileContent(contentResolver, uri); console.log("文件内容: " + fileContent); } else { try { let ClipData = plus.android.importClass('android.content.ClipData'); let clipData = new ClipData(); clipData = data.getClipData(); if (clipData != null) { for (let i = 0; i < clipData.getItemCount(); i++) { let item = clipData.getItemAt(i); let uri = item.getUri(); let path = getPath(this, uri); that.fileList.push(path); // 获取文件大小 let fileSize = getFileSize(contentResolver, uri); console.log("文件大小: " + fileSize + " bytes"); // 获取文件内容 let fileContent = getFileContent(contentResolver, uri); console.log("文件内容: " + fileContent); } } } catch (e) { console.log(e); } } } function getFileSize(contentResolver, uri) { let cursor = contentResolver.query(uri, null, null, null, null); let size = 0; if (cursor != null && cursor.moveToFirst()) { let sizeIndex = cursor.getColumnIndex("_size"); if (sizeIndex != -1) { size = cursor.getLong(sizeIndex); } cursor.close(); } return size; } function getFileContent(contentResolver, uri) { try { // 打开输入流 let inputStream = contentResolver.openInputStream(uri); let ByteArrayOutputStream = plus.android.importClass('java.io.ByteArrayOutputStream'); let byteArrayOutputStream = new ByteArrayOutputStream(); // 创建一个缓冲区(使用标准 JavaScript 数组) let buffer = new Uint8Array(1024); // 每次读取 1024 字节 let bytesRead; // 循环读取文件内容 while ((bytesRead = inputStream.read(buffer.buffer)) !== -1) { console.log('bytesRead', bytesRead); byteArrayOutputStream.write(buffer.subarray(0, bytesRead)); } // 关闭输入流 inputStream.close(); // 获取完整的字节数组 let byteArray = byteArrayOutputStream.toByteArray(); byteArrayOutputStream.close(); console.log(`文件读取完成,共 ${byteArray.length} 字节`); return byteArray; // 返回字节数组 } catch (e) { console.error("读取文件内容时发生错误:", e); return null; } } function getPath(context, uri) { try { let isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider //一些三方的文件浏览器会进入到这个方法中,例如ES //QQ文件管理器不在此列 if (isExternalStorageDocument(uri)) { let docId = DocumentsContract.getDocumentId(uri); let split = docId.split(':'); let type = split[0]; // 如果是手机内部存储 if ('primary' == type.toLowerCase()) { return Environment.getExternalStorageDirectory() + '/' + split[1]; } else { return '/storage/' + type + '/' + split[1]; } } // DownloadsProvider else if (isDownloadsDocument(uri)) { let id = DocumentsContract.getDocumentId(uri); if (id.indexOf('raw:') > -1) { id = id.replace('raw:', ''); } // 不同系统获取的id开头可能不一样,在这后面便是真实的地址 if (id.substring(0, 5) == 'msf:') { return id.substring(5, id.length); } let contentUri = ContentUris.withAppendedId(Uri.parse( 'content://downloads/public_downloads'), ContentUris.parseId(uri)); return getDataColumn(context, contentUri, null, null); } // MediaProvider else if (isMediaDocument(uri)) { let MediaStore = plus.android.importClass('android.provider.MediaStore'); let docId = DocumentsContract.getDocumentId(uri); let split = docId.split(':'); let type = split[0]; let contentUri = null; if ('image' == type.toLowerCase()) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ('video' == type.toLowerCase()) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ('audio' == type.toLowerCase()) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } let selection = '_id=?'; let selectionArgs = [split[1]]; return getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ('content' == uri.getScheme().toLowerCase()) { if (isGooglePhotosUri(uri)) { return uri.getLastPathSegment(); } else if (isQQMediaDocument(uri)) { let paths = uri.getPath(); let Environment = plus.android.importClass('android.os.Environment'); let fileDir = Environment.getExternalStorageDirectory(); let files = plus.android.importClass('java.io.File'); let file = new files(fileDir, paths.substring('/QQBrowser'.length, paths .length)); return file.getPath(); } return getDataColumn(context, uri, null, null); } // File else if ('file' == uri.getScheme().toLowerCase()) { return uri.getPath(); } return null; } catch (e) { console.log(e); return null; } } // 通过uri 查找出绝对路径 function getDataColumn(context, uri, selection, selectionArgs) { try { let MediaStore = plus.android.importClass('android.provider.MediaStore'); let cursor = null; let column = MediaStore.MediaColumns.DATA; let projection = [column]; cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { let column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index); } } catch (e) { console.log(e); return null; } } function isExternalStorageDocument(uri) { return 'com.android.externalstorage.documents' == uri.getAuthority() ? true : false; } function isDownloadsDocument(uri) { return 'com.android.providers.downloads.documents' == uri.getAuthority() ? true : false; } function isMediaDocument(uri) { return 'com.android.providers.media.documents' == uri.getAuthority() ? true : false; } /** * 使用第三方qq文件管理器打开 * * @param uri * @return */ function isQQMediaDocument(uri) { return 'com.tencent.mtt.fileprovider' == uri.getAuthority() ? true : false; } /** * @param uri The Uri to check. * @return Whether the Uri authority is Google Photos. */ function isGooglePhotosUri(uri) { return 'com.google.android.apps.photos.content' == uri.getAuthority() ? true : false; } }; }, } }; </script> <style></style> 获取文件大小正确,但是我怎末获取文件的二进制内容从而符合以上文档的发送格式 附录1:Ymodem OTA 升级流程 1. YMODEM协议概述 YMODEM是一种基于XMODEM的改进文件传输协议,支持批处理传输和更大文件。133字节版本是YMODEM的标准实现,使用128字节数据块加5字节头信息。 发送方和接收方都需支持YMODEM协议。 确定使用CRC-16校验(YMODEM标准)。 2. 传输流程详解 2.1 初始化阶段 • 接收方发送"C" ASCII字符0x43,表示接收方准备就绪,请求使用CRC校验,连续发送直到收到第一个数据包超时 • 发送方响应 发送第一个数据块(序号0),包含文件名和文件信息 2.2 发送文件信息包(第0号包) 项 说明 SOH (0x01) 起始头 00 包序号0 FF 包序号的补码(255-0=255) 文件名 以NULL(0x00)结尾的ASCII字符串 文件大小 ASCII字符串表示的文件大小,以NULL结尾 修改时间 可选,ASCII格式的时间戳 填充 用0x00填充至128字节 CRC-16 2字节校验码 2.3 数据传输阶段 • 接收方ACK(0x06) 正确接收文件信息包后确认 • 发送方开始发送数据包: 项 说明 STX (0x02) 表示133字节数据包 序号 从1开始的包序号(1-255) 补码 255-序号 数据 实际文件数据,不足128字节EOF填充字符补全 STX (0x02) 表示133字节数据包 • 接受方响应 项 说明 ACK(0x06) 成功接收 NAK(0x15) 请求重传 CAN(0x18) 取消传输 2.4 传输结束阶段 • 发送方发送EOT(0x04) 文件传输结束 • 接收方ACK(0x06) 确认结束 • 最终结束 发送方发送全零数据包表示批传输结束 接收方ACK确认 3. 错误处理机制 3.1 重传机制 • 接收方检测到错误时发送NAK • 发送方重传当前数据包 3.2 超时处理 • 接收方等待数据包超时(通常0.5秒)发送NAK • 发送方等待响应超时重传当前包 3.3 重新传输 • 发送方发送: stop=1\r\n 其中\r\n是ascii表示换行回车符号,占2个字节 设备会在收到该指令后重新进行升级流程 4. 校验方式 • 多项式:x^16 + x^12 + x^5 + 1 (0x1021) • 初始值:0x0000 • 计算范围:以133字节为例,CRC计算从第1个字节到第131个字节,132和133分别保存CRC值的高低位 • CRC-16校验算法 5. 注意事项 • 包序号从0开始(文件信息),数据包从1开始 • 包序号达到255后回绕到0 • 文件名和文件信息必须使用ASCII字符
最新发布
05-14
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值