Dela via


Transport: UDP

UDP Transport-exemplet visar hur du implementerar UDP unicast och multicast som en anpassad WCF-transport (Windows Communication Foundation). Exemplet beskriver den rekommenderade proceduren för att skapa en anpassad transport i WCF, med hjälp av kanalramverket och följande metodtips för WCF. Stegen för att skapa en anpassad transport är följande:

  1. Bestäm vilken av kanalens Exchange-mönster för meddelande (IOutputChannel, IInputChannel, IDuplexChannel, IRequestChannel eller IReplyChannel) som ChannelFactory och ChannelListener stöder. Bestäm sedan om du ska stödja sessionskänsliga varianter av dessa gränssnitt.

  2. Skapa en kanalfabrik och lyssnare som stöder ditt Message Exchange-mönster.

  3. Se till att alla nätverksspecifika undantag normaliseras till lämplig härledd klass för CommunicationException.

  4. Lägg till ett <bindningselement> som lägger till den anpassade transporten i en kanalstacken. Mer information finns i Lägga till ett bindningselement.

  5. Lägg till ett avsnitt för bindningselementtillägg för att exponera det nya bindningselementet för konfigurationssystemet.

  6. Lägg till metadatatillägg för att kommunicera funktioner till andra slutpunkter.

  7. Lägg till en bindning som förkonfigurerar en stack med bindningselement enligt en väldefinierad profil. Mer information finns i Lägga till en standardbindning.

  8. Lägg till ett bindningsavsnitt och ett bindningskonfigurationselement för att exponera bindningen till konfigurationssystemet. Mer information finns i Lägga till konfigurationsstöd.

Exchange-mönster för meddelanden

Det första steget i att skriva en anpassad transport är att bestämma vilka mönster för meddelandeutbyte som krävs för transporten. Det finns tre parlamentsledamöter att välja mellan:

  • Datagram (IInputChannel/IOutputChannel)

    När du använder en datagram-MEP skickar en klient ett meddelande med hjälp av ett "fire and forget"-utbyte. En brand och glöm utbyte är en som kräver out-of-band bekräftelse av lyckad leverans. Meddelandet kan gå förlorat under överföring och aldrig nå tjänsten. Om sändningsåtgärden har slutförts i klientdelen garanterar den inte att fjärrslutpunkten har tagit emot meddelandet. Datagrammet är en grundläggande byggsten för meddelanden, eftersom du kan skapa egna protokoll ovanpå det, inklusive tillförlitliga protokoll och säkra protokoll. Klientdatagramkanaler implementerar IOutputChannel gränssnittet och tjänstens datagramkanaler implementerar IInputChannel gränssnittet.

  • Begärandesvar (IRequestChannel/IReplyChannel)

    I denna parlamentsledamot skickas ett meddelande och ett svar tas emot. Mönstret består av par för begäran-svar. Exempel på begärandesvarsanrop är fjärrproceduranrop (RPC) och webbläsar-GET. Det här mönstret kallas även för Half-Duplex. I denna MEP implementerar IRequestChannel klientkanaler och tjänstkanaler implementerar IReplyChannel.

  • Duplex (IDuplexChannel)

    Duplex-PARP tillåter att ett godtyckligt antal meddelanden skickas av en klient och tas emot i valfri ordning. Parparlamentarikern är som ett telefonsamtal, där varje ord som talas är ett meddelande. Eftersom båda sidor kan skicka och ta emot i denna Parlamentsledamot är IDuplexChannelgränssnittet som implementeras av klient- och tjänstkanalerna .

Var och en av dessa parlamentsledamöter kan också stödja sessioner. Den tillagda funktionen som tillhandahålls av en sessionsmedveten kanal är att den korrelerar alla meddelanden som skickas och tas emot på en kanal. Mönstret Request-Response är en fristående session med två meddelanden eftersom begäran och svar korreleras. Däremot innebär mönstret Begäran-svar som stöder sessioner att alla par för begäran/svar på kanalen är korrelerade med varandra. Detta ger dig totalt sex parlamentsledamöter – Datagram, Begärandesvar, Duplex, Datagram med sessioner, Begärandesvar med sessioner och Duplex med sessioner – att välja mellan.

Kommentar

