Measure data transfer rate over tcp using c# - c#

i want to measure current download speed. im sending huge file over tcp. how can i capture the transfer rate every second? if i use IPv4InterfaceStatistics or similar method, instead of capturing the file transfer rate, i capture the device transfer rate. the problem with capturing device transfer rate is that it captures all ongoing data through the network device instead of the single file that i transfer.
how can i capture the file transfer rate? im using c#.

Since you doesn't have control over stream to tell him how much read, you can time-stamp before and after a stream read and then based on received or sent bytes calculate the speed:
using System.IO;
using System.Net;
using System.Diagnostics;
// some code here...
Stopwatch stopwatch = new Stopwatch();
// Begining of the loop
int offset = 0;
stopwatch.Reset();
stopwatch.Start();
bytes[] buffer = new bytes[1024]; // 1 KB buffer
int actualReadBytes = myStream.Read(buffer, offset, buffer.Length);
// Now we have read 'actualReadBytes' bytes
// in 'stopWath.ElapsedMilliseconds' milliseconds.
stopwatch.Stop();
offset += actualReadBytes;
int speed = (actualReadBytes * 8) / stopwatch.ElapsedMilliseconds; // kbps
// End of the loop
You should put the Stream.Read in a try/catch and handle reading exception. It's the same for writing to streams and calculate the speed, just these two lines are affected:
myStream.Write(buffer, 0, buffer.Length);
int speed = (buffer.Length * 8) / stopwatch.ElapsedMilliseconds; // kbps

Related

Read Stdin continously

I have a C# program which takes FFMPEG raw data and begins streaming it to a monitor connected to the network. The program works with still images so I assume I have something wrong with my stdin reading speed. I want the program to work like this:
Begin reading stdin ffmpeg raw
When the buffer is at a specified length (I have to form the frames myself from the raw data, this specified length is width * height * 3)
Send frame & meantime start reading the next part of the raw data
Repeat until shutdown / video end. My current code can stream with 30 FPS, but its not quite good. I could achieve 60 FPS when sending still images. The timers are needed because I have to send the frames at very specific timestamps.
int width = 128;
int height = 64;
static Stream standard_input = Console.OpenStandardInput();
static byte[] buffer_one = new byte[width*height*3];
public static void ReadStandardInput(){
int in_len;
while((in_len = standard_input.Read(buffer, 0, height*width*3)) > 0 ){
if (in_len == height*width*3){
if (frame_delay_clock.IsRunning == false){
frame_delay_clock.Start();
}
if (frame_delay_clock.ElapsedMilliseconds >= wait_between_frames){
SendFrame(buffer, 15);
frame_delay_clock.Reset();
}
else{
continue;
}
}
else{
continue;
}
}
System.Console.WriteLine("STDIN ended.");
standard_input.Close();
}

Images streamed over tcp are split in 3 with middle missing

