开发者

How do you move the MSDN "Self-Hosted" WCF example away from localhost?

I've gone through the MSDN WCF - Getting Started Tutorial, and it went great until I tried to move the client from one machine in my domain to another in my domain. When I moved the client peice to the other machine in my network, it gave me an SecurityNegotiationException. Here is what I've done:

  1. I defined a service contract (See code block 1 below).
  2. I implemented the service contract (See code block 2 below).
  3. I created a host to run the service (See code block 3 below).
  4. Ran svcutil.exe to generate my proxy class and config files (See code block 4 below).
  5. Copied output from 4 into my client project.
  6. I Created a client to connect to my host (See code block 5 below).

When I run the service and the client on my own machine (SCOTT), it works fine. When I run the service on my machine (SCOTT) and run the client on my virtual machine (SCOTT-VM) it fails with the following stack trace:

Unhandled Exception: System.ServiceModel.Security.SecurityNegotiationException: SOAP security negotiation with 'http://scott:8000/ServiceModelSamples/Service/CalculatorService' for target 'http://scott:8000/ServiceModelSamples/Service/CalculatorService' failed. See inner exception for more details. ---> System.ComponentModel.Win32Exception: Security Support Provider Interface (SSPI) authentication failed. The server may not be running in an account with identity 'host/scott'. If the server is running in a service account (Network Service for example), specify the account's ServicePrincipalName as the identity in the EndpointAddress for the server. If the server is running in a user account, specify the account's UserPrincipalName as th开发者_StackOverflowe identity in the EndpointAddress for the server.
   at System.ServiceModel.Security.WindowsSspiNegotiation.GetOutgoingBlob(Byte[] incomingBlob)
   at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)
   at System.ServiceModel.Security.IssuanceTokenProviderBase`1.GetNextOutgoingMessage(Message incomingMessage, T negotiationState)
   at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout)
   --- End of inner exception stack trace ---

Server stack trace:
   at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout)
   at System.ServiceModel.Security.SspiNegotiationTokenProvider.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Security.CommunicationObjectSecurityTokenProvider.Open(TimeSpan timeout)
   at System.ServiceModel.Security.SecurityUtils.OpenTokenProviderIfRequired(SecurityTokenProvider tokenProvider, TimeSpan timeout)
   at System.ServiceModel.Security.SymmetricSecurityProtocol.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.SecurityChannelFactory`1.ClientSecurityChannel`1.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.DoOperation(SecuritySessionOperation operation, EndpointAddress target, Uri via, SecurityToken currentToken, TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.GetTokenCore(TimeSpan timeout)
   at System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecuritySessionChannel.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)

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 System.ServiceModel.ICommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.ClientBase`1.System.ServiceModel.ICommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.ClientBase`1.Open()
   at Client.Module1.Main() in C:\Projects\SKUNK\WCFServiceTutorial\Client\Module1.vb:line 11

(I used code block above so that it would scroll and be formatted nicely. I tried to use quote and it was unreadable.)

To a trained user of WCF this stack trace probably has the glaringly obvious issue, but I cannot see the problem. First, I'd like to have this problem solved. Second, I'd like to know of any good tutorials and training materials. mark_s has some great links in his answer. I have zipped up my source code for your review.

Thanks


Code Blocks

Code Block 1:

Imports System.ServiceModel

<ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _
Public Interface ICalculator

    <OperationContract()> _
    Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double
    <OperationContract()> _
    Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double
    <OperationContract()> _
    Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double
    <OperationContract()> _
    Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double
    <OperationContract()> _
    Function Sin(ByVal n1 As Double) As Double

End Interface

Code Block 2:

Public Class CalculatorService
    Implements ICalculator

    Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Add
        Dim result As Double = n1 + n2
        Console.WriteLine("Received Add({0}, {1})", n1, n2)
        Console.WriteLine("Return: {0}", result)
        Return result
    End Function

    Public Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Subtract
        Dim result As Double = n1 - n2
        Console.WriteLine("Received Subtract({0},{1})", n1, n2)
        Console.WriteLine("Return: {0}", result)
        Return result

    End Function

    Public Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Multiply
        Dim result As Double = n1 * n2
        Console.WriteLine("Received Multiply({0},{1})", n1, n2)
        Console.WriteLine("Return: {0}", result)
        Return result

    End Function


    Public Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Divide
        Dim result As Double = n1 / n2
        Console.WriteLine("Received Divide({0},{1})", n1, n2)
        Console.WriteLine("Return: {0}", result)
        Return result

    End Function

    Public Function Sin(ByVal n1 As Double) As Double Implements ICalculator.Sin
        Dim result As Double = Math.Sin(n1)
        Console.WriteLine("Received Sin({0})", n1)
        Console.WriteLine("Return: {0}", result)
        Return result
    End Function
End Class

Code Block 3:

Imports System.ServiceModel
Imports System.ServiceModel.Description

Module Module1

    Sub Main()
        Dim baseAddress As New Uri("http://scott:8000/ServiceModelSamples/Service")
        Using selfHost As New ServiceHost(GetType(CalculatorService), baseAddress)
            Try
                ' Add a service endpoint
                selfHost.AddServiceEndpoint(GetType(ICalculator), New WSHttpBinding(), "CalculatorService")
                ' Enable metadata exchange
                Dim smb As New ServiceMetadataBehavior()
                smb.HttpGetEnabled = True
                selfHost.Description.Behaviors.Add(smb)

                selfHost.Open()
                Console.WriteLine("The service is ready.")
                Console.WriteLine("Press <ENTER> to terminate service.")
                Console.WriteLine()
                Console.ReadLine()

                ' Close the ServiceHostBase to shutdown the service.
                selfHost.Close()
            Catch ex As Exception
                Console.WriteLine("An exception occurred: {0}", ex.Message)
                selfHost.Abort()
            End Try
        End Using

    End Sub

End Module

Code Block 4:

C:\> svcutil.exe /language:vb /out:c:\generatedProxy.vb /config:c:\app.config http://scott:8000/ServiceModelSamples/service

Output from Code Block 4:

app.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_ICalculator" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
                    allowCookies="false">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <reliableSession ordered="true" inactivityTimeout="00:10:00"
                        enabled="false" />
                    <security mode="Message">
                        <transport clientCredentialType="Windows" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="Windows" negotiateServiceCredential="true"
                            algorithmSuite="Default" establishSecurityContext="true" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://scott:8000/ServiceModelSamples/Service/CalculatorService"
                binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ICalculator"
                contract="ICalculator" name="WSHttpBinding_ICalculator">
                <identity>
                    <userPrincipalName value="{MY_DOMAIN_NAME}" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

generatedProxy.vb:

'------------------------------------------------------------------------------
' <auto-generated>
'     This code was generated by a tool.
'     Runtime Version:2.0.50727.4200
'
'     Changes to this file may cause incorrect behavior and will be lost if
'     the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------

Option Strict Off
Option Explicit On



<System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0"),  _
 System.ServiceModel.ServiceContractAttribute([Namespace]:="http://Microsoft.ServiceModel.Samples", ConfigurationName:="ICalculator")>  _
Public Interface ICalculator

    <System.ServiceModel.OperationContractAttribute(Action:="http://Microsoft.ServiceModel.Samples/ICalculator/Add", ReplyAction:="http://Microsoft.ServiceModel.Samples/ICalculator/AddResponse")>  _
    Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double

    <System.ServiceModel.OperationContractAttribute(Action:="http://Microsoft.ServiceModel.Samples/ICalculator/Subtract", ReplyAction:="http://Microsoft.ServiceModel.Samples/ICalculator/SubtractResponse")>  _
    Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double

    <System.ServiceModel.OperationContractAttribute(Action:="http://Microsoft.ServiceModel.Samples/ICalculator/Multiply", ReplyAction:="http://Microsoft.ServiceModel.Samples/ICalculator/MultiplyResponse")>  _
    Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double

    <System.ServiceModel.OperationContractAttribute(Action:="http://Microsoft.ServiceModel.Samples/ICalculator/Divide", ReplyAction:="http://Microsoft.ServiceModel.Samples/ICalculator/DivideResponse")>  _
    Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double

    <System.ServiceModel.OperationContractAttribute(Action:="http://Microsoft.ServiceModel.Samples/ICalculator/Sin", ReplyAction:="http://Microsoft.ServiceModel.Samples/ICalculator/SinResponse")>  _
    Function Sin(ByVal n1 As Double) As Double
End Interface

<System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")>  _
Public Interface ICalculatorChannel
    Inherits ICalculator, System.ServiceModel.IClientChannel
End Interface

<System.Diagnostics.DebuggerStepThroughAttribute(),  _
 System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")>  _
Partial Public Class CalculatorClient
    Inherits System.ServiceModel.ClientBase(Of ICalculator)
    Implements ICalculator

    Public Sub New()
        MyBase.New
    End Sub

    Public Sub New(ByVal endpointConfigurationName As String)
        MyBase.New(endpointConfigurationName)
    End Sub

    Public Sub New(ByVal endpointConfigurationName As String, ByVal remoteAddress As String)
        MyBase.New(endpointConfigurationName, remoteAddress)
    End Sub

    Public Sub New(ByVal endpointConfigurationName As String, ByVal remoteAddress As System.ServiceModel.EndpointAddress)
        MyBase.New(endpointConfigurationName, remoteAddress)
    End Sub

    Public Sub New(ByVal binding As System.ServiceModel.Channels.Binding, ByVal remoteAddress As System.ServiceModel.EndpointAddress)
        MyBase.New(binding, remoteAddress)
    End Sub

    Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Add
        Return MyBase.Channel.Add(n1, n2)
    End Function

    Public Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Subtract
        Return MyBase.Channel.Subtract(n1, n2)
    End Function

    Public Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Multiply
        Return MyBase.Channel.Multiply(n1, n2)
    End Function

    Public Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Divide
        Return MyBase.Channel.Divide(n1, n2)
    End Function

    Public Function Sin(ByVal n1 As Double) As Double Implements ICalculator.Sin
        Return MyBase.Channel.Sin(n1)
    End Function
End Class

Code Block 5:

Imports System.ServiceModel

Module Module1

    Sub Main()
        'Dim client As MyCalcServiceRef.CalculatorClient = New MyCalcServiceRef.CalculatorClient()
        '' Step 1: Create an endpoint address and an instance of the WCF Client.
        Dim epAddress As New EndpointAddress("http://scott:8000/ServiceModelSamples/Service/CalculatorService")
        Dim Client As New CalculatorClient(New WSHttpBinding(), epAddress)

        Client.Open()

        'Step 2: Call the service operations.
        'Call the Add service operation.
        Dim value1 As Double = 100D
        Dim value2 As Double = 15.99D
        Dim result As Double = client.Add(value1, value2)
        Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result)

        'Call the Subtract service operation.
        value1 = 145D
        value2 = 76.54D
        result = client.Subtract(value1, value2)
        Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result)

        'Call the Multiply service operation.
        value1 = 9D
        value2 = 81.25D
        result = client.Multiply(value1, value2)
        Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result)

        'Call the Divide service operation.
        value1 = 22D
        value2 = 7D
        result = client.Divide(value1, value2)
        Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result)

        'Call the Sin service operation.
        value1 = 144D
        result = client.sin(value1)
        Console.WriteLine("Sin({0}) = {1}", value1, result)

        ' Step 3: Closing the client gracefully closes the connection and cleans up resources.
        client.Close()

        Console.WriteLine()
        Console.WriteLine("Press <ENTER> to terminate client.")
        Console.ReadLine()
    End Sub

End Module


A "self-hosted service" (one running in an application exe) can be just as open to the world as one hosted in IIS or a web service. It all depends on what bindings you use. The examples show WSHttpBinding which uses TCP/IP. Like any ordinary TCP-based server (including IIS) on Windows, this will be open to the world unless something like a firewall blocks access. The only difference is the examples bind to port 8000 instead of 80 as IIS would by default (the examples possibly do this to avoid any possibility of conflicting with IIS).

You need to track down exactly why you are getting a SecurityNegotiationException. By default security features are enabled on the WSHttpBinding, though these can be shut off. Generally, the client and server should be on the same domain, otherwise expect to have to do extra work to get them to successfully authenticate.

This gets some hits: https://stackoverflow.com/search?q=SecurityNegotiationException


There's a ton of good WCF material out there - it's just a question of finding it :-)

  • WCF Developer Center @ MSDN
  • A bunch of WCF screencasts by Aaron Skonnard @ Pluralsight

Then there's a ton of blogs I follow, and of course there's the "Service Station" column in MSDN magazine (usually more intermediate to advanced topics)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