För UDP-transporten är den enda Parlamentsledamot som stöds Datagram, eftersom UDP i sig är ett "fire and forget"-protokoll.

ICommunicationObject och WCF-objektets livscykel

WCF har en vanlig tillståndsdator som används för att hantera livscykeln för objekt som IChannel, IChannelFactoryoch IChannelListener som används för kommunikation. Det finns fem tillstånd där dessa kommunikationsobjekt kan finnas. Dessa tillstånd representeras av CommunicationState uppräkningen och är följande:

  • Skapad: Det här är tillståndet för en ICommunicationObject när den först instansieras. Inga indata/utdata (I/O) inträffar i det här tillståndet.

  • Öppning: Objekt övergår till det här tillståndet när Open anropas. I det här läget görs egenskaperna oföränderliga och indata/utdata kan börja. Den här övergången är endast giltig från tillståndet Skapad.

  • Öppnat: Objekt övergår till det här tillståndet när den öppna processen är klar. Den här övergången är endast giltig från tillståndet Öppna. I det här läget kan objektet användas fullt ut för överföring.

  • Stängning: Objekt övergår till det här tillståndet när Close anropas för en graciös avstängning. Den här övergången är endast giltig från tillståndet Öppnade.

  • Stängd: Objekt med stängt tillstånd kan inte längre användas. I allmänhet är de flesta konfigurationer fortfarande tillgängliga för inspektion, men ingen kommunikation kan ske. Det här tillståndet motsvarar att tas bort.

  • Fel: I feltillståndet är objekt tillgängliga för inspektion men kan inte längre användas. När ett fel som inte kan återställas inträffar övergår objektet till det här tillståndet. Den enda giltiga övergången från det här tillståndet är till Closed tillståndet.

Det finns händelser som utlöses för varje tillståndsövergång. Metoden Abort kan anropas när som helst och gör att objektet övergår omedelbart från dess aktuella tillstånd till tillståndet Stängd. Samtal Abort avslutar allt oavslutat arbete.

Kanalfabrik och kanallyssnare

Nästa steg i att skriva en anpassad transport är att skapa en implementering av IChannelFactory för klientkanaler och IChannelListener för tjänstkanaler. Kanallagret använder ett fabriksmönster för att konstruera kanaler. WCF tillhandahåller basklasshjälp för den här processen.

I det här exemplet finns fabriksimplementeringen i UdpChannelFactory.cs och lyssnarimplementeringen finns i UdpChannelListener.cs. Implementeringarna IChannel finns i UdpOutputChannel.cs och UdpInputChannel.cs.

UDP-kanalfabriken

Härleds UdpChannelFactory från ChannelFactoryBase. Exemplet åsidosätter GetProperty för att ge åtkomst till meddelandeversionen av meddelandekodaren. Exemplet åsidosätter OnClose också så att vi kan ta bort vår instans av BufferManager när tillståndsdatorn övergår.

UDP-utdatakanalen

Implementerar UdpOutputChannelIOutputChannel. Konstruktorn validerar argumenten och konstruerar ett målobjekt EndPoint baserat på EndpointAddress det som skickas in.

this.socket = new Socket(this.remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);

Kanalen kan stängas graciöst eller ospårbart. Om kanalen stängs korrekt stängs socketen och ett anrop görs till basklassmetoden OnClose . Om detta utlöser ett undantag anropar Abort infrastrukturen för att säkerställa att kanalen rensas.

this.socket.Close(0);

Sedan implementerar Send() vi och/BeginSend()EndSend() . Detta delas upp i två huvudavsnitt. Först serialiserar vi meddelandet till en bytematris.

ArraySegment<byte> messageBuffer = EncodeMessage(message);

Sedan skickar vi resulterande data på tråden.

this.socket.SendTo(messageBuffer.Array, messageBuffer.Offset, messageBuffer.Count, SocketFlags.None, this.remoteEndPoint);

The UdpChannelListener

Det UdpChannelListener som exemplet implementerar härleds från ChannelListenerBase klassen. Den använder en enda UDP-socket för att ta emot datagram. Metoden OnOpen tar emot data med UDP-socketen i en asynkron loop. Data konverteras sedan till meddelanden med hjälp av Ramverk för meddelandekodning.

