Live FLV streaming in C# WebApi - c#

Currently I have a working live stream using webapi. By receiving a flv stream directly from ffmpeg and sending it straight to the client using PushStreamContent. This works perfectly fine if the webpage is already open when the stream starts. The issue is when I open another page or refresh this page you can no longer view the stream (the stream is still being sent to the client fine). I think it is due to something missing from the start of the stream but I am not sure what to do. Any pointers would be greatly appreciated.
Code for client reading stream
public class VideosController : ApiController
{
public HttpResponseMessage Get()
{
var response = Request.CreateResponse();
response.Content = new PushStreamContent(WriteToStream, new MediaTypeHeaderValue("video/x-flv"));
return response;
}
private async Task WriteToStream( Stream arg1, HttpContent arg2, TransportContext arg3 )
{
//I think metadata needs to be written here but not sure how
Startup.AddSubscriber( arg1 );
await Task.Yield();
}
}
Code for receiving stream and then sending to client
while (true)
{
bytes = new byte[8024000];
int bytesRec = handler.Receive(bytes);
foreach (var subscriber in Startup.Subscribers.ToList())
{
var theSubscriber = subscriber;
try
{
await theSubscriber.WriteAsync( bytes, 0, bytesRec );
}
catch
{
Startup.Subscribers.Remove(theSubscriber);
}
}
}

I've never used FLV or studied video formats closely
Most file formats are structured, especially video formats. They contain frames (i.e. a complete or partial screen shots depending on the compression format).
You should be really lucky if you manage to hit a specific frame when you start streaming to the new subscriber. Hence when they start receiving the stream they cannot identify the format as frame is partial.
You can read more FLV frames in wikipedia article. This is most likely your problem.
A simple attempt would be to try to save the initial header that you receive from the streaming server when the first subscriber connects.
Something like:
static byte _header = new byte[9]; //signature, version, flags, headerSize
public void YourStreamMethod()
{
int bytesRec = handler.Receive(bytes);
if (!_headerIsStored)
{
//store header
Buffer.BlockCopy(bytes, 0, _header, 0, 9);
_headerIsStored = true;
}
}
.. which allows you to send the header to the next connecting subscriber:
private async Task WriteToStream( Stream arg1, HttpContent arg2, TransportContext arg3 )
{
// send the FLV header
arg1.Write(_header, 0, 9);
Startup.AddSubscriber( arg1 );
await Task.Yield();
}
Once done, pray that the receiver will ignore partial frames. If it doesn't you need to analyze the stream to identify where the next frame is.
To do that you need to do something like this:
Create a BytesLeftToNextFrame variable.
Store the received packet header in a buffer
Convert the "Payload size" bits to an int
Reset the BytesLeftToNextFrame to the parsed value
Countdown until the next time you should read a header.
Finally, when a new client connects, do not start streaming until you know that the next frame arrives.
Pseudo code:
var bytesLeftToNextFrame = 0;
while (true)
{
bytes = new byte[8024000];
int bytesRec = handler.Receive(bytes);
foreach (var subscriber in Startup.Subscribers.ToList())
{
var theSubscriber = subscriber;
try
{
if (subscriber.IsNew && bytesLeftToNextFrame < bytesRec)
{
//start from the index where the new frame starts
await theSubscriber.WriteAsync( bytes, bytesLeftToNextFrame, bytesRec - bytesLeftToNextFrame);
subscriber.IsNew = false;
}
else
{
//send everything, since we've already in streaming mode
await theSubscriber.WriteAsync( bytes, 0, bytesRec );
}
}
catch
{
Startup.Subscribers.Remove(theSubscriber);
}
}
//TODO: check if the current frame is done
// then parse the next header and reset the counter.
}

I'm not a expert in streaming, but looks like you should close stream then all data will be writed
await theSubscriber.WriteAsync( bytes, 0, bytesRec );
Like it mentions in WebAPI StreamContent vs PushStreamContent
{
// After save we close the stream to signal that we are done writing.
xDoc.Save(stream);
stream.Close();
}

