Socket.Avaliable loss C# - c#

I wrote this method using UDP socket with ReceiveTimeout = 1000. And sometimes it loses second packet:
private static byte[] ReceivePlainData(Socket socket) {
var recievedData = new List<byte>();
var buffer = new byte[1024];
do {
int recievedAmount;
while (true) {
try {
recievedAmount = socket.Receive(buffer);
}
catch (SocketException) {
continue;
}
break;
}
recievedData.AddRange(buffer.Take(recievedAmount));
} while (socket.Available > 0);
return recievedData.ToArray();
}
Socket initialization:
using (var socket = new Socket(SocketType.Dgram, ProtocolType.Udp) {
ReceiveTimeout = 1000
})
What do i need to change to make this method work correctly?

Socket.Available only tells you if data has been received and available for immediate reading. It doesn't tell you if the stream is complete or not. The sender may still be in the process of generating or transmitting more data. If you intend to consume the entire response and are relying on the socket to be closed by the sender when there is no more data, continue reading from the socket until the number of bytes returned is 0 (indicating the socket was closed normally) or an exception occurs (indicating abnormal termination).
MemoryStream stream = new MemoryStream();
int received;
var buffer = new byte[8096];
do {
received = socket.Receive(buffer);
stream.Write(buffer, 0, received);
} while (received > 0);
// Use the stream
// byte[] bytes = stream.ToArray();
Set your socket's ReceiveTimeout to the maximum amount of time you're willing to wait between bytes before aborting.

Related

Bad audio while sending through TCP/IP connection