message = MessageEncoderFactory.Encoder.ReadMessage(new ArraySegment<byte>(buffer, 0, count), bufferManager);

Eftersom samma datagramkanal representerar meddelanden som kommer från ett antal källor är UdpChannelListener det en singleton-lyssnare. Det finns som mest en aktiv IChannel som är associerad med den här lyssnaren i taget. Exemplet genererar endast en annan om en kanal som returneras av AcceptChannel metoden sedan tas bort. När ett meddelande tas emot placeras det i den här singleton-kanalen.

UdpInputChannel

Klassen UdpInputChannel implementerar IInputChannel. Den består av en kö med inkommande meddelanden som fylls i av socketen UdpChannelListener. Dessa meddelanden är dequeued av IInputChannel.Receive metoden.

Lägga till ett bindningselement

Nu när fabrikerna och kanalerna har skapats måste vi exponera dem för ServiceModel-körningen via en bindning. En bindning är en samling bindningselement som representerar kommunikationsstacken som är associerad med en tjänstadress. Varje element i stacken representeras av ett bindningselement>.<

I exemplet är UdpTransportBindingElementbindningselementet , som härleds från TransportBindingElement. Den åsidosätter följande metoder för att skapa de fabriker som är associerade med vår bindning.

public IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
{
    return (IChannelFactory<TChannel>)(object)new UdpChannelFactory(this, context);
}

public IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
{
    return (IChannelListener<TChannel>)(object)new UdpChannelListener(this, context);
}

Den innehåller också medlemmar för att klona BindingElement och returnera vårt schema (soap.udp).

Lägga till metadatastöd för ett transportbindningselement

För att integrera vår transport i metadatasystemet måste vi stödja både import och export av principen. På så sätt kan vi generera klienter för vår bindning via ServiceModel Metadata Utility Tool (Svcutil.exe).

Lägga till WSDL-stöd

Transportbindningselementet i en bindning ansvarar för att exportera och importera adresseringsinformation i metadata. När du använder en SOAP-bindning bör transportbindningselementet också exportera en korrekt transport-URI i metadata.

WSDL-export

Om du vill exportera adresseringsinformation UdpTransportBindingElement implementerar IWsdlExportExtension gränssnittet. Metoden ExportEndpoint lägger till rätt adressinformation till WSDL-porten.

if (context.WsdlPort != null)
{
    AddAddressToWsdlPort(context.WsdlPort, context.Endpoint.Address, encodingBindingElement.MessageVersion.Addressing);
}

Implementeringen UdpTransportBindingElement av ExportEndpoint metoden exporterar också en transport-URI när slutpunkten använder en SOAP-bindning.

WsdlNS.SoapBinding soapBinding = GetSoapBinding(context, exporter);
if (soapBinding != null)
{
    soapBinding.Transport = UdpPolicyStrings.UdpNamespace;
}

WSDL-import

Om du vill utöka WSDL-importsystemet till att hantera importen av adresserna måste vi lägga till följande konfiguration i konfigurationsfilen för Svcutil.exe som visas i filen Svcutil.exe.config.

<configuration>
  <system.serviceModel>
    <client>
      <metadata>
        <policyImporters>
          <extension type=" Microsoft.ServiceModel.Samples.UdpBindingElementImporter, UdpTransport" />
        </policyImporters>
      </metadata>
    </client>
  </system.serviceModel>
</configuration>

När du kör Svcutil.exe finns det två alternativ för att hämta Svcutil.exe för att läsa in WSDL-importtilläggen:

  1. Peka Svcutil.exe till konfigurationsfilen med hjälp av filen> /SvcutilConfig:<.

  2. Lägg till konfigurationsavsnittet i Svcutil.exe.config i samma katalog som Svcutil.exe.

Typen UdpBindingElementImporter implementerar IWsdlImportExtension gränssnittet. Metoden ImportEndpoint importerar adressen från WSDL-porten.

BindingElementCollection bindingElements = context.Endpoint.Binding.CreateBindingElements();
TransportBindingElement transportBindingElement = bindingElements.Find<TransportBindingElement>();
if (transportBindingElement is UdpTransportBindingElement)
{
    ImportAddress(context);
}

Lägga till principstöd

Det anpassade bindningselementet kan exportera principkontroller i WSDL-bindningen för en tjänstslutpunkt för att uttrycka funktionerna i det bindningselementet.