I LIKE THIS CODE BECAUSE IT DEMONSTRATES A FUNDAMENTAL ERROR when dealing with async programming
while (true)
{
}
this is a synced loop, that loops itself as fast as possible.. every second it can execute thousands of times (depending on availabe software and hardware resources)
await theSubscriber.WriteAsync( bytes, 0, bytesRec );
this is an async command (if that wasn't clear enough) that execute in a DIFFERENT thread (while loop representes the main thread execution)
now... in order to make the while loop to wait to the async command we use await... sounds good (or else the while loop will execute thousands of times, executing countless async commands)
BUT because the loop (of subscribers) need to transmit the stream for all subscribers simulatanly it get stucked by the await keyword
THAT IS WHY RELOAD / NEW SUBSCRIBER FREEZE THE WHOLE THING (new connection = new subscriber)
conclusion: the entire for loop should be inside a Task. the Task need to wait until the server send the stream to all subscribers. ONLY THEN it should continue to the while loop with ContinueWith (that is why it called like that, right?)
so... the write command need to get execute without await keyword
theSubscriber.WriteAsync
the foreach loop should use a task that continue with the while loop after it is done

Related

How to close the TcpClient with a timeout?

I'm using .NET Core and want to send messages via TCP. For this I'm using the TcpClient class and created a custom service. This solution works for now, not sure if I can improve it
class MyTcpService : IMyTcpService
{
private readonly TcpClient tcpClient = new TcpClient();
public async Task Send(byte[] bytesToSend)
{
if (!tcpClient.Connected) // Check if client was closed before
{
await tcpClient.ConnectAsync("127.0.0.1", 5000); // Read values from config
}
NetworkStream networkStream = tcpClient.GetStream();
// Send the message
await networkStream.WriteAsync(bytesToSend, 0, bytesToSend.Length);
// Read the response
byte[] responseBuffer = new byte[1024]; // Read value from config
int amountOfResponseBytes = await networkStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
string responseMessage = Encoding.ASCII.GetString(responseBuffer, 0, amountOfResponseBytes);
// Close the connection with a timeout if true
if (true) // Read value from config
{
networkStream.Close(1000); // Read value from config
tcpClient.Close();
}
// Handle the response message here
// ...
}
}
I want to inject IMyTcpService as a transient service. I would like to know how to close the client with a timeout? The Socket class has a Close method accepting a timeout parameter
https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.close?view=netcore-3.1#System_Net_Sockets_Socket_Close_System_Int32_
but I'm not able to find an equivalent for the TcpClient just for its NetworkStream.
At the moment you are awaiting both the WriteAsync and ReadAsync calls. Because of this the timeout in your call to networkStream.Close(1000) should have no impact and the connection will always close immediately as no data is waiting to be sent/received. For neither the write or read you have specified a timeout, which means they won't return until data has finished being transferred.
I would like to know how to close the client with a timeout?
It's not clear why you want this or what you want to achieve with this. TcpClient is simply a wrapper around a NetworkStream which in turn wraps around a Socket. So handling timeouts both on the TcpClient and the NetworkStream doesn't make much sense.
Resource management:
In your current example I would first of all advise you to keep the TcpClient inside the Send method instead of a class field. If you don't need to use the TcpClient in other places (which expect you don't since you are closing it in the Send function) you should narrow it's scope for easier resource management. While doing that I'd suggest you make use of the using statement to avoid forgetting to properly dispose your resources. This applies to all types that implement the IDisposable interface (of course there are exceptions to this).
Handling timeout:
To handle timeouts in this snippet of code you have shared I suggest you configure the timeout on the write and read operations rather than the close operation, since your code is very sequential. An example of what that could look like:
class MyTcpService : IMyTcpService
{
public async Task Send(byte[] bytesToSend)
{
string responseMessage;
using (tcpClient = new TcpClient())
{
if (shouldUseTimeout) // From config
{
tcpClient.ReceiveTimeout = 1000; // From config
tcpClient.SendTimeout = 1000; // From config
}
await tcpClient.ConnectAsync("127.0.0.1", 5000); // Read values from config
NetworkStream networkStream = tcpClient.GetStream();
// Send the message
await networkStream.WriteAsync(bytesToSend, 0, bytesToSend.Length);
// Read the response
byte[] responseBuffer = new byte[1024]; // Read value from config
int amountOfResponseBytes = await networkStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
responseMessage = Encoding.ASCII.GetString(responseBuffer, 0, amountOfResponseBytes);
}
// The tcpClient is now properly closed and disposed of
// Handle the response message here
// responseMessage...
}
}
Update in response to the comment 13/10/2020:
hey, thanks for your reply :) I tried to improve your answer, what do you think about this snippet? pastebin.com/7kTvtTv2
From your https://pastebin.com/7kTvtTv2:
public async Task<string> Send(byte[] messageToSend)
{
string responseMessage;
using (TcpClient tcpClient = new TcpClient())
{
await tcpClient.ConnectAsync("127.0.0.1", 5000); // From config
NetworkStream networkStream = tcpClient.GetStream();
await networkStream.WriteAsync(messageToSend, 0, messageToSend.Length);
await networkStream.FlushAsync();
tcpClient.Client.Shutdown(SocketShutdown.Send); // shutdown gracefully
byte[] responseBuffer = new byte[256]; // This can be of any size
StringBuilder stringBuilder = new StringBuilder();
int amountOfResponseBytes;
do
{
amountOfResponseBytes = await networkStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
string responseData = Encoding.ASCII.GetString(responseBuffer, 0, amountOfResponseBytes);
stringBuilder.Append(responseData);
} while (amountOfResponseBytes > 0);
responseMessage = stringBuilder.ToString();
}
return responseMessage;
}
Looks pretty good to me. Only some minor comments:
await networkStream.FlushAsync() - it seems like this should be unnecessary when I look at the remarks for the Flush method but I haven't tested it:
The Flush method implements the Stream.Flush method; however, because NetworkStream is not buffered, it has no effect on network streams. Calling the Flush method does not throw an exception
tcpClient.Client.Shutdown(SocketShutdown.Send) - this method simply tells the Socket to no longer allow writing/sending data. Since your TcpClient only stays within the Send method and therefore not being shared anywhere it seems a little unnecessary too. I think the Shutdown method is mostly relevant if you don't have complete control over when the Socket is used.
do { ... } while (...) - looks good to me. Just remember the responseBuffer need to be a multiple of 8 if you are dealing with ASCII characters so you don't end up trying to decode a partial character.
Where did the timeout handling go? Did you forget to add timeout handling or is it not relevant anymore? Currently, if you have a lot of data to send or receive or the network is just slow, the WriteAsync and ReadAsync calls may potentially take a long time.

