An abstraction over the socket helper classes of .NET and WinRT, providing a PCL-friendly socket library for projects targeting Xamarin iOS/Android/Forms, Xamarin.Mac/MonoMac, Windows Phone 8/8.1, Windows Store, and Windows Desktop. It allows you to write socket code in your PCL, simplifying cross-platform peer-to-peer communications significantly as well as enabling code sharing for many other use cases.
TcpSocketListener
and TcpSocketClient
each expose ReadStream
and WriteStream
properties of type System.IO.Stream
for receiving and sending data. UdpReceiver
, UdpClient
and UdpMulticastClient
expose a MessageReceived
event and a Send()
method due to the nature of the transport and the underlying implementations.
var listenPort = 11000;
var listener = new TcpSocketListener();
// when we get connections, read bytes until we get -1 (eof)
listener.ConnectionReceived += async (sender, args) =>
{
var client = args.SocketClient;
while (true)
{
// read from the 'ReadStream' property of the socket client to receive data
var nextByte = await Task.Run(()=> client.ReadStream.ReadByte());
Debug.Write(nextByte);
}
};
// bind to the listen port across all interfaces
await listener.StartListeningAsync(listenPort);
var address = "127.0.0.1";
var port = 11000;
var r = new Random();
var client = new TcpSocketClient();
await client.ConnectAsync(address, port);
// we're connected!
for (int i = 0; i<5; i++)
{
// write to the 'WriteStream' property of the socket client to send data
var nextByte = (byte) r.Next(0,254);
client.WriteStream.WriteByte(nextByte);
await client.WriteStream.FlushAsync();
// wait a little before sending the next bit of data
await Task.Delay(TimeSpan.FromMilliseconds(500));
}
await client.DisconnectAsync();
var listenPort = 11011;
var receiver = new UdpSocketReceiver();
receiver.MessageReceived += (sender, args) =>
{
// get the remote endpoint details and convert the received data into a string
var from = String.Format("{0}:{1}", args.RemoteAddress, args.RemotePort);
var data = Encoding.UTF8.GetString(args.ByteData, 0, args.ByteData.Length);
Debug.WriteLine("{0} - {1}", from, data);
};
// listen for udp traffic on listenPort
await receiver.StartListeningAsync(listenPort);
var port = 11011;
var address = "127.0.0.1";
var client = new UdpSocketClient();
// convert our greeting message into a byte array
var msg = "HELLO WORLD";
var msgBytes = Encoding.UTF8.GetBytes(msg);
// send to address:port,
// no guarantee that anyone is there
// or that the message is delivered.
await client.SendToAsync(msgBytes, address, port);
var port = 11811;
var address = "239.192.0.1"; // must be a valid multicast address
// typical instantiation
var receiver = new UdpSocketMulticastClient();
receiver.TTL = 5;
receiver.MessageReceived += (sender, args) =>
{
var from = String.Format("{0}:{1}", args.RemoteAddress, args.RemotePort);
var data = Encoding.UTF8.GetString(args.ByteData, 0, args.ByteData.Length);
Debug.WriteLine("{0} - {1}", from, data);
};
// join the multicast address:port
await receiver.JoinMulticastGroupAsync(address, port);
var msg = "HELLO MULTIVERSE";
var msgBytes = Encoding.UTF8.GetBytes(msg);
// send a message that will be received by all listening in
// the same multicast group.
await receiver.SendMulticastAsync(msgBytes);
For a majority of mobile use cases, binding to all interfaces is a good approach. However, when working with multicast or on a machine with many interfaces, it may be useful to bind to a specific interface. The TcpSocketListener
, UdpSocketReceiver
and UdpSocketMulitcastClient
classes include an optional CommsInterface
parameter on their listen/join methods, allowing them to be bound to a specific interface only. If this parameter is not specified, all interfaces will be bound. CommsInterface
has a static method GetAllInterfacesAsync
that can be used to enumerate the available interfaces.
// retrieve the list of interfaces from the device
var allInterfaces = await CommsInterface.GetAllInterfacesAsync();
// get the first interface with an ip address
var firstUsable = allInterfaces.FirstOrDefault(ci => ci.IsUsable);
if (firstUsable == null)
return; // no connected interfaces, too bad!
var listener = new TcpSocketListener();
await listener.StartListeningAsync(11000, firstUsable);
Console.WriteLine("Listening on interface with ip: {0}", firstUsable.IpAddress);
TcpSocketClient
supports TLS connections (server certificate only). Pass true
to the optional parameter useTls
on ConnectAsync
to enable secure communication.
- Xamarin.Mac Unified and MonoMac should work out of the box. For Xamarin.Mac Classic projects, after installing sockets-for-pcl you must manually alter the
<HintPath>
entries forSockets.Plugin
andSockets.Plugin.Abstractions
in your .csproj file, to replace references tonet45
withXamarin.Mac.Classic
. Without this, code interacting with portions of theSystem.Net
namespace not implemented in mono, including methods on some classes required byCommsInterface
, will fail. - On Windows Phone, you will require appropriate permissions in your app manifest. Depending on whether you are listening or sending, this could include a combination of
privateNetworkClientServer
,internetClient
and/orinternetClientServer
capabilities. - On Windows Phone/Store, there are restrictions regarding passing traffic over loopback between separate apps (i.e. no IPC)
- Binding to specific interfaces is not supported on Windows Phone 8.0 (8.1 is fine). All interfaces will be bound, even if a specific
CommsInterface
is provided.
Additional 'higher level' features will likely end up in the sockethelpers-for-pcl project mentioned earlier.