Principexport

Typen UdpTransportBindingElement implementerar IPolicyExportExtension för att lägga till stöd för exportprincip. Därför System.ServiceModel.MetadataExporter ingår UdpTransportBindingElement i genereringen av principen för alla bindningar som innehåller den.

I IPolicyExportExtension.ExportPolicylägger vi till en försäkran för UDP och en annan försäkran om vi är i multicast-läge. Det beror på att multicast-läget påverkar hur kommunikationsstacken konstrueras och därför måste samordnas mellan båda sidor.

ICollection<XmlElement> bindingAssertions = context.GetBindingAssertions();
XmlDocument xmlDocument = new XmlDocument();
bindingAssertions.Add(xmlDocument.CreateElement(
UdpPolicyStrings.Prefix, UdpPolicyStrings.TransportAssertion, UdpPolicyStrings.UdpNamespace));
if (Multicast)
{
    bindingAssertions.Add(xmlDocument.CreateElement(
        UdpPolicyStrings.Prefix,
        UdpPolicyStrings.MulticastAssertion,
        UdpPolicyStrings.UdpNamespace));
}

Eftersom anpassade transportbindningselement ansvarar för hantering av adresser IPolicyExportExtension måste implementeringen av UdpTransportBindingElement även hantera export av lämpliga principkontroller för WS-adressering för att ange vilken version av WS-Addressing som används.

AddWSAddressingAssertion(context, encodingBindingElement.MessageVersion.Addressing);

Principimport

Om du vill utöka systemet för principimport måste vi lägga till följande konfiguration i konfigurationsfilen för Svcutil.exe som visas i filen Svcutil.exe.config.

<configuration>
  <system.serviceModel>
    <client>
      <metadata>
        <policyImporters>
          <extension type=" Microsoft.ServiceModel.Samples.UdpBindingElementImporter, UdpTransport" />
        </policyImporters>
      </metadata>
    </client>
  </system.serviceModel>
</configuration>

Sedan implementerar IPolicyImporterExtension vi från vår registrerade klass (UdpBindingElementImporter). I ImportPolicy()tittar vi igenom försäkran i vårt namnområde och bearbetar dem för att generera transporten och kontrollerar om det är multicast. Vi måste också ta bort de intyg som vi hanterar från listan över bindande intyg. När du kör Svcutil.exe finns det två alternativ för integrering:

  1. Peka Svcutil.exe till konfigurationsfilen med hjälp av filen> /SvcutilConfig:<.

  2. Lägg till konfigurationsavsnittet i Svcutil.exe.config i samma katalog som Svcutil.exe.

Lägga till en standardbindning

Vårt bindningselement kan användas på följande två sätt:

  • Via en anpassad bindning: Med en anpassad bindning kan användaren skapa en egen bindning baserat på en godtycklig uppsättning bindningselement.

  • Genom att använda en systembaserad bindning som innehåller vårt bindningselement. WCF tillhandahåller ett antal av dessa systemdefinierade bindningar, till exempel BasicHttpBinding, NetTcpBindingoch WsHttpBinding. Var och en av dessa bindningar är associerad med en väldefinierad profil.

Exemplet implementerar profilbindning i SampleProfileUdpBinding, som härleds från Binding. Innehåller SampleProfileUdpBinding upp till fyra bindningselement i den: UdpTransportBindingElement, TextMessageEncodingBindingElement CompositeDuplexBindingElementoch ReliableSessionBindingElement.

public override BindingElementCollection CreateBindingElements()
{
    BindingElementCollection bindingElements = new BindingElementCollection();
    if (ReliableSessionEnabled)
    {
        bindingElements.Add(session);
        bindingElements.Add(compositeDuplex);
    }
    bindingElements.Add(encoding);
    bindingElements.Add(transport);
    return bindingElements.Clone();
}

Lägga till en anpassad standardbindningsimportör

Svcutil.exe och WsdlImporter typen identifierar och importerar som standard systemdefinierade bindningar. Annars importeras bindningen som en CustomBinding instans. Om du vill aktivera Svcutil.exe och WsdlImporter importera SampleProfileUdpBindingUdpBindingElementImporter fungerar även som en anpassad standardbindningsimportör.