Socket Constant Streaming Data

I have a server application but am getting some intermittent timeouts. I want to start writing back to the client immediately and then defer the rest of my write until my payload is ready. However, nothing I've seen in TcpClient or NetworkStream supports active streaming. In my own test bed, any write to the stream is in isolation, even the Begin/End methods.
For example, in my client side write/read loop, the following server code only sends back a single time entry instead of two.
static void Main(string[] args)
{
var listener = TcpListener.Create(2345);
listener.Start();
while (true)
{
var client = listener.AcceptTcpClientAsync().Result;
ThreadPool.QueueUserWorkItem(async state =>
{
try
{
await HandleClient(state);
}
catch (SocketException error) when (error.Message.Contains("existing connection was forcibly closed"))
{
// do nothing
}
}, client);
}
}
private static async Task HandleClient(object state)
{
var client = (TcpClient) state;
var stream = client.GetStream();
while (true)
{
var readBuffer = new byte[4096];
var requestSize = await stream.ReadAsync(readBuffer, 0, readBuffer.Length);
var requestString = Encoding.ASCII.GetString(readBuffer, 0, requestSize);
Console.WriteLine($"[{DateTime.Now.ToLongTimeString()}] Received: {requestString}");
if (requestString == "quit")
{
break;
}
var responseString = DateTime.Now.ToLongTimeString();
var writeBuffer = Encoding.ASCII.GetBytes(responseString);
stream.BeginWrite(writeBuffer, 0, writeBuffer.Length, DelayedWriteCallback, stream);
}
client.Close();
client.Dispose();
}
private static void DelayedWriteCallback(IAsyncResult ar)
{
var stream = (NetworkStream) ar.AsyncState;
Thread.Sleep(5000);
var writeString = Encoding.ASCII.GetBytes(DateTime.Now.ToLongTimeString());
stream.Write(writeString, 0, writeString.Length);
stream.Flush();
stream.EndWrite(ar);
}
How do I do this?
TCP is a streaming protocol, which just means that it will transport any data you send through it and make sure it is received in the correct order on the other side.
That data might be a constant series of bytes, or many seconds of nothing followed by a single byte. TCP does not care, it´s just the pipe for.
This is made clear by the fact that every socket Send and Receive method takes a buffer with a specific size as a parameter (same goes for NetworkStream). The "streaming" of useful data is up to the application, as is the reception and interpretation of the data.
If you want to send data continuously, you need a send() loop that will pump bytes into the socket as soon as your server has them. If you need to read data continuously, you need a loop that will receive() chunks of bytes continuously and make sense of them.