The goal of the project is to stream video captured from a python host to a c# client via tcp sockets.
Relavent python2 server script:
import cv2
import numpy as np
import socket
from threading import Thread
_continue = True
def imageStreamer4():
global _continue
cam = cv2.VideoCapture(0)
camSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
camSocket.bind(("",8081))
camSocket.listen(1)
# set flip image to false if you don't want the image to be flipped
flipImage = True
while _continue:
try:
client,address = camSocket.accept()
print("client connected")
ret,camImage = cam.read()
if flipImage:
camImage = cv2.flip(camImage,1)
#uncomment the below code to view the webcam stream locally
"""
cv2.imshow('image',camImage)
if cv2.waitKey(1) == 27:
break # esc to quit
"""
byteString = bytes(cv2.imencode('.jpg', camImage)[1].tostring())
fileSize = len(byteString)
totalSent = 0
client.send(str(fileSize).encode())
sizeConfirmation = client.recv(1024)
totalSent = 0
while totalSent < fileSize:
totalSent += client.send(byteString[totalSent:])
print(str(fileSize), str(totalSent),sizeConfirmation.decode('utf-8'))
except Exception as e:
print(e)
print("shutting down video stream")
_continue = False
print("video stream exited.")
Relevant c# client code:
using System.Collections;
using UnityEngine;
using System;
using System.Net;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using UnityEngine.UI;
using System.IO;
void getVideoStream()
{
byte[] header;
int recieved;
int fileSize;
NetworkStream dataStream;
MemoryStream ms;
while (connectCam)
{
fileSize = 0;
recieved = 0;
camClient = new TcpClient(camIP, camPort);
//get header
dataStream = camClient.GetStream();
while (!dataStream.DataAvailable)
{
//waste time
}
header = new byte[1024];
dataStream.Read(header, 0, header.Length);
fileSize = Int32.Parse(Encoding.Default.GetString(bytesReducer(header)));
byte[] result = Encoding.ASCII.GetBytes(fileSize.ToString());
//send response
dataStream.Write(result, 0, result.Length);
ms = new MemoryStream();
while (!dataStream.DataAvailable)
{
//waste time
}
while (recieved < fileSize)
{
byte[] data = new byte[camClient.ReceiveBufferSize];
recieved += dataStream.Read(data, 0, data.Length);
ms.Write(data, 0, data.Length);
}
//the below class simply sends function calls from secondary thread back to the main thread
UnityMainThreadDispatcher.Instance().Enqueue(convertBytesToTexture(ms.ToArray()));
dataStream.Close();
camClient.Close();
}
}
void convertBytesToTexture(byte[] byteArray) {
try
{
camTexture.LoadImage(byteArray); //Texture2D object
camImage.texture = camTexture; //RawImage object
}
catch (Exception e)
{
print(e);
}
}
The byte counts sent and received match as they should. I'm admittedly new to working with sockets but I'm pretty certain that the data is arriving whole and intact. Unfortunately I really have no idea why the image is splitting as it is. (As shown in the above image) If it's relevant at all, both the server and client functions are being run on their own separate threads.
I've run the scripts on separate hosts and clients and the results remain the same.If any different information is required to help, just ask. I'll be happy to update as required.
I was able to bring the picture together properly by removing empty space inserted by the following code:
while (recieved < fileSize)
{
byte[] data = new byte[camClient.ReceiveBufferSize];
recieved += dataStream.Read(data, 0, data.Length);
ms.Write(data, 0, data.Length);
}
replacing it with this:
int increment = 0;
while (recieved < fileSize)
{
byte[] data = new byte[camClient.ReceiveBufferSize];
increment = dataStream.Read(data, 0, data.Length);
recieved += increment;
ms.Write(data.Take(increment).ToArray(), 0, increment);
}
So instead of taking an array the size of the client's buffer into the memory stream(even if it wasn't full), it's being condensed to only the amount of information received via the read method. This effectively removed all the blank space in the received image.
Because Tcp is a stream you should consider it as a flow of bytes so you should apply some mechanism to identify each image
this way you can apply:
A delimiter:
use a set of bytes to separate each image( but this byte set must not repeated in your stream) for example:
1- send image bytes
2-send delimiter
in the client you start to read until reach the delimiter
Consider defining a protocol and send the image size first and then send image data for example :
1-first 8 bytes is always image size
2- then the image bytes
and when you read data on the client first you always read 8 bytes and then base on this size you read the rest of data.
Another way is to use fixed size data(which is a bit hard for you in this case) you get a fix size byte array in the server and if the image data is less than the byte array fill it with zero and on the client always read bytes as many as the server array size.
Server Send byte[2048] and Client reads until the bytes which is readed reach 2048

WasapiCapture NAudio