En anpassad standardbindningsimportör implementerar ImportEndpoint metoden i IWsdlImportExtension gränssnittet för att undersöka den instans som CustomBinding importerats från metadata för att se om den kunde ha genererats av en specifik standardbindning.

if (context.Endpoint.Binding is CustomBinding)
{
    Binding binding;
    if (transportBindingElement is UdpTransportBindingElement)
    {
        //if TryCreate is true, the CustomBinding will be replace by a SampleProfileUdpBinding in the
        //generated config file for better typed generation.
        if (SampleProfileUdpBinding.TryCreate(bindingElements, out binding))
        {
            binding.Name = context.Endpoint.Binding.Name;
            binding.Namespace = context.Endpoint.Binding.Namespace;
            context.Endpoint.Binding = binding;
        }
    }
}

I allmänhet innebär implementering av en anpassad standardbindningsimportör att kontrollera egenskaperna för de importerade bindningselementen för att kontrollera att endast egenskaper som kunde ha angetts av standardbindningen har ändrats och att alla andra egenskaper är deras standardvärden. En grundläggande strategi för att implementera en standardbindningsimportör är att skapa en instans av standardbindningen, sprida egenskaperna från bindningselementen till den standardbindningsinstans som standardbindningen stöder och jämföra bindningselementen från standardbindningen med de importerade bindningselementen.

Lägga till konfigurationsstöd

För att exponera vår transport via konfigurationen måste vi implementera två konfigurationsavsnitt. Den första är en BindingElementExtensionElement för UdpTransportBindingElement. Det är så att CustomBinding implementeringar kan referera till vårt bindningselement. Den andra är en Configuration för vår SampleProfileUdpBinding.

Tilläggselement för bindningselement

Avsnittet UdpTransportElement är ett BindingElementExtensionElement som exponeras UdpTransportBindingElement för konfigurationssystemet. Med några grundläggande åsidosättningar definierar vi vårt namn på konfigurationsavsnittet, typen av bindningselement och hur du skapar vårt bindningselement. Sedan kan vi registrera vårt tilläggsavsnitt i en konfigurationsfil enligt följande kod.

<configuration>
  <system.serviceModel>
    <extensions>
      <bindingElementExtensions>
        <add name="udpTransport" type="Microsoft.ServiceModel.Samples.UdpTransportElement, UdpTransport" />
      </bindingElementExtensions>
    </extensions>
  </system.serviceModel>
</configuration>

Tillägget kan refereras från anpassade bindningar för att använda UDP som transport.

<configuration>
  <system.serviceModel>
    <bindings>
      <customBinding>
       <binding configurationName="UdpCustomBinding">
         <udpTransport/>
       </binding>
      </customBinding>
    </bindings>
  </system.serviceModel>
</configuration>

Bindningsavsnitt

Avsnittet SampleProfileUdpBindingCollectionElement är ett StandardBindingCollectionElement som exponeras SampleProfileUdpBinding för konfigurationssystemet. Huvuddelen av implementeringen delegeras till SampleProfileUdpBindingConfigurationElement, som härleds från StandardBindingElement. Har SampleProfileUdpBindingConfigurationElement egenskaper som motsvarar egenskaperna för SampleProfileUdpBindingoch funktioner som ska mappas från bindningen ConfigurationElement . Slutligen åsidosätter du OnApplyConfiguration metoden i vår SampleProfileUdpBinding, som du ser i följande exempelkod.

protected override void OnApplyConfiguration(string configurationName)
{
    if (binding == null)
        throw new ArgumentNullException("binding");

    if (binding.GetType() != typeof(SampleProfileUdpBinding))
    {
        throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
            "Invalid type for binding. Expected type: {0}. Type passed in: {1}.",
            typeof(SampleProfileUdpBinding).AssemblyQualifiedName,
            binding.GetType().AssemblyQualifiedName));
    }
    SampleProfileUdpBinding udpBinding = (SampleProfileUdpBinding)binding;

    udpBinding.OrderedSession = this.OrderedSession;
    udpBinding.ReliableSessionEnabled = this.ReliableSessionEnabled;
    udpBinding.SessionInactivityTimeout = this.SessionInactivityTimeout;
    if (this.ClientBaseAddress != null)
        udpBinding.ClientBaseAddress = ClientBaseAddress;
}

