anchor.diff:              else
                   Ok parseUnknown
   
  -        let parse (msg : MsgReceived) =
  +        let deserialize (msg : MsgReceived) =
   
               let content = Encoding.UTF8.GetString(msg.UdpResult.Buffer)
   
               getVerb content
               |> Result.bind verbToParser
  -            |> Result.map (fun parse -> parse content msg)
  -
  +            |> Result.map (fun parse -> parse content)
  +
  +    module internal SsdpMsgSerialize =
  +        let CRLF = "\r\n"
  +
  +        let serializeNotify (msg : SsdpNotifyMessage) =
  +
  +            let s =
  +                "NOTIFY * HTTP/1.1" + CRLF
  +                + (sprintf "Host:%s:%d" (msg.Host.Address.ToString()) msg.Host.Port) + CRLF
  +                + (sprintf "ST:%s" msg.ST) + CRLF
  +                + (sprintf "NTS:%s" msg.NTS) + CRLF
  +                + (sprintf "Location:%s" msg.Location) + CRLF
  +                + (sprintf "USN:%s" msg.USN) + CRLF
  +                // Skipping cache-control for now.
  +                + (sprintf "Server:%s" msg.Server) + CRLF
  +                + CRLF
  +            Encoding.ASCII.GetBytes(s)
  +
  +        let serializeMSearch (msg : SsdpMSearchMessage) =
  +
  +            let s =
  +                "M-SEARCH * HTTP/1.1" + CRLF
  +                + (sprintf "Host:%s:%d" (msg.Host.Address.ToString()) msg.Host.Port) + CRLF
  +                + (sprintf "ST:%s" msg.ST) + CRLF
  +                + (sprintf "MAN:%s" msg.MAN) + CRLF
  +                + CRLF
  +            Encoding.ASCII.GetBytes(s)
  +
  +        let serialize = function
  +            | Notify msg ->     serializeNotify msg
  +            | MSearch msg ->    serializeMSearch msg
  +            | Unhandled _ ->    failwith "not supported"
  +
  +    //-------------------------------------------------------------------------
  +    // SerDes helpers
  +
  +    open SsdpMsgSerialize
  +    open SsdpMsgDeserialize
   
       type SsdpMessage with
  -        static member internal From(packet : MsgReceived) = SsdpMsgDetails.parse packet
  +        static member internal From(packet : MsgReceived) : Result<SsdpMessage, string> =
  +            
  +            deserialize packet
  +
  +        static member internal ToPacket (message : SsdpMessage) : byte array =
  +
  +            serialize message
  
positive.code:     open System.Net.Sockets
      open System.Text
  
      type internal MsgReceived = { UdpResult : UdpReceiveResult; Timestamp : DateTimeOffset }
  
      type CacheControl = Dummy
  
      /// Traits of a received SSDP message.
      type SsdpMessageTraits = {
  
negative.code: 
              map
  
          (*---------------------------------------------------------------------
  
              Message Object Construction
  
              We construct SSDP message objects from a received packet. The
              packet looks something like this:
  
                  NOTIFY * HTTP/1.1\r\n
                  Host: 239.255.255.250:1900\r\n
                  Cache-Control: max-age=4\r\n
                  Location: 192.168.10.164:55456\r\n
                  NT: uuid:4E50646A-B607-4ECB-9676-8DC10ABE8A5F\r\n
                  NTS: ssdp:alive\r\n
                  SERVER: windows/6.2 IntelUSBoverIP:1/1\r\n
                  USN: uuid:4E50646A-B607-4ECB-9676-8DC10ABE8A5F::IntelUSBoverIP:1\r\n
                  \r\n
                  ...
  
              In order to parse that into an object, we
  
                  1.  Determine the message type from the first line (NOTIFY).
  
                  2.  Parse the remaining lines, up to the first empty line, as
                      name-value pairs.
  
                  3.  Using a dictionary of field setter functions, fold over the
                      name-value pairs and, if there is a setter function available
                      for that field, use it to create an updated object.
  
                  4.  The output of the fold is the object representation of the message.
          *)
  
  
          // Helper function for applying a fold to a key-value map
          // (args key and value are an entry in this map).
          // The key and value args are from a received SSDP message.
          // The state of the fold is the message being built.
          // If the key, e.g., HOST, is in the function map, the function
          // for HOST is applied and creates a new state (object).
          //
          // (The functions in the map are keyed by name, such as HOST,
          // for which the value is probably 239.255.255.250:1900.)
          let inline private applyValueToMsg functionMap state key value =
              if functionMap |> Map.containsKey key then
                  let fn = functionMap.[key]
                  fn state value
              else
                  state
  
          //---------------------------------------------------------------------
          // Parse the NOTIFY message
  
          let private notifyFieldSetters = // At module scope to create it only once.
              [
                  "HOST",     fun msg value ->
                                  match parseEndPoint value with
  