We are using the NAudio Stack written in c# and trying to capture the audio in Exclusive mode with PCM 8kHZ and 16bits per sample.
In the following function:
private void InitializeCaptureDevice()
{
if (initialized)
return;
long requestedDuration = REFTIMES_PER_MILLISEC * 100;
if (!audioClient.IsFormatSupported(AudioClientShareMode.Shared, WaveFormat) &&
(!audioClient.IsFormatSupported(AudioClientShareMode.Exclusive, WaveFormat)))
{
throw new ArgumentException("Unsupported Wave Format");
}
var streamFlags = GetAudioClientStreamFlags();
audioClient.Initialize(AudioClientShareMode.Shared,
streamFlags,
requestedDuration,
requestedDuration,
this.waveFormat,
Guid.Empty);
int bufferFrameCount = audioClient.BufferSize;
this.bytesPerFrame = this.waveFormat.Channels * this.waveFormat.BitsPerSample / 8;
this.recordBuffer = new byte[bufferFrameCount * bytesPerFrame];
Debug.WriteLine(string.Format("record buffer size = {0}", this.recordBuffer.Length));
initialized = true;
}
We configured the WaveFormat before calls this function to (8000,1) and also a period of 100 ms.
We expected the system to allocate 1600 bytes for the buffer and interval of 100 ms as requested.
But we noticed following occured:
1. the system allocated audioClient.BufferSize to be 4800 and "this.recordBuffer" an array of 9600 bytes (which means a buffer for 600ms and not 100ms).
2. the thread is going to sleep and then getting 2400 samples (4800 bytes) and not as expected frames of 1600 bytes
Any idea what is going there?
You say you are capturing audio in exclusive mode, but in the example code you call the Initialize method with AudioClientMode.Shared. It strikes me as very unlikely that shared mode will let you work at 8kHz. Unlike the wave... APIs, WASAPI does no resampling for you of playback or capture, so the soundcard itself must be operating at the sample rate you specify.

c# SslStream.Read Loop problem

I've been learning C# by creating an app and i've hit a snag i'm really struggling with.
Basicly i have the code below which is what im using to read from a network stream I have setup. It works but as its only reading 1 packet for each time the sslstream.Read() unblocks. It's causes a big backlog of messages.
What im looking at trying to do is if the part of the stream read contains multiple packets read them all.
I've tried multiple times to work it out but i just ended up in a big mess of code.
If anyone could help out I'd appreciate it!
(the first 4bytes of each packet is the size of the packet.. packets range between 8 bytes and 28,000 bytes)
SslStream _sslStream = (SslStream)_sslconnection;
int bytes = -1;
int nextread = 0;
int byteslefttoread = -1;
byte[] tmpMessage;
byte[] buffer = new byte[3000000];
do
{
bytes = _sslStream.Read(buffer, nextread, 8192);
int packetSize = BitConverter.ToInt32(buffer, 0);
nextread += bytes;
byteslefttoread = packetSize - nextread;
if (byteslefttoread <= 0)
{
int leftover = Math.Abs(byteslefttoread);
do
{
tmpMessage = new byte[packetSize];
Buffer.BlockCopy(buffer, 0, tmpMessage, 0, packetSize);
PacketHolder tmpPacketHolder = new PacketHolder(tmpMessage, "in");
lock (StaticMessageBuffers.MsglockerIn)
{
//puts message into the message queue.. not very oop... :S
MessageInQueue.Enqueue(tmpPacketHolder);
}
}
while (leftover > 0);
Buffer.BlockCopy(buffer, packetSize , buffer, 0, leftover);
byteslefttoread = 0;
nextread = leftover;
}
} while (bytes != 0);
If you are using .Net 3.5 or later I would highly suggest you look into Windows Communication Foundation (wcf). It will simply anything you are trying to do over a network.
On the other hand, if you are doing this purely for educational purposes.
Take a look at this link. Your best bet is to read from the stream in somewhat smaller increments, and feed that data into another stream. Once you can identify the length of data you need for a message, you can cut the second stream off into a message. You can setup an outside loop where available bytes are being checked and wait until its value is > 0 to start the next message. Also should note, that any network code should be running on its own thread, so as to not block the UI thread.

C# TCP file transfer - Images semi-transferred