För att registrera den här hanteraren med konfigurationssystemet lägger vi till följande avsnitt i den relevanta konfigurationsfilen.

<configuration>
  <configSections>
     <sectionGroup name="system.serviceModel">
        <sectionGroup name="bindings">
          <section name="sampleProfileUdpBinding" type="Microsoft.ServiceModel.Samples.SampleProfileUdpBindingCollectionElement, UdpTransport" />
        </sectionGroup>
     </sectionGroup>
  </configSections>
</configuration>

Det kan sedan refereras från avsnittet serviceModel-konfiguration.

<configuration>
  <system.serviceModel>
    <client>
      <endpoint configurationName="calculator"
                address="soap.udp://localhost:8001/"
                bindingConfiguration="CalculatorServer"
                binding="sampleProfileUdpBinding"
                contract= "Microsoft.ServiceModel.Samples.ICalculatorContract">
      </endpoint>
    </client>
  </system.serviceModel>
</configuration>

UDP-testtjänsten och -klienten

Testkod för att använda den här exempeltransporten finns i UdpTestService- och UdpTestClient-katalogerna. Tjänstkoden består av två tester – ett test konfigurerar bindningar och slutpunkter från kod och det andra gör det via konfigurationen. Båda testerna använder två slutpunkter. En slutpunkt använder SampleUdpProfileBinding med <reliableSession> inställt på true. Den andra slutpunkten använder en anpassad bindning med UdpTransportBindingElement. Detta motsvarar att använda SampleUdpProfileBinding med <reliableSession> inställt på false. Båda testerna skapar en tjänst, lägger till en slutpunkt för varje bindning, öppnar tjänsten och väntar sedan på att användaren ska trycka på RETUR innan tjänsten stängs.

När du startar tjänsttestprogrammet bör du se följande utdata.

Testing Udp From Code.
Service is started from code...
Press <ENTER> to terminate the service and start service from config...

Du kan sedan köra testklientprogrammet mot de publicerade slutpunkterna. Klienttestprogrammet skapar en klient för varje slutpunkt och skickar fem meddelanden till varje slutpunkt. Följande utdata finns på klienten.

Testing Udp From Imported Files Generated By SvcUtil.
0
3
6
9
12
Press <ENTER> to complete test.

Följande är de fullständiga utdata för tjänsten.

Service is started from code...
Press <ENTER> to terminate the service and start service from config...
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
   adding 0 + 0
   adding 1 + 2
   adding 2 + 4
   adding 3 + 6
   adding 4 + 8

Om du vill köra klientprogrammet mot slutpunkter som publicerats med hjälp av konfigurationen trycker du på RETUR på tjänsten och kör sedan testklienten igen. Du bör se följande utdata på tjänsten.

Testing Udp From Config.
Service is started from config...
Press <ENTER> to terminate the service and exit...

Att köra klienten igen ger samma resultat som föregående resultat.

Om du vill återskapa klientkoden och konfigurationen med hjälp av Svcutil.exe startar du tjänstprogrammet och kör sedan följande Svcutil.exe från rotkatalogen i exemplet.

svcutil http://localhost:8000/udpsample/ /reference:UdpTransport\bin\UdpTransport.dll /svcutilConfig:svcutil.exe.config

Observera att Svcutil.exe inte genererar bindningstilläggskonfigurationen SampleProfileUdpBindingför , så du måste lägga till den manuellt.

<configuration>
  <system.serviceModel>
    <extensions>
      <!-- This was added manually because svcutil.exe does not add this extension to the file -->
      <bindingExtensions>
        <add name="sampleProfileUdpBinding" type="Microsoft.ServiceModel.Samples.SampleProfileUdpBindingCollectionElement, UdpTransport" />
      </bindingExtensions>
    </extensions>
  </system.serviceModel>
</configuration>

Så här konfigurerar du, skapar och kör exemplet

  1. Skapa lösningen genom att följa anvisningarna i Skapa Windows Communication Foundation-exempel.

  2. Om du vill köra exemplet i en konfiguration med en eller flera datorer följer du anvisningarna i Köra Windows Communication Foundation-exempel.

  3. Se avsnittet "UDP-testtjänst och -klient" ovan.