I'm making an application that sends audio through TCP/IP connection using NAudio.
This is my server code:
// Get server port.
var iServerPort = GetServerPort();
// Initialize memory stream.
_bytes = new List<byte>();
// Initialize tcp listener.
var tcpListener = new TcpListener(IPAddress.Any, iServerPort);
tcpListener.Start();
// Accept a tcp client.
Console.WriteLine("Waiting for incoming connection");
_tcpClient = tcpListener.AcceptTcpClient();
Console.WriteLine("Client found !");
// Buffered wave provider.
_bufferedWaveProvider = new BufferedWaveProvider(new WaveFormat());
_bufferedWaveProvider.DiscardOnBufferOverflow = true;
_bufferedWaveProvider.BufferLength = iBufferBytes;
_waveOut = new WaveOut();
_waveOut.Init(_bufferedWaveProvider);
_waveOut.Play();
// Initialize a thread to listen to incoming data from another source.
_tcpConnectionListeningThread = new Thread(() =>
{
// Get data stream of tcp connection.
var networkStream = _tcpClient.GetStream();
// Initalize buffer.
var buffer = new byte[iBufferBytes];
// Number of bytes which have been read.
while (true)
{
try
{
int iReadBytes;
while ((iReadBytes = networkStream.Read(buffer, 0, buffer.Length)) > 0)
{
//var memoryStream = new MemoryStream();
//memoryStream.Write(buffer, 0, iReadBytes);
//_memoryStreams.Enqueue(memoryStream);
//_memoryStream.CopyTo();
//_memoryStream.Write(buffer, 0, iReadBytes);
var realBytes = buffer.Take(iReadBytes).ToList();
_bytes.AddRange(realBytes);
Console.WriteLine($"Read: {iReadBytes} bytes. Enqueue a stream. Count: {realBytes.Count}");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
break;
}
}
});
// Initialize a thread to check audio buffer. Reload as audio stream consumes whole data.
_waveBufferCheckThread = new Thread(() =>
{
while (true)
{
// Get remaining bytes in buffer.
var iRemainingBytes = _bufferedWaveProvider.BufferedBytes;
// No byte is available in buffer.
// Check memory stream to find available bytes array. If nothing found, terminate this thread.
if (iRemainingBytes >= iBufferBytes)
continue;
// No byte available.
if (_bytes.Count < 1)
{
//Console.WriteLine("No data in memory stream");
continue;
}
// Get bytes array from memory stream.
var iDifferent = iBufferBytes - iRemainingBytes;
if (iDifferent > _bytes.Count)
iDifferent = _bytes.Count;
var bytes = _bytes.GetRange(0, iDifferent).ToArray();
_bytes.RemoveRange(0, iDifferent);
//var bytes = new byte[iBufferBytes];
_bufferedWaveProvider.AddSamples(bytes, 0, bytes.Length);
//Console.WriteLine($"Wrote {iReadBytes} to buffer. Count: {_memoryStream.Length}");
//Thread.Sleep(TimeSpan.FromSeconds(1));
//_waveOut.Play();
}
});
_tcpConnectionListeningThread.IsBackground = true;
_waveBufferCheckThread.IsBackground = true;
_tcpConnectionListeningThread.Start();
_waveBufferCheckThread.Start();
Console.ReadLine();
_tcpConnectionListeningThread.Abort();
_waveBufferCheckThread.Abort();
_bufferedWaveProvider.ClearBuffer();
_waveOut.Stop();
tcpListener.Stop();
This is my client code:
public Main()
{
InitializeComponent();
// Initialize tcp client.
_tcpClient = new TcpClient();
_recordingStream = new MemoryStream();
_soundRecorder = new WaveIn();
_waveOut = new WaveOut();
_playbackBufferedWaveProvider = new BufferedWaveProvider(_soundRecorder.WaveFormat);
_waveOut.Init(_playbackBufferedWaveProvider);
_soundRecorder.DataAvailable += SoundRecorderOnDataAvailable;
btnRecord.Enabled = true;
}
private void SoundRecorderOnDataAvailable(object sender, WaveInEventArgs waveInEventArgs)
{
if (_networkStream != null)
{
_networkStream.Write(waveInEventArgs.Buffer, 0, waveInEventArgs.BytesRecorded);
_networkStream.Flush();
}
}
My application works fine. But there is one problem I'm facing with :
- The recorded voice cannot be heard while sending to server end-point. If I save it to memory stream and play it back, the quality is ok.
Should I compress my recorded voice before sending to server ? Are there any ways to solve this issue ?
Thank you,
Your _tcpConnectionListeningThread should be writing directly into the BufferedWaveProvider, not going through an intermediate step of copying into an non-thread safe list. _waveBufferCheckThread is completely unnecessary. Update the listening thread to do something like:
while ((iReadBytes = networkStream.Read(buffer, 0, buffer.Length)) > 0)
{
_bufferedWaveProvider.AddSamples(buffer, 0, iReadBytes);
}
Also you must ensure the WaveFormat on the server side is exactly the same as used on the client side.

Client server socket C#

I am working on socket C#. I've implemented a client server application using socket, but the problem is that the client doesn't receive all data sent by the server.
Here is the client application code. What should I do so that it would receive all data sent by the server?
strRecieved = "";
Socket soc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9001);
soc.Connect(endPoint);
byte[] msgBuffer = Encoding.Default.GetBytes(path);
soc.Send(msgBuffer, 0, msgBuffer.Length, 0);
byte[] buffer = new byte[2000];
int rec = soc.Receive(buffer);
strRecieved = String.Format(Encoding.Default.GetString(buffer));
First of all. If you're implementing some kind of streaming feature ( tcp/udp/file ) you should consider using some kind of protocol.
What is a protocol? It's just a scheme to use when streaming data. Example:
[4Bytes - length][lengthBytes - message][1Byte - termination indicator]
Knowing the protocol you can read all of the incoming bytes simply as such :
byte[] buffer = new byte[4];
stream.ReadBytes(buffer, 0, 4); // cast that to int and read the rest
int packetLen = BitConverter.ToInt32(buffer, 0);
buffer = new byte[packetLen];
stream.ReadBytes(buffer, 0, buffer.Length); // all bytes that was sent
Remember that you have to subtract thease 4 bytes in the length before sending the message.
EDIT:
Simple example on how to send and receive data using shared protocol.
// sender.cs
string _stringToSend = "some fancy string";
byte[] encodedString = Encoding.UTF8.GetBytes(_stringToSend);
List<byte> buffer = new List<byte>();
buffer.AddRange(BitConverter.GetBytes(encodedString.Length));
buffer.AddRange(encodedString);
netStream.WriteBytes(buffer.ToArray(), 0, buffer.Count);
// netStream sent message in protocol [#LEN - 4Bytes][#MSG - #LENBytes]
// simply speaking something like: 5ABCDE
// receiver.cs
byte[] buffer = new byte[sizeof(int)];
netStream.ReadBytes(buffer, 0, buffer.Length);
// receiver got the length of the message eg. 5
int dataLen = BitConverter.ToInt32(buffer, 0);
buffer = new byte[dataLen];
// now we can read an actual message because we know it's length
netStream.ReadBytes(buffer, 0, buffer.Length);
string receivedString = Encoding.UTF8.GetString(buffer);
// received string is equal to "some fancy string"
Making it simpler
This technique forces you to use desired protocol which in this example will be :
First 4 bytes sizeof(int) are indicating the length of the incoming packet
Every byte further is your packet until the end.
So right now you should make ProtocolHelper object:
public static class ProtocolHelper
{
public byte[] PackIntoProtocol(string message)
{
List<byte> result = new List<byte>();
byte[] messageBuffer = Encoding.UTF8.GetBytes(message);
result.AddRange(BitConverter.GetBytes(messageBuffer.Length), 0); // this is the first part of the protocol ( length of the message )
result.AddRange(messageBuffer); // this is actual message
return result.ToArray();
}
public string UnpackProtocol(byte[] buffer)
{
return Encoding.UTF8.GetString(buffer, 0, buffer.Length);
}
}
Now ( depending on method you've chosen to read from network ) you have to send and receive your message.
// sender.cs
string meMessage = "network message 1";
byte[] buffer = ProtocolHelper.PackIntoProtocol(meMessage);
socket.Send(buffer, 0, buffer.Length, 0);
// receiver.cs
string message = string.Empty;
byte[] buffer = new byte[sizeof(int)]; // or simply new byte[4];
int received = socket.Receive(buffer);
if(received == sizeof(int))
{
int packetLen = BitConverter.ToInt32(buffer);// size of our message
buffer = new byte[packetLen];
received = socket.Receive(buffer);
if( packetLen == received ) // we have full buffer
{
message = PacketHelper.UnpackProtocol(buffer);
}
}
Console.WriteLine(message); // output: "network message 1"
You're limiting the size of received messages by 2KB as you're using new byte[2000].
I think you could either:
Size up you buffer to meet you message's size needs; and/or
Split you message into more than one socket messages.
Given that 4-8K is a good size for buffering socket messages and assuming RAM size is not a issue I would start with that, say, new byte[8000].
Also, you can send socket messages splitted in chunks. Maybe this is a good idea for the case. For example, if you have msg as the message (or object) you want to send:
private static async Task SendAnswer(Message msg, WebSocket socket)
{
var answer = System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(msg).ToCharArray());
var bufferSize = 8000;
var endOfMessage = false;
for (var offset = 0; offset < answer.Length; offset += bufferSize)
{
if (offset + bufferSize >= answer.Length)
{
bufferSize = answer.Length - offset;
endOfMessage = true;
}
await socket.SendAsync(new ArraySegment<byte>(answer, offset, bufferSize),
WebSocketMessageType.Text, endOfMessage, CancellationToken.None);
}
}
And when receiving, you can also split the reception in chunks, so you can control you buffer (and therefore you memory consumption). After hanlding the whole message, you should wait for another message from the client to do more stuff. Source
private async Task ReceiveMessage(WebSocket webSocket)
{
var buffer = new byte[8000];
var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
while (!result.CloseStatus.HasValue)
{
string msg = Encoding.UTF8.GetString(new ArraySegment<byte>(buffer, 0, result.Count).ToArray());
while (!result.EndOfMessage)
{
result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
msg += Encoding.UTF8.GetString(new ArraySegment<byte>(buffer, 0, result.Count).ToArray());
}
//At this point, `msg` has the whole message you sent, you can do whatever you want with it.
// [...]
//After you handle the message, wait for another message from the client
result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
}
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}