I am developing a TCP file transfer client-server program. At the moment I am able to send text files and other file formats perfectly fine, such as .zip with all contents intact on the server end. However, when I transfer a .gif the end result is a gif with same size as the original but with only part of the image showing as if most of the bytes were lost or not written correctly on the server end.
The client sends a 1KB header packet with the name and size of the file to the server. The server then responds with OK if ready and then creates a fileBuffer as large as the file to be sent is.
Here is some code to demonstrate my problem:
// Serverside method snippet dealing with data being sent
while (true)
{
// Spin the data in
if (streams[0].DataAvailable)
{
streams[0].Read(fileBuffer, 0, fileBuffer.Length);
break;
}
}
// Finished receiving file, write from buffer to created file
FileStream fs = File.Open(LOCAL_FOLDER + fileName, FileMode.CreateNew, FileAccess.Write);
fs.Write(fileBuffer, 0, fileBuffer.Length);
fs.Close();
Print("File successfully received.");
// Clientside method snippet dealing with a file send
while(true)
{
con.Read(ackBuffer, 0, ackBuffer.Length);
// Wait for OK response to start sending
if (Encoding.ASCII.GetString(ackBuffer) == "OK")
{
// Convert file to bytes
FileStream fs = new FileStream(inPath, FileMode.Open, FileAccess.Read);
fileBuffer = new byte[fs.Length];
fs.Read(fileBuffer, 0, (int)fs.Length);
fs.Close();
con.Write(fileBuffer, 0, fileBuffer.Length);
con.Flush();
break;
}
}
I've tried a binary writer instead of just using the filestream with the same result.
Am I incorrect in believing successful file transfer to be as simple as conversion to bytes, transportation and then conversion back to filename/type?
All help/advice much appreciated.
Its not about your image .. It's about your code.
if your image bytes were lost or not written correctly that's mean your file transfer code is wrong and even the .zip file or any other file would be received .. It's gonna be correpted.
It's a huge mistake to set the byte buffer length to the file size. imagine that you're going to send a large a file about 1GB .. then it's gonna take 1GB of RAM .. for an Idle transfering you should loop over the file to send.
This's a way to send/receive files nicely with no size limitation.
Send File
using (FileStream fs = new FileStream(srcPath, FileMode.Open, FileAccess.Read))
{
long fileSize = fs.Length;
long sum = 0; //sum here is the total of sent bytes.
int count = 0;
data = new byte[1024]; //8Kb buffer .. you might use a smaller size also.
while (sum < fileSize)
{
count = fs.Read(data, 0, data.Length);
network.Write(data, 0, count);
sum += count;
}
network.Flush();
}
Receive File
long fileSize = // your file size that you are going to receive it.
using (FileStream fs = new FileStream(destPath, FileMode.Create, FileAccess.Write))
{
int count = 0;
long sum = 0; //sum here is the total of received bytes.
data = new byte[1024 * 8]; //8Kb buffer .. you might use a smaller size also.
while (sum < fileSize)
{
if (network.DataAvailable)
{
{
count = network.Read(data, 0, data.Length);
fs.Write(data, 0, count);
sum += count;
}
}
}
}
happy coding :)
When you write over TCP, the data can arrive in a number of packets. I think your early tests happened to fit into one packet, but this gif file is arriving in 2 or more. So when you call Read, you'll only get what's arrived so far - you'll need to check repeatedly until you've got as many bytes as the header told you to expect.
I found Beej's guide to network programming a big help when doing some work with TCP.
As others have pointed out, the data doesn't necessarily all arrive at once, and your code is overwriting the beginning of the buffer each time through the loop. The more robust way to write your reading loop is to read as many bytes as are available and increment a counter to keep track of how many bytes have been read so far so that you know where to put them in the buffer. Something like this works well:
int totalBytesRead = 0;
int bytesRead;
do
{
bytesRead = streams[0].Read(fileBuffer, totalBytesRead, fileBuffer.Length - totalBytesRead);
totalBytesRead += bytesRead;
} while (bytesRead != 0);
Stream.Read will return 0 when there's no data left to read.
Doing things this way will perform better than reading a byte at a time. It also gives you a way to ensure that you read the proper number of bytes. If totalBytesRead is not equal to the number of bytes you expected when the loop is finished, then something bad happened.
Thanks for your input Tvanfosson. I tinkered around with my code and managed to get it working. The synchronicity between my client and server was off. I took your advice though and replaced read with reading a byte one at a time.

Categories

Resources