Sådan bruger du TcpClient i Windows 8 Consumer Preview

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg skriver en Metro-app i Windows 8 Consumer Preview.


Imidlertid kan jeg ikke bruge TcpClient i .NET 4.5, der synes ikke at være et sted at tilføje forsamlingsreferencen.


http://msdn.microsoft.com/en-us/library/1612451t(v=vs.110).aspx[2]

Bedste reference


TcpClient understøttes ikke på metro side. Du kan bruge StreamSocket klasse i stedet. Her er en prøve om hvordan man bruger den til at oprette en TCP-stik, foretage en forbindelse, sende og modtage data. Prøverne er i JS og C ++, men samme klasse vil fungere for C #. [3] [4]

Andre referencer 1


I sidste ende skal vi nok bruge de nye Metro NET-ting. Men hvis du bruger en masse kode og afhængigt af hvor meget af de TcpClient-medlemmer du bruger, er det måske ikke så slemt, at du kun opretter en begrænset implementering omkring Metro-objekter. Jeg ønskede at gøre en hurtig port af en masse kode over til Metro i en fart (bare for at prøve nogle ting), så jeg slog sammen noget, der syntes at virke, men absolut ikke ideelt. (Du ender med at lave nogle synkronisering af async-metoder, som almindeligvis frynser.)


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;

namespace MetroNetHelper
{
public class IPAddress  // assumes IPv4 currently
{
    public string IP\_String;
    public IPAddress() { }
    public IPAddress(string IP) { IP\_String = IP; }
    public static IPAddress Broadcast { get { return new IPAddress("255.255.255.255"); } }
    public static IPAddress Parse(string IP) { return new IPAddress(IP); }
    public static bool TryParse(string V, out IPAddress Addr)
    {
        try
        {
            Addr = IPAddress.Parse(V);
            return true;
        }
        catch { Addr = null; return false; }
    }
    public HostName GetHostNameObject() { return new HostName(IP\_String); }
    public byte[] GetAddressBytes()
    {
        string[] Fields = IP\_String.Split('.');
        byte[] temp = new byte[4];
        for (int i = 0; i < temp.Length; i++) temp[i] = byte.Parse(Fields[i]);
        return temp;
    }
}

public class IPEndPoint
{
    public IPAddress Address;
    public int Port;
    public IPEndPoint() { }
    public IPEndPoint(IPAddress Addr, int PortNum)
    {
        Address = Addr; Port = PortNum;
    }
}

public class NetworkStream
{
    private DataReader Reader;
    private DataWriter Writer;

    public void Set(StreamSocket HostClient)
    {
        Reader = new DataReader(HostClient.InputStream);
        Reader.InputStreamOptions = InputStreamOptions.Partial;
        Writer = new DataWriter(HostClient.OutputStream);
    }

    public int Write(byte[] Buffer, int Offset, int Len)
    {
        if (Offset != 0 || Len != Buffer.Length) throw new ArgumentException("Can only write whole byte array");
        Writer.WriteBytes(Buffer);
        Task Tk = Writer.StoreAsync().AsTask();
        Tk.Wait();
        return Buffer.Length;
    }

    public int Read(byte[] Buffer, int Offset, int Len)
    {
        if (Offset != 0 || Len != Buffer.Length) throw new ArgumentException("Can only read whole byte array");
        Task<uint> Tk = Reader.LoadAsync((uint)Len).AsTask<uint>();
        Tk.Wait();
        uint Count = Tk.Result;
        for (int i=0;i<Count;i++)
        {
            Buffer[i] = Reader.ReadByte();
        }
        return (int)Count;
    }

    public bool DataAvailable
    {
        get
        {
            return true; // Read() will still work if no data; could we do read ahead 1 byte to determine?
        }
    }
}


public class TcpClient
{
    private StreamSocket sock;

    public void Connect(IPEndPoint EndPt)
    {
        try
        {
            sock = new StreamSocket();
            HostName Hst = EndPt.Address.GetHostNameObject();
            Task Tsk = sock.ConnectAsync(Hst, EndPt.Port.ToString()).AsTask();
            Tsk.Wait();
        }
        catch (Exception ex) { MetroHelpers.UnpeelAggregate(ex); }
    }

    public void Close()
    {
        sock.Dispose();
        sock = null;
    }

    public NetworkStream GetStream()
    {
        var N = new NetworkStream();
        N.Set(sock);
        return N;
    }
}

public static class MetroHelpers
{
    public static void UnpeelAggregate(Exception Ex)
    {
        AggregateException Ag\_Ex = Ex as AggregateException;
        if (Ag\_Ex == null) throw Ex;
        if (Ag\_Ex.InnerExceptions.Count > 0)
        {
            if (Ag\_Ex.InnerExceptions.Count == 1) throw Ag\_Ex.InnerExceptions[0];
            StringBuilder Str = new StringBuilder();
            foreach (Exception X in Ag\_Ex.InnerExceptions)
            {
                Str.AppendLine(X.Message);
            }
            throw new Exception(Str.ToString(), Ag\_Ex);
        }
        else throw Ag\_Ex;
    }
}
} // END NAMESPACE


Dette var bare noget, jeg gjorde hurtig og beskidt om morgenen. Hvis det hjælper nogen med ideer, gode. Igen er vi sandsynligvis bedre ude af at udvikle den måde, som Microsoft ønsker os til Metro apps. Det bliver bare frustrerende, når de fortsætter med at ændre den ruddy ramme. (Hvis Xamarin kan holde konsekvente rammer på iOS/Android/etc, hvorfor kan det ikke være MS på deres eget OS!)