Соединение с сокетом было прервано - CommunicationException
Первоначально:
- Я думал, что это была проблема круговой ссылки........ Оказывается, это не так.
- Проблема возникла из-за того, что не настроил конфигурацию сервиса вообще.
- Поскольку значения по умолчанию очень низкие, отправка большого количества данных приведет к сбою службы.
Сценарий:
- Кажется, у меня могут быть циклические ссылки в моей службе WCF, но использование "[DataContract(IsReference=true)]" ничего не может сделать, чтобы это исправить.
- Я получаю сообщение об ошибке "Соединение с сокетом было прервано. Это может быть вызвано ошибкой при обработке вашего сообщения или превышением тайм-аута приема удаленным хостом, или проблемой основного сетевого ресурса. Тайм-аут локального сокета был" 00:01:00 " ".
- Я что-то пропустил?
Код:
[DataContract(IsReference=true)]
public class Message
{
[DataMember]
public string TopicName { get; set; }
[DataMember]
public string EventData { get; set; }
[DataMember]
public SerializableDictionary<string, FuturesLineAsset> FuturesLineDictionary { get; set ; }
}
Мысли:
- Интересно, это потому, что у меня есть класс FuturesAsset, у которого есть свойство типа BindableDictionary (ЭТО ТАМОЖЕННЫЙ ОБЪЕКТ), и это свойство содержит список FuturesLinesAssets.
- Увидеть ниже:
родитель:
public class FuturesAsset
{
public string AssetName { get; set; }
public BindableDictionary<string, FuturesLineAsset> AssetLines { get; private set; }
public FuturesAsset()
{
AssetLines = new BindableDictionary<string, FuturesLineAsset>();
}
public FuturesAsset(string assetName)
{
AssetLines = new BindableDictionary<string, FuturesLineAsset>();
AssetName = assetName;
}
}
Ребенок:
public class FuturesLineAsset
{
public string ReferenceAsset { get; set; }
public string MID { get; set; }
public double LivePrice { get; set; }
public DateTime UpdateTime { get; set; }
public DateTime LastContributedTime { get; set; }
public double Spread { get; set; }
public double Correlation { get; set; }
public DateTime Maturity { get; set; }
public double ReferenceCurve { get; set; }
public FuturesLineAsset(string mID, string referenceAsset, double livePrice)
{
MID = mID;
ReferenceAsset = referenceAsset;
ReutersLivePrice = livePrice;
}
}
6 ответов
Это исключение не относится к циклическому справочнику, оно просто истекает по времени, когда вы пытаетесь передать тонны данных по проводам.
Значения по умолчанию, которые идут с WCF, очень и очень низки (я думаю, они были изменены в WCF 4). Прочитайте эти два сообщения в блоге, они должны дать вам представление о том, как свернуть ваш сервис:
Создание высокопроизводительных сервисов WCF
Как ограничить службу Wcf, помочь предотвратить DoS-атаки и поддерживать масштабируемость Wcf
Обновление: кроме того, в конфигурации WCF есть несколько разных таймаутов, и в зависимости от того, о каком клиенте или сервере вы говорите, нужно обновить другое предложение тайм-аута... прочитайте эту ветку о том, что означает каждый из них. и вы должны быть в состоянии выяснить, какой из них вам нужно увеличить. Или вы можете просто установить для каждого тайм-аута значение int.max, если вам все равно, если вызов может занять много времени.
Эта ошибка может быть вызвана несколькими причинами. Хотя в данном случае это была проблема с синхронизацией, обычно она не имеет ничего общего с синхронизацией, особенно, если ошибка получена немедленно. Возможные причины:
- Объекты, используемые в качестве параметров или возвращаемых типов в вашем контракте, не имеют конструкторов без параметров и не имеют атрибута DataContract. Проверьте классы, используемые в качестве параметров или возвращаемых типов, а также все типы, используемые открытыми свойствами этих классов. Если вы реализуете конструктор с параметрами для одного из этих классов, компилятор больше не будет добавлять для вас конструктор по умолчанию без параметров, поэтому вам придется добавить его самостоятельно.
- Ограничения по умолчанию, определенные в конфигурации службы, слишком малы (MaxItemsInObjectGraph, MaxReceivedMessageSize, MaxBufferPoolSize, MaxBufferSize, MaxArrayLength).
- Некоторые открытые свойства ваших объектов DataContract доступны только для чтения. Убедитесь, что все общедоступные свойства имеют как геттеры, так и сеттеры.
Возникла проблема с длительным процессом инициализации, который вызывался из события OnStart установщика узла службы Windows. Исправлено путем установки режима безопасности и тайм-аутов для привязки TCP.
// Create a channel factory.
NetTcpBinding b = new NetTcpBinding();
b.Security.Mode = SecurityMode.Transport;
b.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
b.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
b.MaxReceivedMessageSize = 1000000;
b.OpenTimeout = TimeSpan.FromMinutes(2);
b.SendTimeout = TimeSpan.FromMinutes(2);
b.ReceiveTimeout = TimeSpan.FromMinutes(10);
Эта проблема также может быть вызвана тем, что клиент WCF не очищает, когда вы его используете. В нашей системе мы используем одноразовый шаблон вместе с упаковкой всех вызовов функций в систему, чтобы обеспечить надлежащую очистку и регистрацию. Мы используем версию следующего класса:
public class WcfWrapper : IDisposable
{
private readonly OperationContextScope _operationContextScope;
private readonly IClientChannel _clientChannel;
public WcfWrapper(IClientChannel clientChannel)
{
_clientChannel = clientChannel;
_operationContextScope = new OperationContextScope(_clientChannel);
}
public void Dispose()
{
_operationContextScope.Dispose();
}
public T Function<T>(Func<T> func)
{
try
{
var result = func();
_clientChannel.Close();
return result;
}
catch (Exception ex)
{
KTrace.Error(ex);
_clientChannel.Abort();
throw;
}
}
public void Procedure(Action action)
{
try
{
action();
_clientChannel.Close();
}
catch (Exception ex)
{
KTrace.Error(ex);
_clientChannel.Abort();
throw;
}
}
}
}
Каждый вызов WCF, который мы делаем в нашем сервисе, осуществляется через определенный класс интерфейса, подобный следующему:
public sealed class WcfLoginManager : ILoginManager
{
private static LoginManagerClient GetWcfClient()
{
return
new LoginManagerClient(
WcfBindingHelper.GetBinding(),
WcfBindingHelper.GetEndpointAddress(ServiceUrls.LoginManagerUri));
}
public LoginResponse Login(LoginRequest request)
{
using(var loginManagerClient = GetWcfClient())
using (var slice = new WcfWrapper(loginManagerClient.InnerChannel))
{
DSTicket ticket;
DSAccount account;
return slice.Function(() => new LoginResponse(loginManagerClient.Login(request.accountName, request.credentials, out ticket, out account), ticket, account));
}
}
}
Используя этот шаблон, все вызовы WCF в систему переносятся с помощью метода Function или Method, что позволяет им сначала убедиться, что ведение журнала происходит при всех ошибках, и, во-вторых, обеспечить закрытие канала, когда ошибки не возникают, но прервать, если происходит исключение., Наконец, как указано в операторе using, вызывается окончательное удаление канала. Таким образом, ошибки, возникающие из-за неправильной очистки каналов, которые будут выглядеть как эта ошибка, будут предотвращены.
Это исключение произошло для меня, когда я возвращал объект с коллекциями IEnumerable, и возникла исключительная ситуация при получении одного из членов коллекции. В этот момент уже слишком поздно перехватывать его в вашем коде, и, по-видимому, WCF предназначен для отключения сокета в этом случае, поскольку также слишком поздно сообщать клиенту об исключительной ситуации, поскольку он уже начал потоковую передачу результатов.
Ошибка WCF:
Разъем подключения был прерван. Это может быть вызвано ошибкой обработки вашего сообщения или превышением тайм-аута приема удаленным хостом, или проблемой основного сетевого ресурса. Тайм-аут локального сокета был...
где время ожидания очень близко к 1 минуте (например, 00:00:59.9680000
) или ровно 1 минуту (т.е. 00:01:00
) может быть вызвано тем, что сообщение слишком велико и превышает настройки для привязки.
Это можно исправить, увеличив значения в файле конфигурации, например:
<binding name="MyWcfBinding"
maxReceivedMessageSize="10000000"
maxBufferSize="10000000"
maxBufferPoolSize="10000000" />
(только примеры значений, вы можете настроить их).
В моем случае я пытался создать экземпляр Wcf с помощью net tcp. Итак, если в разделе привязок вашего web.config у вас есть "netTcpBinding", настроенный следующим образом
<bindings>
<netTcpBinding>
<binding name="bindingName" closeTimeout="01:10:00" openTimeout="01:10:00" receiveTimeout="01:10:00" sendTimeout="01:10:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxConnections="10" maxReceivedMessageSize="2147483647" portSharingEnabled="true">
<readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="16384"/>
<reliableSession ordered="true" inactivityTimeout="01:10:00" enabled="false"/>
<security mode="None"/>
</binding>
</netTcpBinding>
</bindings>
Затем вам нужно настроить раздел службы, определяющий конечные точки с их атрибутом контракта, используя пространство имен интерфейса ваших служб, и также поместить baseAddres, что-то вроде этого
<services>
<service behaviorConfiguration="sgiBehavior" name="Enterprise.Tecnic.SGI.OP.Wcf.OP">
<endpoint address="" behaviorConfiguration="endPointBehavior" binding="webHttpBinding" bindingConfiguration="BindingWebHttp" name="endPointHttp" contract="Enterprise.Tecnic.SGI.OP.Interface.IOP"/>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
<endpoint address="" binding="netTcpBinding" bindingConfiguration="bindingName"
name="EndpointNetTcp" contract="Enterprise.Tecnic.SGI.OP.Interface.IOP" />
<host>
<baseAddresses>
<!-- <add baseAddress="http://localhost:61217/OP.svc"/> -->
<add baseAddress="http://www.Enterprise.com/Tecnic/SGI/OP/" />
<add baseAddress="net.tcp://www.Enterprise.com/Tecnic/SGI/OP/" />
</baseAddresses>
</host>
</service>
</services>
Я провел два дня с этой проблемой, но это единственное, что у меня сработало.