C# PdfReader上的iTextSharp多线程进入死胡同

C# PdfReader上的iTextSharp多线程进入死胡同,c#,multithreading,C#,Multithreading,在我们的生产服务器中,我们通过wkhtmltopdf创建数千个pdf文档,并使用HSM通过iTextSharp进行签名。大部分手术在夜间进行。但在执行多线程更新之后,我们已经陷入了一个严重的境地。在长时间工作后,我们的WCF服务开始为每个请求提供异常: System.IndexOutOfRangeException: Probable I/O race condition detected while copying memory. The I/O package is not thread s

在我们的生产服务器中,我们通过wkhtmltopdf创建数千个pdf文档,并使用HSM通过iTextSharp进行签名。大部分手术在夜间进行。但在执行多线程更新之后,我们已经陷入了一个严重的境地。在长时间工作后,我们的WCF服务开始为每个请求提供异常:

System.IndexOutOfRangeException: Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader. 
 at System.Buffer.InternalBlockCopy(Array src, Int32 srcOffsetBytes, Array dst, Int32 dstOffsetBytes, Int32 byteCount
 at System.IO.StreamWriter.Write(Char[] buffer, Int32 index, Int32 count
 at System.IO.TextWriter.SyncTextWriter.WriteLine(String value
 at iTextSharp.text.pdf.PdfReader..ctor(IRandomAccessSource byteSource, Boolean partialRead, Byte[] ownerPassword, X509Certificate certificate, ICipherParameters certificateKey, Boolean closeSourceOnConstructorError
 at iTextSharp.text.pdf.PdfReader..ctor(RandomAccessFileOrArray raf, Byte[] ownerPassword, Boolean partial
 at Turkkep.Daemons.Crypto.PDFSignerService.sign(Byte[] sourceData, Stream destination
 at Turkkep.Daemons.Crypto.PDFSignerService.Sign(Byte[] sourceData) Server stack trace: at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter
 at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc
 at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout
 at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation
 at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message) Exception rethrown at [0]: at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg
 at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type
 at Turkkep.Core.Signature.IPDFSigner.Sign(Byte[] sourceData
 at Turkkep.Shared.Signature.PDFSigner.Sign(Byte[] sourceData)
   --- End of inner exception stack trace --- at Turkkep.Shared.Signature.PDFSigner.Sign(Byte[] sourceData
 at Turkkep.Shared.Daemon.InvoiceWorker.getPdfDocument(InvoiceType12 ublInvoice, Int64 organization
 at Turkkep.Shared.Daemon.InvoiceWorker.processEArchive(Invoice invoice, String signedInvoice, InvoiceType12 ublInvoice
 at Turkkep.Shared.Daemon.InvoiceWorker.sendInvoice12(Invoice invoice)
我们的WCF服务签名代码为

    private void sign(byte[] sourceData, Stream destination)
    {
        RandomAccessFileOrArray source = new RandomAccessFileOrArray(sourceData);
        using (var reader = new PdfReader(source, null))
        {
            using (var stamper = PdfStamper.CreateSignature(reader, destination, '\0'))
            {
                var appearance = stamper.SignatureAppearance;
                appearance.Reason = _reason;
                appearance.Location = _location;
                appearance.SetVisibleSignature(new Rectangle(0, 0, 0, 0), 1, String.Format("TurkKEP-{0}-{1}-{2}", DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day));
                var externalSigner = new PDFExternalSigner("SHA-256");

                ICollection<X509Certificate> chain = getChain(externalSigner.GetActiveConnection());

                MakeSignature.SignDetached(appearance, externalSigner, chain, null, null, null, _estimatedSize, CryptoStandard.CMS);
            }
        }
    }

    public byte[] Sign(byte[] sourceData)
    {
        try
        {
            using (MemoryStream destination = new MemoryStream())
            {
                sign(sourceData, destination);
                return destination.ToArray();
            }
        }
        catch (FaultException)
        {
            throw;
        }
        catch (Exception ex)
        {
            _logger.Error("PDFSignerService.Sign", ex);
            throw new Exception(ex.ToString());
        }
    }
在此异常之后,服务为每个调用抛出相同的异常

现在,我们将更改如下代码,以便使用块将PdfStamper移出

private void sign(byte[] sourceData, Stream destination)
{
    RandomAccessFileOrArray source = new RandomAccessFileOrArray(sourceData);
    using (var reader = new PdfReader(source, null))
    {
        try
        {
            var stamper = PdfStamper.CreateSignature(reader, destination, '\0');
            var appearance = stamper.SignatureAppearance;
            appearance.Reason = _reason;
            appearance.Location = _location;
            appearance.SetVisibleSignature(new Rectangle(0, 0, 0, 0), 1, String.Format("TurkKEP-{0}-{1}-{2}", DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day));
            var externalSigner = new PDFExternalSigner("SHA-256");

            ICollection<X509Certificate> chain = getChain(externalSigner.GetActiveConnection());

            MakeSignature.SignDetached(appearance, externalSigner, chain, null, null, null, _estimatedSize, CryptoStandard.CMS);
            stamper.Dispose();
        }
        catch (Exception ex)
        {
            _logger.Error("Document sign failed unexpectedly.", ex);
            throw new FaultException("Document sign failed unexpectedly. Exception: " + ex.ToString());
        }
    }
}
private void符号(字节[]sourceData,流目的地)
{
RandomAccessFileOrArray source=新的RandomAccessFileOrArray(sourceData);
使用(变量读取器=新PdfReader(源,空))
{
尝试
{
var stamper=PdfStamper.CreateSignature(读卡器,目标,'\0');
var外观=母版。签名外观;
外观.原因=_原因;
外观.位置=_位置;
SetVisibleSignature(新矩形(0,0,0,0),1,String.Format(“TurkKEP-{0}-{1}-{2}”,DateTime.Now.Year,DateTime.Now.Month,DateTime.Now.Day));
var externalSigner=新的PDFExternalSigner(“SHA-256”);
ICollection chain=getChain(externalSigner.GetActiveConnection());
签名分离(外观、外部签名者、链、空、空、空、_estimatedSize、CryptoStandard.CMS);
母模处理();
}
捕获(例外情况除外)
{
_logger.Error(“文件签名意外失败”,例如);
抛出新的FaultException(“文档签名意外失败。异常:+ex.ToString());
}
}
}

看起来像IO问题..当两个线程试图写入同一个文件时..写入PDFW的代码在哪里?我们不写入文件。我们称之为“public byte[]Sign(byte[]sourceData)”方法..异常在PdfReader的构造函数中引发,该构造函数将以某种方式访问sourceData数组。可能是PdfReader中的一个bug,或者是您调用sign方法的代码中的bug,我们不认为sign方法是私有方法,它仅从public sign方法调用。。。。调用我们没有看到的Sign方法。
private void sign(byte[] sourceData, Stream destination)
{
    RandomAccessFileOrArray source = new RandomAccessFileOrArray(sourceData);
    using (var reader = new PdfReader(source, null))
    {
        try
        {
            var stamper = PdfStamper.CreateSignature(reader, destination, '\0');
            var appearance = stamper.SignatureAppearance;
            appearance.Reason = _reason;
            appearance.Location = _location;
            appearance.SetVisibleSignature(new Rectangle(0, 0, 0, 0), 1, String.Format("TurkKEP-{0}-{1}-{2}", DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day));
            var externalSigner = new PDFExternalSigner("SHA-256");

            ICollection<X509Certificate> chain = getChain(externalSigner.GetActiveConnection());

            MakeSignature.SignDetached(appearance, externalSigner, chain, null, null, null, _estimatedSize, CryptoStandard.CMS);
            stamper.Dispose();
        }
        catch (Exception ex)
        {
            _logger.Error("Document sign failed unexpectedly.", ex);
            throw new FaultException("Document sign failed unexpectedly. Exception: " + ex.ToString());
        }
    }
}