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());
}
Related
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.
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.
I actually work on a web application (ASP.NET MVC) which sends command to a thick-client application (that I can not modify) on a local network in TCP/IP. I need that the thick-client application respond to my command or give me information, so the connection need to be open all the time. I create a loop that checks the server response like that :
public static async void getMessage()
{
while(true)
{
// Buffer to store the response bytes.
Byte[] data = new Byte[100000];
// String to store the response ASCII representation.
String responseData = String.Empty;
// Read the first batch of the TcpServer response bytes.
Int32 bytes = await stream.ReadAsync(data, 0, data.Length);
if (bytes == 0) continue;
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
Debug.Write(responseData);
await stream.ReadAsync(data, 0, data.Length);
}
}
That's actually working, and I received the response from the server, but the browser is "waiting for localhost" all the time, so is there a better way to do that, without the loop on the browser?
Thank you a lot !
This sounds like a really good case for a websocket. In an MVC app, you'd typically use SignalR: http://www.asp.net/signalr
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'm attempting to write a Java Servlet to receive binary data requests and reply to them, using HttpServletRequest.getOutputStream() and HttpServletResponse.getInputStream(). This is for a project which involves having a request sent by a Silverlight client to which this servlet responds to through an HTTP POST connection. For the time being, to test the Servlet I'm implementing a client in Java which I'm more familiar with than Silverlight.
The problem is that in my test project I send the data from a Servlet client as a byte array and expect to receive a byte array with the same length -- only it doesn't, and instead I'm getting a single byte. Therefore I'm posting here the relevant code snippets in the hopes that you might point me where I'm doing wrong and hopefully provide relevant bibliography to help me further.
So here goes.
The client servlet handles POST requests from a very simple HTML page with a form which I use as front-end. I'm not too worried about using JSP etc, instead I'm focused on making the inter-Servlet communication work.
// client HttpServlet invokes this method from doPost(request,response)
private void process(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String firstName = (String) request.getParameter("firstname");
String lastName = (String) request.getParameter("lastname");
String xmlRequest = "<MyRequest><Person><Name Firstname=\""+firstName+"\" Lastname=\""+lastName+"\" /></Person></MyRequest>";
OutputStream writer = null;
InputStream reader = null;
try {
URL url = new URL("http://localhost:8080/project/Server");
URLConnection conn = url.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
writer = conn.getOutputStream();
byte[] baXml = xmlRequest.getBytes("UTF-8");
writer.write(baXml, 0,baXml.length);
writer.flush();
// perhaps I should be waiting here? how?
reader = conn.getInputStream();
int available = reader.available();
byte[] data = new byte[available];
reader.read(data,0,available);
String xmlResponse = new String(data,"UTF-8");
PrintWriter print = response.getWriter();
print.write("<html><body>Response:<br/><pre>");
print.write(xmlResponse);
print.write("</pre></body></html>");
print.close();
} finally {
if(writer!=null)
writer.close();
if(reader!=null)
reader.close();
}
}
The server servlet handles HTTP POST requests. This is done by receiving requests the requests from a client Servlet for testing purposes above, but in the future I intend to use it for clients in other languages (specifically, Silverlight).
// server HttpServlet invokes this method from doPost(request,response)
private void process(HttpServletRequest request, HttpServetResponse response)
throws ServletException, IOException {
ServletInputStream sis = null;
try {
sis = request.getInputStream();
// maybe I should be using a BufferedInputStream
// instead of the InputStream directly?
int available = sis.available();
byte[] input = new byte[available];
int readBytes = sis.read(input,0,available);
if(readBytes!=available) {
throw new ServletException("Oops! readBytes!=availableBytes");
}
// I ONLY GET 1 BYTE OF DATA !!!
// It's the first byte of the client message, a '<'.
String msg = "Read "+readBytes+" bytes of "
+available+" available from request InputStream.";
System.err.println("Server.process(HttpServletRequest,HttpServletResponse): "+msg);
String xmlReply = "<Reply><Message>"+msg+"</Message></Reply>";
byte[] data = xmlReply.getBytes("UTF-8");
ServletOutputStream sos = response.getOutputStream();
sos.write(data, 0,data.length);
sos.flush();
sos.close();
} finally {
if(sis!=null)
sis.close();
}
}
I have been sticking to byte arrays instead of using BufferInputStreams so far because I've not decided yet if I'll be using e.g. Base64-encoded strings to transmit data or if I'll be sending binary data as-is.
Thank you in advance.
To copy input stream to output stream use the standard way:
InputStream is=request.getInputStream();
OutputStream os=response.getOutputStream();
byte[] buf = new byte[1000];
for (int nChunk = is.read(buf); nChunk!=-1; nChunk = is.read(buf))
{
os.write(buf, 0, nChunk);
}
The one thing I can think of is that you are reading only request.getInputStream().available() bytes, then deciding that you have had everything. According to the documentation, available() will return the number of bytes that can be read without blocking, but I don't see any mention of whether this is actually guaranteed to be the entire content of the input stream, so am inclined to assume that no such guarantees are made.
I'm not sure how to best find out when there is no more data (maybe Content-Length in the request can help?) without risking blocking indefinitely at EOF, but I would try looping until having read all the data from the input stream. To test that theory, you could always scan the input for a known pattern that occurs further into the stream, maybe a > matching the initial < that you are getting.