.NET Convert.To/FromBase64String导致SOAP消息中的OutOfMemory异常
我使用SOAP扩展来加密/解密服务器和客户端Web服务之间的SOAP消息 一般流程是,在发送soap消息之前,1.提取消息正文,2.加密消息,3.向消息中添加客户端信息,4.将消息放回消息中,最后将修改后的soap消息发送到服务器 它的工作与小要求完美。 但在发送大请求时,当我试图将加密的字节数组转换为人类可读的字符串(Base64String),然后将其放回SOAP消息时,会引发OutOfMemory异常。 在调试器中,我可以看到消息太大,当在Stackoverflow上查找该异常时,我知道.NET Convert.To/FromBase64String导致SOAP消息中的OutOfMemory异常,.net,web-services,soap,base64,out-of-memory,.net,Web Services,Soap,Base64,Out Of Memory,我使用SOAP扩展来加密/解密服务器和客户端Web服务之间的SOAP消息 一般流程是,在发送soap消息之前,1.提取消息正文,2.加密消息,3.向消息中添加客户端信息,4.将消息放回消息中,最后将修改后的soap消息发送到服务器 它的工作与小要求完美。 但在发送大请求时,当我试图将加密的字节数组转换为人类可读的字符串(Base64String),然后将其放回SOAP消息时,会引发OutOfMemory异常。 在调试器中,我可以看到消息太大,当在Stackoverflow上查找该异常时,我知道C
Convert.ToBase64String(bytearr)
在转换整个大字节数组时将抛出OOM异常,我应该逐块转换
但当我更改为逐块转换这个字节数组时,仍然会出现OOM异常,如下代码所示
请给我一些处理这个问题的建议
SOAP消息修改方法:
Private Shared Function EncryptSoap(ByVal streamToEncrypt As Stream, ByVal clientInfo As String, ByVal encryptKey As String) As MemoryStream
streamToEncrypt.Position = 0
'read the soap stream into a xml doc
Dim reader As XmlTextReader = New XmlTextReader(streamToEncrypt)
Dim xmlDoc As XmlDocument = New XmlDocument()
xmlDoc.Load(reader)
'' Select soap body to encrypt
Dim targetNode As XmlNode = xmlDoc.SelectSingleNode(SOAPConstraints.c_strSOAP_BODY_STR, nsmgr)
'' Encrypt SOAP Body
Dim encryptedString As String = Utilities.Rfc2898Encrypt(targetNode.InnerXml, encryptKey)
' build soap body string = clientInfo + Encrypted Request
Dim strbld As StringBuilder = New StringBuilder(clientInfo, clientInfo.Length + encryptedString.Length)
strbld.Append(encryptedString)
'now save the encrypted xml doc to the returning stream
targetNode.InnerXml = strbld.ToString
'now save the encrypted xml doc to the returning stream
Dim ms As MemoryStream = New MemoryStream()
xmlDoc.Save(ms)
ms.Position = 0
Return ms
End Function
下面是加密方法:
Public Shared Function Rfc2898Encrypt(ByVal p_strInput As String, ByVal p_strEncryptKey As String) As String
Try
'' Encrypt Object initialization
Dim rijndael As New System.Security.Cryptography.RijndaelManaged()
Dim encrypter As System.Security.Cryptography.ICryptoTransform = rijndael.CreateEncryptor()
Dim buffer As Byte() = System.Text.Encoding.Unicode.GetBytes(p_strInput)
Dim bytearr As Byte() = encrypter.TransformFinalBlock(buffer, 0, buffer.Length)
'' Convert to human readable string (Base64String)
'' Convert all at one => THROW OUT OF MEM EXCEPTION
'Return Convert.ToBase64String(bytearr) ''
Dim chunkSize As Integer = 3 * 1024
'' Convert chunk by chunk
''**ADD** Init MemoryStream with predicted capacity
Using ms As New MemoryStream((bytearr.Length / 3 + 1) * 4)
Dim sw As New StreamWriter(ms)
Dim intConvertCount As Integer = bytearr.Length \ chunkSize
For idx As Integer = 0 To intConvertCount - 1
sw.Write(Convert.ToBase64String(bytearr, idx * chunkSize, chunkSize)) '' => STILL THROW OUT OF MEM EXCEPTION
sw.Flush()
Next
'' convert last block
sw.Write(Convert.ToBase64String(bytearr, intConvertCount * chunkSize, bytearr.Length - intConvertCount * chunkSize))
sw.Flush()
ms.Position = 0
Dim sr As New StreamReader(ms)
Return sr.ReadToEnd
End Using
Catch ex As Exception
Throw ex
End Try
Return String.Empty
End Function
其他信息
异常堆栈跟踪:
System.String.GetStringForStringBuilder(String value, Int32 startIndex, Int32 length, Int32 capacity)
System.Text.StringBuilder.GetNewString(String currentString, Int32 requiredLength)
System.Text.StringBuilder.Append(Char[] value, Int32 startIndex, Int32 charCount)
System.IO.StreamReader.ReadToEnd()
消息长度:
加密前(p_strInput
Length):2748763210加密(
bytearr
Length)后:54975280添加(a)第二个OOM的堆栈跟踪和(b)正在处理的bytearr
的长度。另外,尝试预先分配一个内存流,其容量刚好足以容纳base64编码的消息。默认情况下,MemoryStream在需要扩展时会将其容量加倍,我猜这就是它爆炸的地方。@AntonTykhyy:我已经添加了一些关于消息长度和异常堆栈跟踪的附加信息。我还用预测的大小初始化了MemoryStream
。看起来OOM不会出现在Convert.ToBase64String上,因为我们已经在流中完成了。但当我试图从流中获取整个字符串以推回XML SOAP消息时,OOM仍然发生。堆栈跟踪与注释“still THROW OOM”的行不匹配。是哪一个?顺便说一下,您不需要使用内存流和所有这些读写器。只需创建一个字符串生成器并直接向其追加base64块。另外,我觉得奇怪的是,一个2,7G字符长的字符串加密到54M字节(缓冲区的大小是多少?)可能是一个整数。长于int.MaxValue元素的数组需要使用长索引,并且并非所有CLR代码都一致地使用长索引。