Reputation: 1566
I am using https://github.com/rdavisau/sockets-for-pcl in my PCL Xamarin app and trying to send/receive to a server on my network. Currently I am using Android (an actual phone) to test. I am able to successfully connect (I know this because my server shows the connection and the app throws no exceptions), but when I try to read or write, I receive
"Operation is not supported."
The server gives:
"Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host."
I have looked at other posts about this exception and it looks like a mismatch between IPv4 and IPv6. But I am not using localhost, I use the network IP so it should be fine? Specifically I host and connect at the same address (192.168.2.169 and port 207). Here is my code:
//Functions called by 2 buttons "Connect" and "Send"
//This works. I connect.
public async void SocketConnect(object sender, EventArgs args)
{
try
{
client = new TcpSocketClient();
await client.ConnectAsync(ipEntry.Text, int.Parse(portEntry.Text));
// we're connected!
connectBtn.Text = "Connected";
}
catch (Exception e)
{
var notificator = DependencyService.Get<IToastNotificator>();
bool tapped = await notificator.Notify(ToastNotificationType.Error,
"Error", e.Message, TimeSpan.FromSeconds(10));
}
}
public async void SocketSend(object sender, EventArgs args)
{
try
{
if(client==null)
{
var notificator = DependencyService.Get<IToastNotificator>();
bool tapped = await notificator.Notify(ToastNotificationType.Error,
"Error", "Connect First Please", TimeSpan.FromSeconds(10));
}
byte[] toSend = System.Text.Encoding.UTF8.GetBytes(toSendEntry.Text);
using (Stream s = client.WriteStream)
{
s.Write(toSend, 0, toSend.Length);
await s.FlushAsync();
} //Fails Here with Operation is not supported.
await Task.Delay(70);
using (Stream s = client.ReadStream)
{
if (s.Length > 0)
{
byte[] response = new byte[s.Length];
s.Read(response, 0, (int)s.Length);
responseFromServer.Text = response.ToString();
}
}
}
catch (Exception e)
{
var notificator = DependencyService.Get<IToastNotificator>();
bool tapped = await notificator.Notify(ToastNotificationType.Error,
"Error", e.Message, TimeSpan.FromSeconds(10));
}
}
I thought it might be because I am disposing the stream after, so I took out the using, but it still broke.
Upvotes: 0
Views: 728
Reputation: 7091
There are multiple issues with this code.
NotSupportedException
is probably this line: if (s.Length > 0)
. Length
property is most probably not supported on network streams.Length
worked, it would be useless, as it introduces a race condition: during the time between accessing Length
and calling Read
more data could arrive.Task.Delay
to wait for the server's response is a really bad idea. You should not be making assumptions on how fast the network is, especially on a mobile device!So, your reading code is wrong. Something like this should work:
var ms = new MemoryStream();
var buffer = new byte[1024]; // real size doesn't matter. too small will make
// your code slow. too big will just waste memory
for(;;)
{
var len = await s.ReadAsync(buffer, 0, buffer.Length);
if (len == -1)
break; // server closed the connection!
ms.Write(buffer, 0, len); // note that we use len here, as
// Read might have not used the whole buffer
}
var response = ms.ToArray();
This code reads all available data in the socket until the connection is closed. If the server is not supposed to close the connection, you need to prefix your data with length or use some kind of marker at the end. E.g. HTTP uses \r\n\r\n
to mark the end of headers.
Also note that Read
/ReadAsync
never returns 0. It will just wait for any data to arrive before returning. This is an important point and developers who are not familiar with Unix-style I/O often get confused here.
Upvotes: 1