I use HoloLens 2 as a client and my unity server on my PC. (More discussion about this: How can I read byte array coming from server in UWP app?) I lost my debug await reader1.LoadAsync(256);. I tried everything to get my stream data but I couldn't. I don't want to const value for the buffer I need the exact stream size for the buffer. I tested this and it works only and only if the buffer size and data stream size is equal. Or you can suggest me other approaches?
// Create the StreamSocket and establish a connection to the server.
using (var streamSocket = new Windows.Networking.Sockets.StreamSocket())
{
// The server hostname that we will be establishing a connection to.
var hostName = new Windows.Networking.HostName(host);
// client is trying to connect...
await streamSocket.ConnectAsync(hostName, port);
// client connected!
// Read data from the server.
using (Windows.Storage.Streams.IInputStream inputStream = streamSocket.InputStream)
{
using (var reader1 = new Windows.Storage.Streams.DataReader(inputStream))
{
reader1.InputStreamOptions = Windows.Storage.Streams.InputStreamOptions.ReadAhead;
reader1.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
reader1.ByteOrder = Windows.Storage.Streams.ByteOrder.LittleEndian;
// Should be in stream size !!!
await reader1.LoadAsync(256);
while (reader1.UnconsumedBufferLength > 0)
{
var bytes1 = new byte[reader1.UnconsumedBufferLength];
reader1.ReadBytes(bytes1);
// Handle byte array internally!
HandleData(bytes1);
await reader1.LoadAsync(256);
}
reader1.DetachStream();
}
}
}
// close socket
}
catch (Exception ex)
{
Windows.Networking.Sockets.SocketErrorStatus webErrorStatus = Windows.Networking.Sockets.SocketError.GetStatus(ex.GetBaseException().HResult);
}
How can I get Windows.Storage.Streams.IInputStream inputStream length?
IInputStream of SteamSocket can't be seek. So we can't get length of steam, That's why we need set a buffer to load input stream all the time until the stream finished.
I checked code above, if you have set InputStreamOptions as ReadAhead. It will do the next step, when the 256 buffer fills up, please try to InputStreamOptions as Partial.
reader1.InputStreamOptions = InputStreamOptions.ReadAhead;
Update
If you want to get length of current message before load it, we suggest you write the message length as message header into to the steam.
For example
string stringToSend = "PC client uses System.Net.Sockets.TcpClient, System.Net.Sockets.NetworkStream but UWP (HoloLens) uses Windows.Networking.Sockets.StreamSocket.";
var bytes = Encoding.UTF8.GetBytes(stringToSend);
writer.WriteInt32(bytes.Length);
writer.WriteBytes(bytes);
Receive client
while (true)
{
// Read first 4 bytes (length of the subsequent string).
uint sizeFieldCount = await reader.LoadAsync(sizeof(uint));
if (sizeFieldCount != sizeof(uint))
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Read the string.
int bytesLength = reader.ReadInt32();
uint Actualbytelength = await reader.LoadAsync((uint)bytesLength);
if (Actualbytelength != bytesLength)
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Display the string on the screen. The event is invoked on a non-UI thread, so we need to marshal
// the text back to the UI thread.
var bytes = new byte[Actualbytelength];
reader.ReadBytes(bytes);
}
Hi Nico sorry for the late update. I tried everything but TCP is nearly impossible for HoloLens with UWP app. So I tried UDP and it works perfectly (https://github.com/mbaytas/HoloLensUDP). I hope Microsoft put a TCP example for HoloLens 1 and 2 in near future.
Related
The old version of this question got too long so by the end of numerous attemts to solve this issue I came up that all can be taken down to a simple question. Why does this produce a SystemObjectDisposed.
private async void PickPhotoButton_OnClicked(object sender, EventArgs e)
{
_globalStream = await DependencyService.Get<IPicturePicker>
().GetImageStreamAsync();
_globalArray = StreamToByteArray(_globalStream);
var gal = new GalleryResource()
{
Pic = _globalArray
};
MemoryObjects.CurrentGallery = gal;
var ctr = HelperMethods.GetInstance<GalleryController>();
await ctr.Post();
}
public byte[] StreamToByteArray(Stream input)
{
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
}
The stream arrives from the native side, I turn it into a byte array and pass it into my repository. Everyting work with a dummy byte array so something is wrong with the stream object that possibly gets closed or disposed at point.
The exception is thrown in the repository at this point:
var response = await _client.PostAsync(endPoint, _repService.ConvertObjectToStringContent(obj));
ConvertObjectToStringContent(obj) - not this part of it. From here it actually returns with a value and the byte array is seen inside the debug ie. the byte array stay with a valid lenght all way through.
The only event that does take place when we do finish picking the photo from the library is the following:
void OnImagePickerFinishedPickingMedia(object sender,
UIImagePickerMediaPickedEventArgs args)
{
UIImage image = args.EditedImage ?? args.OriginalImage;
if (image != null)
{
// Convert UIImage to .NET Stream object
NSData data = image.AsJPEG(1);
Stream stream = data.AsStream();
// Set the Stream as the completion of the Task
taskCompletionSource.SetResult(stream);
}
else
{
taskCompletionSource.SetResult(null);
}
imagePicker.DismissModalViewController(true);
}
However it doesn't seem to dispose the stream and even if it did we already got a byte array from it.
Tried even doing this inside Native code
var client = new HttpClient();
var c = new MultipartFormDataContent();
c.Add(new StreamContent(image.AsJPEG(1).AsStream()));
var response = await client.PostAsync(Settings.EndPoint + "api/gallery/", c);
Same error.
I think your problem lies somewhere in this line _byteArray = ToByteArray(_array);
ToByteArray(stream) seems to return you the byte array maybe via conversion from a stream, and this stream might still have a reference to the bytearray. And it might have become disposed.
If it's inside this method, please post it, I wanna knowww
I'm not quite experienced enough to exactly tell what it is about, but maybe my suggestions will be hitting the right spot!
Btw your code looks real clean, I like it!
So, although this issue did come up in the first place with the CrossMedia plugin https://github.com/jamesmontemagno/MediaPlugin it did the same error.
However the error only comes up if you for instance pick a photo like this:
var _mediaFile = await CrossMedia.Current.PickPhotoAsync();
So, when I did this:
var _mediaFile = await CrossMedia.Current.PickPhotoAsync(new
Plugin.Media.Abstractions.PickMediaOptions
{
PhotoSize = Plugin.Media.Abstractions.PhotoSize.Small,
CompressionQuality = 90,
});
The error went away. No idea why.
We use the Windows IoT to connect to another application in network with TCPClient. All works now, except one point which confuse us. When we unplug the network cable from our raspberry Pi (I guess version 2) the Startup application with a GUI is immediately stopped. When we plug in the cable back, after a little time the Startup app is started again.
We use VS2017 to build arm UWP app.
Any idea how to prevent stop of application?
As without your source code,I am not sure what cause your problem.I have tested following pieces codes in raspberry Pi 3, when i unplug the network cable, the application with UI does not stop.In foreground task,asynchronous method is recommend, please reference Asynchronous programming.
Server:
TcpListener tcpListener = new TcpListener(IPAddress.Any, 6550);
tcpListener.Start();
byte[] buffer = new byte[1024];
String data = string.Empty;
while (true)
{
var client = await tcpListener.AcceptTcpClientAsync();
var stream = client.GetStream();
int i = 0;
// Loop to receive all the data sent by the client.
do
{
i = await stream.ReadAsync(buffer, 0, buffer.Length);
if(i > 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(buffer, 0, i);
//Display received message
txtRServerContent.Text = data;
}
} while (i > 0);
}
Client:
TcpClient tcpClient = new TcpClient(AddressFamily.InterNetwork);
await tcpClient.ConnectAsync(IPAddress.Parse("10.168.197.66"), 14400);
// Get a client stream for reading and writing.
// Stream stream = client.GetStream();
NetworkStream stream = tcpClient.GetStream();
// Buffer to store the response bytes.
byte[] data = new byte[1024];
// String to store the response ASCII representation.
String responseData = String.Empty;
int count = 0;
while (true)
{
// Read the first batch of the TcpServer response bytes.
count = await stream.ReadAsync(data, 0, data.Length);
if (count > 0)
{
responseData = System.Text.Encoding.ASCII.GetString(data, 0, count);
//Dispaly responsed message
txtClientContent.Text = responseData;
}
}
It seems that this issue only occur if debugger is attached. For now this topic is closed.
Here is my server side code that writes the data to client.
try
{
IPHostEntry addr = Dns.GetHostEntry(Dns.GetHostName());
IPAddress localIP =
addr.AddressList.Where(x =>
x.AddressFamily ==AddressFamily.InterNetwork).FirstOrDefault();
//Console.WriteLine(localIP);
listener = new TcpListener(localIP,2055);
listener.Start();
while (true)
{
s = listener.AcceptSocket();
stream = new NetworkStream(s);
strread = new StreamReader(stream);
strwrite = new StreamWriter(stream);
//strwrite.AutoFlush = true;
strwrite.WriteLine("Hello");
// string recvmessage = strread.ReadLine();
// Console.WriteLine(recvmessage);
// if(string.IsNullOrEmpty(recvmessage))
// strwrite.WriteLine("Idealist");
}
}
The Server side code writes data to client only when autoflush is given true.Can anyone please explain
Because it gets flushed automatically, of course. If you look at the Javadoc you'll see that autoflush happens when the data contains a newline. If you don't set this, the data doesn't get flushed until you call flush() yourself, or close the OutputStream or Writer yourself.
I have a WebMatrix 3 (same as ASP.NET) web page that opens a socket to a server process running on an Azure hosted Linux VM that listens on a TCP connection for clients. The Linux VM server process is mine too. When I run the WebMatrix 3/ASP.NET web site locally from my home PC using a local copy of IIS it works fine (local publish). When I publish my web site to the web and it is now running on Azure I get the Exception:
An attempt was made to access a socket in a way forbidden by its access permissions
What is really confusing is that the error occurs when I read from the socket but oddly enough not when I connect to it or write to it before-hand. I know this because the Exception message is adorned with the current operation, and that is set to:
Waiting for and then reading the response from the ChatScript server.
You can see this line in the code below. Is there something going on with the Azure side that could be blocking reads from the TCP connection to the Linux VM, yet allows connections to that VM and even sends? I say "even sends" because as you can see from the code below, I immediately send a message to the Linux VM process before I try to read from that connection.
public static string readChatScriptMessage(NetworkStream myNetworkStream)
{
if (myNetworkStream == null)
throw new ArgumentNullException("(readChatScriptMessage) The network stream is unassigned.");
StringBuilder myCompleteMessage = new StringBuilder();
// Check to see if this NetworkStream is readable.
if (myNetworkStream.CanRead)
{
byte[] myReadBuffer = new byte[1024];
int numberOfBytesRead = 0;
// Incoming message may be larger than the buffer size.
do
{
numberOfBytesRead = myNetworkStream.Read(myReadBuffer, 0, myReadBuffer.Length);
myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));
}
while (myNetworkStream.DataAvailable);
}
else
{
if (myNetworkStream == null)
throw new InvalidOperationException("(readChatScriptMessage) The network stream is unassigned.");
}
// Print out the received message to the console.
return myCompleteMessage.ToString();
} // public static string readChatScriptMessage(NetworkStream myNetworkStream)
// Lookup the IP address for our chatscript server. (Cache this value
// in a later build since GetHostEntry() is reportedly a slow call.)
ipAddress = Dns.GetHostEntry("myazureapp.cloudapp.net").AddressList[0];
strCurrentOperation = "Validating URL arguments (parameters).";
// LoginName, is mandatory.
strLoginName = checkForValidURLArgument("LoginName", true);
// BotName, is optional.
strBotName = checkForValidURLArgument("BotName", false);
// User message (chat input), is optional. But remember,
// only send a blank message to start a new session
// with ChatScript! After that, send the user's input
// each time.
strMessage = checkForValidURLArgument("Message", false);
strCurrentOperation = "Connecting to Linux VM TCP server.";
// OK, we're good to go. We have the 3 URL arguments we were expecting.
// Connect to the ChatScript server.
tcpCli.Connect(ipAddress, 1024);
strCurrentOperation = "Opening the stream with the server.";
// Open the stream
streamChatScript = tcpCli.GetStream();
StreamReader sr = new StreamReader(streamChatScript);
BinaryWriter sw = new BinaryWriter(streamChatScript);
// Create a message to send to the server, using the URL argument values
// passed to us.
ChatMessage cm = new ChatMessage(strLoginName, strBotName, strMessage);
strCurrentOperation = "Sending the desired chat message to the server.";
// Send the message to the chat server.
string strSendChatMsg = cm.ToString();
// Translate the passed message into ASCII and store it as a Byte array.
Byte[] data = System.Text.Encoding.ASCII.GetBytes(strSendChatMsg);
for (int i = 0; i < strSendChatMsg.Length; i++)
{
data[i] = (byte)strSendChatMsg[i];
}
// Send the chat message.
streamChatScript.Write(data, 0, data.Length);
strCurrentOperation = "Waiting for and then reading the response from the server.";
strResponseMsg = ChatMessage.readChatScriptMessage(streamChatScript);
For my current project, I need to request XML data over a tcp/ip socket connection. For this, I am using the TcpClient class:
Dim client As New TcpClient()
client.Connect(server, port)
Dim stream As NetworkStream = client.GetStream()
stream.Write(request)
stream.Read(buffer, 0, buffer.length)
// Output buffer and return results...
Now this works fine and dandy for small responses. However, when I start receiving larger blocks of data, it appears that the data gets pushed over the socket connection in bursts. When this happens, the stream.Read call only reads the first burst, and thus I miss out on the rest of the response.
What's the best way to handle this issue? Initially I tried to just loop until I had a valid XML document, but I found that in between stream.Read calls the underlying stream would sometimes get shut down and I would miss out on the last portion of the data.
You create a loop for reading.
Stream.Read returns int for the bytes it read so far, or 0 if the end of stream is reached.
So, its like:
int bytes_read = 0;
while (bytes_read < buffer.Length)
bytes_read += stream.Read(buffer, bytes_read, buffer.length - bytes_read);
EDIT: now, the question is how you determine the size of the buffer. If your server first sends the size, that's ok, you can use the above snippet. But if you have to read until the server closes the connection, then you have to use try/catch (which is good idea even if you know the size), and use bytes_read to determine what you received.
int bytes_read = 0;
try
{
int i = 0;
while ( 0 < (i = stream.Read(buffer, bytes_read, buffer.Length - bytes_read) )
bytes_read += i;
}
catch (Exception e)
{
//recover
}
finally
{
if (stream != null)
stream.Close();
}
Read is not guaranteed to fully read the stream. It returns the number of actual bytes read and 0 if there are no more bytes to read. You should keep looping to read all of the data out of the stream.
I strongly advice you to try WCF for such tasks. It gives you, after a not so steep learning curve, many benefits over raw socket communications.
For the task at hand, I agree with the preceeding answers, you should use a loop and dynamically allocate memory as needed.
This is a possible way to do that and get in "response" the response string. If you need the byte array, just save ms.ToArray().
string response;
TcpClient client = new TcpClient();
client.Connect(server, port);
using (NetworkStream ns = c.GetStream())
using (MemoryStream ms = new MemoryStream())
{
ns.Write(request);
byte[] buffer = new byte[512];
int bytes = 0;
while(ns.DataAvailable)
{
bytes = ns.Read(buffer,0, buffer.Length);
ms.Write(buffer, 0, bytes);
}
response = Encoding.ASCII.GetString(ms.ToArray());
}