What is the fastest possible way to read a serial port in .net?

I need a serial port program to read data coming in at 4800 baud. Right now I have a simulator sending 15 lines of data every second. The output of it seems to get "behind" and can't keep up with the speed/amount of data coming in.
I have tried using ReadLine() with a DataReceieved event, which did not seem to be reliable, and now I am using an async method with serialPort.BaseStream.ReadAsync:
okToReadPort = true;
Task readTask = new Task(startAsyncRead);
readTask.Start();
//this method starts the async read process and the "nmeaList" is what
// is used by the other thread to display data
public async void startAsyncRead()
{
while (okToReadPort)
{
Task<string> task = ReadLineAsync(serialPort);
string line = await task;
NMEAMsg tempMsg = new NMEAMsg(line);
if (tempMsg.sentenceType != null)
{
nmeaList[tempMsg.sentenceType] = tempMsg;
}
}
public static async Task<string> ReadLineAsync(
this SerialPort serialPort)
{
// Console.WriteLine("Entering ReadLineAsync()...");
byte[] buffer = new byte[1];
string ret = string.Empty;
while (true)
{
await serialPort.BaseStream.ReadAsync(buffer, 0, 1);
ret += serialPort.Encoding.GetString(buffer);
if (ret.EndsWith(serialPort.NewLine))
return ret.Substring(0, ret.Length - serialPort.NewLine.Length);
}
}
This still seems inefficient, does anyone know of a better way to ensure that every piece of data is read from the port and accounted for?
Generally speaking, your issue is that you are performing IO synchronously with data processing. It doesn't help that your data processing is relatively expensive (string concatenation).
To fix the general problem, when you read a byte put it into a processing buffer (BlockingCollection works great here as it solves Producer/Consumer) and have another thread read from the buffer. That way the serial port can immediately begin reading again instead of waiting for your processing to finish.
As a side note, you would likely see a benefit by using StringBuilder in your code instead of string concatenation. You should still process via queue though.

Problems with asynchronous functions with TcpListener and TcpClient, function not waiting on await keyword

I am new to asynchronous socket programming, and I am having problems with my asynchronous functions.
I am trying to create a chat program that uses Windows Forms for the client, and a console application for the server.
Here is the code for handling connections on my server:
public async void StartServer()
{
TcpListener listener = new TcpListener(_ip, _port);
listener.Start();
Console.WriteLine("Server is running on IP: {0} Port: {1}", _ip.ToString(), _port);
while (true)
{
try
{
TcpClient client = await listener.AcceptTcpClientAsync();
HandleConnections(client);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
private async void HandleConnections(TcpClient client)
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[256];
string message = null;
int x;
while(stream.DataAvailable)
{
x = await stream.ReadAsync(buffer, 0, buffer.Length);
message += Encoding.ASCII.GetString(buffer);
}
message = message.Replace('\0', ' ');
message = message.Trim();
Console.WriteLine("Message Recieved: " + message);
byte[] bytes = Encoding.ASCII.GetBytes(message);
await stream.WriteAsync(bytes, 0, bytes.Length);
stream.Close();
}
And here is the code for the client program connecting to the server:
private async void ConnectButton_Click(object sender, EventArgs e)
{
IPAddress address = IPAddress.Parse(IPInput.Text);
client = new TcpClient();
await client.ConnectAsync(address, 12345);
NetworkStream stream = client.GetStream();
string message = UsernameInput.Text + " Connected!";
Task<int> sendTask = SendMessage(stream, message);
int sendComp = await sendTask;
Task<string> recieveTask = RecieveMessage(stream);
string recieved = await recieveTask;
stream.Close();
ChatText.AppendText(recieved);
}
private async Task<int> SendMessage(NetworkStream stream, string message)
{
byte[] bytes = Encoding.ASCII.GetBytes(message + "\r\n");
await stream.WriteAsync(bytes, 0, bytes.Length);
return 1;
}
private async Task<string> RecieveMessage(NetworkStream stream)
{
byte[] buffer = new byte[256];
string message = null;
int x;
while (stream.DataAvailable)
{
x = await stream.ReadAsync(buffer, 0, buffer.Length);
message += Encoding.ASCII.GetString(buffer);
}
return message;
}
The first problem that I am having is when I run the client program and click the ConnectButton, the message gets sent to the server program which outputs Message Recieved: user Connected!, but then the client program encounters a null reference exception on the line ChatText.AppendText(recieved); saying that the recieved variable is null. It seems that the line string recieved = await recieveTask; is not waiting for the task to finish executing, and it jumps to the next line without assigning a value to recieved. If I put a breakpoint at the top of the private async Task<string> RecieveMessage(NetworkStream stream) function and step through it, then the recieved variable gets it's value and the code will complete successfully, but without the breakpoint I get the null reference exception.
The next issue that I am having, is if I leave the server running and open the client again and try connecting, the server gets a null reference exception on the line message = message.Replace('\0', ' ');. The first time I run with the client, the server receives the message successfully, but the second time, it doesn't get any data from the stream and leaves the variable null, resulting in a null reference exception.
I apologize if my code is garbage, I have been reading the MSDN documentation for hours and am unable to come up with a solution, and I feel like I am doing this completely wrong. So my questions are as follows:
What is causing these errors that I am encountering? And am I approaching this problem the right way?
Both of your issues are not related to asynchronous functions, and actually both issues are because of the same problem:
while (stream.DataAvailable)
{
// read stream here
}
If data is not yet available to read from the stream - both of your ReceiveMessage and HandleConnections functions just skip reading stream at all. What you should do instead (in your case) is:
do
{
// read your stream here
} while (stream.DataAvailable);
Then first Read (or ReadAsync) will wait until first data chunk arrives, and only after first chunk will check if more data is already available.
Also note that you use large buffer (256 bytes) while client\server send short messages (like "Client received: xxx"), which means most of the buffer is empty and when you convert it to string via Encoding.ASCII.GetString - you get a lot of whitespace at the end ("Client received: xxx ... ").
It doesn't look like a problem with async/await so much as an issue with your TCP streams.
You don't appear to be actually waiting for a response. SendMessage writes the server, then RecieveMessage expects a response to already be in the stream.
If stream.DataAvailable is false when you hit the while loop for the first time, message will remain null.
You need some way to wait for there to be data in the stream before you attempt to read from it.

Why does Console.WriteLine() block in callback from Stream.ReadAsync()?

I have a callback function in which I am trying to write the data that I read in an overriden ReadAsync().
private void StreamCallback(byte[] bytes)
{
Console.WriteLine("--> " + Encoding.UTF8.GetString(bytes)); // the whole application is blocked here, why?
if (OnDataReceived != null)
{
string data = Encoding.UTF8.GetString(bytes);
OnDataReceived(data);
}
}
The overriden ReadAsync() looks as follows.
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken)
{
var read = await _originalStream.ReadAsync(buffer, offset, count, cancellationToken);
_readCallback(buffer);
return read;
}
What I actually want to achieve is to monitor a network stream just before it gets parsed by an XmlReader. This relates to my other question > Reading from same SslStream simultaneously? <. How would I do that?
UPDATE:
It is actually Encoding.UTF8.GetString(bytes) that is blocking the application. In order for the question to be more complete I am listing the code for reading the XML stream.
using (XmlReader r = XmlReader.Create(sslStream, new XmlReaderSettings() { Async = true }))
{
while (await r.ReadAsync())
{
switch (r.NodeType)
{
case XmlNodeType.XmlDeclaration:
...
break;
case XmlNodeType.Element:
...
Based on the code you posted, StreamCallback() will block until that stream ends. You pass a byte pointer to Encoding.UTF8.GetString(bytes); So, it needs to keep querying bytes until it reaches the end. It will never reach the end since bytes comes from a stream until that stream is closed.
You need to either process your stream a certain number of bytes at a time or until a certain character is seen.

Categories