Will C# NetworkStream.Read() wait until the specified amount of data is read?

In C/C++ read() on regular non-blocking network sockets will return immediately with the amount of data currently available in the buffer, up to the amount specified (so if we ask for 16 bytes and there are only 8 available at the moment, those 8 we'll get and it's up to us to call read() again and fetch all data).
In C# there's NetworkStream, which has built-in timeouts - does this mean that NetworkStream.Read() waits until either the timeout is reached or the amount of data requested is read, or will it give us any amount of data currently available in the buffer larger than 0 up to the amount requested (as the standard sockets do) even if there's time left?
It will read available data up to the number of bytes specified in the parameters, as described on MSDN, unless the stream is closed via timeout or other exception.
The Read operation reads as much data as is available, up to the number of bytes specified by the size parameter. If the remote host shuts down the connection, and all available data has been received, the Read method completes immediately and return zero bytes.
I solved it like this:
byte[] TotalData = new byte[0];
byte[] TempData = new byte[0];
using (TcpClient TCPClient = new TcpClient())
{
try
{
TCPClient.Connect(somehost, someport);
}
catch (Exception eee)
{
// Report the connection failed in some way if necessary
}
if (TCPClient.Connected)
{
using (NetworkStream clientStream = TCPClient.GetStream())
{
// You can reduce the size of the array if you know
// the data received is going to be small,
// don't forget to change it a little down too
byte[] TCPBuffer = new byte[524288];
int bytesRead = 0;
int loop = 0;
// Wait for data to begin coming in for up to 20 secs
while (!clientStream.DataAvailable && loop< 2000)
{
loop++;
Thread.Sleep(10);
}
// Keep reading until nothing comes for over 1 sec
while (clientStream.DataAvailable)
{
bytesRead = 0;
try
{
bytesRead = clientStream.Read(TCPBuffer, 0, 524288);
Array.Resize(ref TempData, bytesRead);
Array.Copy(TCPBuffer, TempData, bytesRead);
// Add data to TotalData
TotalData = JoinArrays(TotalData, TempData);
}
catch
{
break;
}
if (bytesRead == 0)
break;
Thread.Sleep(1000);
}
}
}
}
The JoinArrays() method:
byte[] JoinArrays(byte[] arrayA, byte[] arrayB)
{
byte[] outputBytes = new byte[arrayA.Length + arrayB.Length];
Buffer.BlockCopy(arrayA, 0, outputBytes, 0, arrayA.Length);
Buffer.BlockCopy(arrayB, 0, outputBytes, arrayA.Length, arrayB.Length);
return outputBytes;
}
The safe method is to use MemoryStream wich will make sure to wait and read all the stream to the memory , then u can use it as you like
public void SaveUserTemplate(Stream stream)
{
MemoryStream memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
memoryStream.Position = 0;
byte[] templatePathLength = new byte[4];
memoryStream.Read(templatePathLength, 0, templatePathLength.Length);
int nBytesTemplatePathLength = BitConverter.ToInt32(templatePathLength,0);
....
CopyTo function finally calls to this function:
github.com/microsoft/referencesource
private void InternalCopyTo(Stream destination, int bufferSize)
{
Contract.Requires(destination != null);
Contract.Requires(CanRead);
Contract.Requires(destination.CanWrite);
Contract.Requires(bufferSize > 0);
byte[] buffer = new byte[bufferSize];
int read;
while ((read = Read(buffer, 0, buffer.Length)) != 0)
destination.Write(buffer, 0, read);
}

create a "listener" for an incoming socket stream that will contain a byte[] array

I'm creating a socket server that needs to continuously listen for incoming messages from the connected clients. Those messages will be sent in a byte[] array. I had the server working great with a StreamReader but StreamReader only works with textual represenations of the data being sent...not byte[] arrays.
Here's what I had:
StreamReader reader = new StreamReader(Client.GetStream());
string line = "";
while (true)
{
line = reader.ReadLine();
if (!string.IsNullOrEmpty(line))
{
parentForm.ApplyText(line + "\r\n");
SocketServer.SendBroadcast(line);
}
}
I need to now convert that into a raw stream somehow that will convert the stream contents into a byte[] array but I can't seem to get a handle on it.
I tried this:
while (true)
{
var bytes = default(byte[]);
using (var memstream = new MemoryStream())
{
var buffer = new byte[512];
var bytesRead = default(int);
while ((bytesRead = reader.BaseStream.Read(buffer, 0, buffer.Length)) > 0)
memstream.Write(buffer, 0, bytesRead);
bytes = memstream.ToArray();
}
//parentForm.ApplyText(bytes.Length + "\r\n");
}
but as you might guess, the while(true) loop doesn't quite work how I need it to. Can anyone help me with some code adjustment to make this work as I need it to. It needs to continuously listen for incoming messages, then when a message is received, it needs to do something with that message (the byte[] array) then go back to listening again.
TIA
I guess "listening continuously" is not task of reader its a task of listener. I ran into same problem when i was writing server using TcpListener. I am not sure what you want to do but i am posting solution for your "listening continuous" and reading into byte[] problem. I guess this code might help you:
TcpListener t = new TcpListener(IPAddress.Loopback, _port);
t.Start();
Console.WriteLine("Server is started and waiting for client\n\n");
byte[] buff = new byte[255];
NetworkStream stream;
TcpClient client;
while(true)
{
client = t.AcceptTcpClient();
if (!client.Connected)
return;
stream = client.GetStream();
while ((stream.Read(buff, 0, buff.Length)) != 0)
{
break;
}
if (0 != buff.Length)
break;
}
There's no need to convert anything. GetStream() returns a NetworkStream. See the sample Microsoft includes in the NetworkStream.Read Method. All you have to do is replace the myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)); line with an appropriate storage mechanism.

Ensure streamreader doesn't hang waiting for data

The code below reads everything there is to read from tcp client stream, and on the next iteration it will just sit there on the Read() (i'm assuming waiting for data). How can I ensure it doesn't and just returns when there's nothing there to read? Do I have to set low timeout, and respond to an exception when it fails out? Or there's a better way?
TcpClient tcpclnt = new TcpClient();
tcpclnt.Connect(ip, port);
Stream stm = tcpclnt.GetStream();
stm.Write(cmdBuffer, 0, cmdBuffer.Length);
byte[] response = new Byte[2048];
MemoryStream ms = new MemoryStream();
byte[] buffer = new Byte[2048];
int length;
while ((length = stm.Read(buffer, 0, buffer.Length)) > 0)
ms.Write(buffer, 0, length);
byte[] response = ms.ToArray();
tcpclnt.Close();
TcpClient has two properties that I would play with.
NoDelay Gets or sets a value that disables a delay when send or receive buffers are not full.
ReceiveTimeout Gets or sets the amount of time a TcpClient will wait to receive data once a read operation is initiated
Maybe you should use multithreading. One thread should wait for data, another should process received data.
Does the server have "Keep Connections Alive" on ?
I have had servers which will send you a stream of empty bytes as long as you request them.
Turning "Keep Connections Alive" stopped it doing this.
var socket = new System.Net.Sockets.TcpClient();
socket.NoDelay = true;
socket.Connect(uri.Host, port);
var ns = socket.GetStream();
int timeout = 500; //ms
DateTime lastReceived = DateTime.Now;
string buffer = "";
while (true)
{
if (ns.DataAvailable)
{
var b = ns.ReadByte();
buffer += b + ", "; //customise this line yourself
lastReceived = DateTime.Now;
continue;
}
if (lastReceived.AddMilliseconds(timeout) < DateTime.Now) break;
System.Threading.Thread.Sleep(timeout / 5);
}

Categories