I am receiving bytes from a socket to a buffer. Right now, the buffer is a byte array field that's large enough to handle any message I expect.
byte[] recvBuffer = new byte[10000];
public void Receive1(int len)
{
client.Receive(recvBuffer, len, SocketFlags.None);
}
I could make improvements to the "elegance" my design by creating a new buffer each time instead
public void Receive2(int len)
{
byte[] buffer = new byte[len];
client.Receive(buffer);
}
My question is: would the latter lead to reduced performance?
Here's my attempt at answering the question myself. Let me know if this looks reasonable. I made a test client/server setup on my local machine using this code
class SocketTester
{
private const string IP = "127.0.0.1";
private const int PORT = 1776; // how patriotic
Socket serv = new Socket(
AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
Socket client = new Socket(
AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
public SocketTester()
{
Socket listener = new Socket(
AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
listener.SetSocketOption(
SocketOptionLevel.Socket,
SocketOptionName.ReuseAddress,
true);
listener.Bind(new IPEndPoint(IPAddress.Parse(IP), PORT));
listener.Listen(1);
listener.BeginAccept((IAsyncResult ar) =>
{
serv = listener.EndAccept(ar);
}, null);
/* fill it with junk data (i don't care about the actual values) */
for (int i = 0; i < sendBuffer.Length; i++)
{
sendBuffer[i] = (byte)i;
}
}
public void Connect()
{
client.Connect(IP, PORT);
if (!client.Connected)
{
throw new Exception();
}
}
byte[] sendBuffer = new byte[4096];
public void StartSending()
{
/* This blocks at 670 sends if nothing is received.*/
Task.Run(() =>
{
int i = 0;
while (true)
{
serv.Send(sendBuffer);
i++;
}
});
}
byte[] recvBuffer = new byte[10000];
public void Receive1(int len)
{
client.Receive(recvBuffer, len, SocketFlags.None);
}
public void Receive2(int len)
{
byte[] buffer = new byte[len];
client.Receive(buffer);
}
public void LoopReceive1(int tries, int len)
{
for (int i = 0; i < tries; i++)
{
Receive1(len);
}
}
public void LoopReceive2(int tries, int len)
{
for (int i = 0; i < tries; i++)
{
Receive2(len);
}
}
}
I then used the VS debugger to step over each line.
SocketTester s = new SocketTester();
s.Connect();
Thread.Sleep(100);
s.StartSending();
Thread.Sleep(100);
s.LoopReceive1(500, 4096);
s.LoopReceive2(500, 4096);
Both lines seem to execute between 3 and 5 ms with seemingly random variations. I manally moved the execution point back several times (so order shouldn't be an issue). This seems more than fast enough for my application and seems to indicate little to no significant difference.
Am I missing anything here? I'm not particularly familiar with memory management and optimization, nor how it's affected by creating new variables in .NET. If there's something I should read on this topic, I'd love a link :)
Related
I have a Java android code that sends data (image or text) to a C# application, to receive these data I'm using Async socket. But exists a problem that is relative to BeginReceive() function is not receiving the complete data when is sent an image.. Then how I can make a kind of "loop" to receive full data and after show the image on Picturebox (for example)?
Form
private Listener listener;
private Thread startListen;
private Bitmap _buffer;
public frmMain()
{
InitializeComponent();
}
private void serverReceivedImage(Client client, byte[] image)
{
try
{
byte[] newImage = new byte[image.Length - 6];
Array.Copy(image, 6, newImage, 0, newImage.Length);
using (var stream = new MemoryStream(newImage))
{
using (var msInner = new MemoryStream())
{
stream.Seek(2, SeekOrigin.Begin);
using (DeflateStream z = new DeflateStream(stream, CompressionMode.Decompress))
{
z.CopyTo(msInner);
}
msInner.Seek(0, SeekOrigin.Begin);
var bitmap = new Bitmap(msInner);
Invoke(new frmMain.ImageCompleteDelegate(ImageComplete), new object[] { bitmap });
}
}
}
catch (Exception)
{
System.Diagnostics.Process.GetCurrentProcess().Kill();
}
}
private delegate void ImageCompleteDelegate(Bitmap bitmap);
private void ImageComplete(Bitmap bitmap)
{
if (_buffer != null)
_buffer.Dispose();
_buffer = new Bitmap(bitmap);
pictureBox1.Size = _buffer.Size;
pictureBox1.Invalidate();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (_buffer == null) return;
e.Graphics.DrawImage(_buffer, 0, 0);
}
private void startToolStripMenuItem_Click(object sender, EventArgs e)
{
startListen = new Thread(listen);
startListen.Start();
}
private void listen()
{
listener = new Listener();
listener.BeginListen(101);
listener.receivedImage += new Listener.ReceivedImageEventHandler(serverReceivedImage);
startToolStripMenuItem.Enabled = false;
}
Listener
class Listener
{
private Socket s;
public List<Client> clients;
public delegate void ReceivedImageEventHandler(Client client, byte[] image);
public event ReceivedImageEventHandler receivedImage;
private bool listening = false;
public Listener()
{
clients = new List<Client>();
s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
public bool Running
{
get { return listening; }
}
public void BeginListen(int port)
{
s.Bind(new IPEndPoint(IPAddress.Any, port));
s.Listen(100);
s.BeginAccept(new AsyncCallback(AcceptCallback), s);
listening = true;
}
public void StopListen()
{
if (listening == true)
{
s.Close();
listening = false;
}
}
void AcceptCallback(IAsyncResult ar)
{
Socket handler = (Socket)ar.AsyncState;
Socket sock = handler.EndAccept(ar);
Client client = new Client(sock);
clients.Add(client);
sock.BeginReceive(client.buffer, 0, client.buffer.Length, SocketFlags.None, new AsyncCallback(ReadCallback), client);
client.Send("REQUEST_PRINT" + Environment.NewLine);
handler.BeginAccept(new AsyncCallback(AcceptCallback), handler);
}
void ReadCallback(IAsyncResult ar)
{
Client client = (Client)ar.AsyncState;
try
{
int rec = client.sock.EndReceive(ar);
if (rec != 0)
{
string data = Encoding.UTF8.GetString(client.buffer, 0, rec);
if (data.Contains("SCREEN"))
{
byte[] bytes = Encoding.UTF8.GetBytes(data);
receivedImage(client, bytes);
}
else // not is a image, is a text
{
// prepare text to show in TextBox
}
}
else
{
Disconnected(client);
return;
}
client.sock.BeginReceive(client.buffer, 0, client.buffer.Length, SocketFlags.None, new AsyncCallback(ReadCallback), client);
}
catch
{
Disconnected(client);
client.sock.Close();
clients.Remove(client);
}
}
}
Client
class Client
{
public Socket sock;
public byte[] buffer = new byte[8192];
public Client(Socket sock)
{
this.sock = sock;
}
public void Send(string data)
{
byte[] buffer = Encoding.ASCII.GetBytes(data);
sock.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback((ar) =>
{
sock.EndSend(ar);
}), buffer);
}
}
Android code
private byte[] compress(byte[] data) {
Deflater deflater = new Deflater();
deflater.setInput(data);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
deflater.finish();
byte[] buffer = new byte[1024];
while (!deflater.finished()) {
int count = deflater.deflate(buffer);
outputStream.write(buffer, 0, count);
}
outputStream.close();
byte[] output = outputStream.toByteArray();
return output;
}
public static DataOutputStream dos;
public static byte[] array;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
array = compress(bos.toByteArray());
//...
dos = new DataOutputStream(SocketBackgroundService.clientSocket.getOutputStream());
byte[] header = ("SCREEN").getBytes(StandardCharsets.UTF_8);
byte[] dataToSend = new byte[header.length + array.length];
System.arraycopy(header, 0, dataToSend, 0, header.length);
System.arraycopy(array, 0, dataToSend, header.length, array.length);
dos.writeInt(dataToSend.length);
dos.write(dataToSend, 0, dataToSend.length);
dos.flush();
EDITION
i'm always getting the error Invalid Parameter in this line
var bitmap = new Bitmap(msInner);
and using compression also happens the same here
z.CopyTo(msInner);
IvalidDataException
on ServerReceivedImage() method respectively.
using this
File.WriteAllBytes(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "image.png"), newImage);
i noted that is receiving only 15KB (size of file without use compression).
I was writing a comment but it does not give me enough space to express my frustration with your code.
My main points are
You try to recompress and perfectly compressed image. PNG is portable network graphics. It was designed for network transfers. If it is acceptable you should use something like jpeg.
You just decode received buffer using UTF8.GetString and search for a text, then re-encode that string and try to decompress and read an image from it, by starting from index 6 which is pretty meaningless considering you added a two byte size field to the start of stream and you really do not know position of "SCREEN".
You do not check if you have received ALL of the stream data.
All of the code looks like you have scoured the SO questions and answers and created a copy pasta.
Now my recommendations.
When transferring data from network, do not try to invent wheels. Try something like gRPC which has both android java and c# packages.
If you will use raw data, please, please know your bytes.
I assume you will extend your code by adding new command pairs. Since you have no magic markers of some kind of signal system, it will be very hard for you to distinguish data from header. For a simple implementation add some kind of magic data to your header and search for that data, then read header and then read data. You may need to read from socket again and again until you receive all of the data.
424A72 0600 53435245454E 008E0005 ..... 724A42
B J r 6 S C R E E N 36352 ..... rJB
this sample data shows that we have a valid stream by looking at "BJr". Then read a 2 byte unsigned integer to read command size which is 6 for SCREEN. Read command and then read four bytes unsigned length for command data. For our sample it is 36352. Just to be safe I've added an end of command marker "rJB".
For a bonus point try reducing memory allocations / copies, you can look at System.Span<T>
my program should be able to send and receive files but for some reason whenever i click on the send (button1) and receive (button2) buttons, it keeps hanging. Not sure if its something wrong my my codes? Also, I feel like my codes are pretty long as compared to other examples i found online but I'm not sure how to rectify.
Client codes
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9000);
private void Form1_Load(object sender, EventArgs e)
{
try
{
socket.Connect(remoteEP);
textBox2.Text = "Connected to Server";
}
catch (Exception ex)
{
textBox2.Text = "Unable to connect to Server";
textBox2.Text = ex.Message;
}
}
public const string SEND = "[SEND]";
public const string RECEIVE = "[RECEIVE]";
public const string QUIT = "[QUIT]";
private void button1_Click(object sender, EventArgs e)
{
textBox1.Clear();
textBox2.Clear();
NetworkStream stream = new NetworkStream(socket);
StreamReader reader = new StreamReader(stream);
StreamWriter writer = new StreamWriter(stream);
try
{
writer.WriteLine(RECEIVE);
writer.Flush();
writer.WriteLine(textBox1.Text);
writer.Flush();
Bitmap bmp = new Bitmap(#"C:\Users\Y400\Desktop\Lectures\Year 3\WAD\Week 11" + textBox1.Text);
MemoryStream ms = new MemoryStream();
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] bmpBytes = ms.GetBuffer();
bmp.Dispose();
ms.Close();
int sent;
sent = sendData(socket, bmpBytes);
textBox1.Text = "Transferring file complete\r\n";
textBox1.Text += bmpBytes.Length + " bytes sent to Server.";
}
catch (Exception ex)
{
textBox2.Text = ex.Message;
}
}
public static int sendData (Socket s, byte[] data)
{
int total = 0;
int size = data.Length;
int left = size;
int sent;
byte[] datasize = new byte[4];
datasize = BitConverter.GetBytes(size);
sent = s.Send(datasize);
while(total<size)
{
sent = s.Send(data, total, left, SocketFlags.None);
total += sent;
left -= sent;
}
return total;
}
private void button2_Click(object sender, EventArgs e)
{
textBox2.Clear();
textBox1.Clear();
byte[] data = new byte[1024];
string fileN = textBox2.Text.Trim();
NetworkStream ns = new NetworkStream(socket);
StreamReader reader = new StreamReader(ns);
StreamWriter writer = new StreamWriter(ns);
writer.WriteLine(SEND);
writer.Flush();
writer.WriteLine(fileN);
writer.Flush();
try
{
while (true)
{
data = receiveData(socket);
MemoryStream ms = new MemoryStream(data);
break;
}
textBox2.Text = ("Receiving file from server ...\r\n" + data.Length + " bytes copied");
}
catch (Exception ex)
{
textBox2.Text = ex.Message;
}
}
public static byte[] receiveData (Socket s)
{
int total = 0;
int recv;
byte[] datasize = new byte[4];
recv = s.Receive(datasize, 0, 4, 0);
int size = BitConverter.ToInt32(datasize, 0);
int dataleft = size;
byte[] data = new byte[size];
while (total < size)
{
recv = s.Receive(data, total, dataleft, 0);
if (recv == 0)
{
break;
}
total += recv;
dataleft -= recv;
}
return data;
}
private void button3_Click(object sender, EventArgs e)
{
textBox1.Clear();
textBox2.Clear();
textBox2.Text = "Connection closed";
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
Server codes
class Program
{
public const string SEND = "[SEND]";
public const string RECV = "[RECV]";
public const string QUIT = "[QUIT]";
static void Main(string[] args)
{
runServer();
}
static void runServer()
{
try
{
byte[] data = new byte[1024];
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint localEP = new IPEndPoint(IPAddress.Any, 9000);
server.Bind(localEP);
server.Listen(10);
Console.WriteLine("Waiting for Client ...");
Socket client = server.Accept();
Console.WriteLine("Client connected");
NetworkStream stream = new NetworkStream(client);
StreamReader reader = new StreamReader(stream);
StreamWriter writer = new StreamWriter(stream);
try
{
while(true)
{
string request = reader.ReadLine();
string filename = reader.ReadLine();
if (request == QUIT)
{
Console.WriteLine("Client disconnected");
break;
}
else if (request == SEND)
{
getFileFromClient(filename, client);
}
else if (request == RECV)
{
receiveFileFromClient(filename, client);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static void getFileFromClient(string filename, Socket client)
{
try
{
FileStream output = File.OpenWrite(filename);
Console.WriteLine(filename + " created");
int count = 0;
while(true)
{
byte[] data = new byte[1024];
int size = client.Receive(data);
output.Write(data, 0, size);
count += size;
if(size<1024)
{
break;
}
}
output.Close();
Console.WriteLine(count + " bytes read from client");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static void receiveFileFromClient(string filename, Socket client)
{
int count = 0;
FileStream input = File.OpenRead(filename);
Console.WriteLine("Reading " + filename);
while(true)
{
byte[] data = new byte[1024];
int bytesRead = input.Read(data, 0, 1024);
client.Send(data, bytesRead, SocketFlags.None);
count += bytesRead;
if(bytesRead < 1024)
{
break;
}
}
Console.WriteLine("Transferring file completed\r\n" + count + " bytes sent to Client");
input.Close();
}
}
Generally speaking
...try to approach the problem differently.
You can't just copy paste stuff from the Internet and hope for the best. You need to understand what you're doing thoroughly.
Regarding your exact problem
Take a look at the button2_Click method.
It contains a while loop which apparently never finishes.
while (true)
{
data = receiveData(socket);
MemoryStream ms = new MemoryStream(data);
break;
}
It does finish because of the break command. But this is all very hard to read.
When you copy paste code around and then apply quick fixes you end up with a pile of code which is very hard to debug.
It took me about 10 minutes to notice the fact that the client defines it's "message verbs" like so:
public const string SEND = "[SEND]";
public const string RECEIVE = "[RECEIVE]";
public const string QUIT = "[QUIT]";
while the server defines them like so:
public const string SEND = "[SEND]";
public const string RECV = "[RECV]";
public const string QUIT = "[QUIT]";
This is maybe not the only problem, but it is sufficient to create a deadlock,
because the server never executes the positive branch of this if statement:
else if (request == RECV)
{
receiveFileFromClient(filename, client);
}
so the client believes it is about to receive something, which proves to be false.
Also make sure you send the "SEND" and "RECEIVE" message verbs when you should and not mix them up.
Good luck!
PS: I would suggest you take a look at more simpler to use techniques for sending and receiving data, such as:
WCF
ASP.NET Web Services
Web API
Ignoring any logic errors that may occur in your programs, the way you are handling things in your client whenever it is doing an action it is doing it on the GUI thread. This will make your application seem like it is locking but instead it is executing your logic on the GUI thread.
The same problem is occurring on the server. It accepts a connection, and then goes on to receive the file. It will not be able to receive any other connection until it finished receiving the file.
The server is not without problems either because it is never checking if it receives 0 bytes from the socket. Which means that the client closed its end of the connection. You are simply assuming that if you receive less than 1024 you are receiving your last part of the file. This is simply not true for TCP. You only know you received the last part if you receive 0 bytes. TCP is a byte streaming protocol you cannot assume you will be receiving blocks of 1024 bytes. It is likely that this will be in fact the case, but you should not code it like that. Check for reception of 0 bytes. On the client you did check for 0 bytes, I am puzzled why you did not do the same on the server. The problematic part is this :
byte[] data = new byte[1024];
int size = client.Receive(data);
output.Write(data, 0, size);
count += size;
if(size<1024) //you can only break if the size is 0
{
break;
}
There are probably more bugs. As the other answer also indicated some other issues.
So I'm trying to make a chatroom. I use TCP Sockets to send all information. But when I send a byte array the receiving socket's buffer has the last byte missing. it's 0 cause the byte array is 255 big, so the left over bytes are 0. I've checked through debug mode, at the point when the packet was sent, the array is correct. But at the break point when the sockets finished receiving it's missing that last byte. Any reason why this would be happening? I saw another thread which said you had to stop the thread till the sockets finished receiving, but I'm using AsyncCallBack which to my knowledge is called when the socket has finished receiving.
Also the first byte in each packet is supposed to be the string size (To account for the extra empty bytes).
PacketClass:
public class Packet
{
public byte[] Buffer;
public Packet(string message)
{
Buffer = new byte[255];
byte[] messageArray = Encoding.ASCII.GetBytes(message);
Buffer[0] = (byte)messageArray.GetLength(0);
for (int i = 0; i < messageArray.GetLength(0); i++)
{
Buffer[i+1] = messageArray[i];
}
}
public Packet(byte[] buffer)
{
Buffer = buffer;
}
public string GetMessage()
{
List<byte> messageBuffer = new List<byte>();
for (int i = 1; i <= Buffer[0]; i++)
{
messageBuffer.Add(Buffer[i]);
}
return Encoding.ASCII.GetString(messageBuffer.ToArray());
}
}
SocketClass:
class Client
{
public static List<Client> connectedClients = new List<Client>();
public Socket Sock;
public byte[] Buffer = new byte[255];
public Client(Socket pSock)
{
Sock = pSock;
Sock.BeginReceive(Buffer, 0, 255, SocketFlags.None, new AsyncCallback(RecieveCallBack), null);
}
public void Send(byte[] pBuffer)
{
Sock.BeginSend(pBuffer, 0, pBuffer.GetLength(0), SocketFlags.None, new AsyncCallback(SendCallBack), null);
}
public void SendCallBack(IAsyncResult AR)
{
Console.WriteLine("Sent");
Sock.EndSend(AR);
}
public void RecieveCallBack(IAsyncResult AR)
{
Sock.EndReceive(AR);
Packet recPacket = new Packet(Buffer);
Console.WriteLine(recPacket.GetMessage());
Sock.BeginReceive(Buffer, 0, 255, SocketFlags.None, new AsyncCallback(RecieveCallBack), null);
}
}
Main Server Program
`class Client
{
public static List connectedClients = new List();
public Socket Sock;
public byte[] Buffer = new byte[255];
public Client(Socket pSock)
{
Sock = pSock;
Sock.BeginReceive(Buffer, 0, 255, SocketFlags.None, new AsyncCallback(RecieveCallBack), null);
}
public void Send(byte[] pBuffer)
{
Sock.BeginSend(pBuffer, 0, pBuffer.Length, SocketFlags.None, new AsyncCallback(SendCallBack), null);
}
public void SendCallBack(IAsyncResult AR)
{
Console.WriteLine("Sent");
Sock.EndSend(AR);
}
public void RecieveCallBack(IAsyncResult AR)
{
Sock.EndReceive(AR);
Packet recPacket = new Packet(Buffer);
Console.WriteLine(recPacket.GetMessage());
Sock.BeginReceive(Buffer, 0, 255, SocketFlags.None, new AsyncCallback(RecieveCallBack), null);
}
}`
Solved
The error was in the send method which I didn't include.
sock.BeginSend(sentBuffer[0].Buffer, 0, sentBuffer[0].Buffer[0], SocketFlags.None, new AsyncCallback(SendCallBack), null);
sentBuffer is a list, to be a queue. I changed it to 255 and it workd
I'm working on a project where I communicate serialized commands over TCP/IP. I have it working when I'm on local host but when I run my listener on a different server, it fails randomly when trying to deserialize commands at the listeners side.
Exceptions thrown are: 'Attempting to deserialize an empty stream.' and 'End of Stream encountered before parsing was completed.' from the serializer
When I run a sequence of commands individually it works fine but when I create threads and runs several sequences simultaneously it fails.
The Listener create listeners on 4 different ports and the client runs 1 thread for each port. When one of the threads reaches the end of a sequence it terminates.
I tried to make my client singleton and also tried Mutex. But still the same problem.
Here is my client:
public class TcpIpCommunicator : ICommunicator, IDisposable
{
private Dictionary<int,TcpClient> clientSockets = new Dictionary<int,TcpClient>();
public IInverterCommand ReadAsyncCommand { set; get; }
private static TcpIpCommunicator tcpIpCommunicator;
private TcpIpCommunicator()
{
}
public static TcpIpCommunicator GetInstance()
{
if(tcpIpCommunicator == null)
tcpIpCommunicator = new TcpIpCommunicator();
return tcpIpCommunicator;
}
public void Send(IInverterCommand command, int id)
{
var serializer = new Serializer();
MemoryStream stream = serializer.SerializeMultipleObjects(command);
var _bytes = stream.GetBuffer();
var networkStream = clientSockets[id].GetStream();
networkStream.Write(_bytes, 0, _bytes.Length);
networkStream.Flush();
}
public IInverterCommand Read(int id)
{
var memoryStream = new MemoryStream();
byte[] buffer;
var networkStream = clientSockets[id].GetStream();
do
{
buffer = new byte[clientSockets[id].ReceiveBufferSize];
int sizeRead = networkStream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, sizeRead);
} while (networkStream.DataAvailable);
networkStream.Flush();
memoryStream.Position = 0;
var serializer = new Serializer();
return serializer.DeSerializeMultipleObject(memoryStream);
}
public void ReadAsync(object id)
{
ReadAsyncCommand = Read((int)id);
}
public void Dispose()
{
foreach (var tcpClient in clientSockets.Values)
{
tcpClient.Close();
}
}
public int Connect(string ip, int port)
{
var tcpClient = new TcpClient();
tcpClient.ReceiveTimeout = int.MaxValue;
tcpClient.SendTimeout = int.MaxValue;
tcpClient.Connect(ip, port);
int key = findKey();
clientSockets.Add(key, tcpClient);
return key;
}
public void DestroyConnection(int id)
{
clientSockets[id].Close();
clientSockets.Remove(id);
}
private int findKey()
{
int key = 0;
while(clientSockets.ContainsKey(key))
{
key++;
}
return key;
}
}
And my server side code is here:
public class TCPListener : IDisposable
{
private readonly TcpListener _serverSocket;
private NetworkStream _networkStream;
private readonly TcpClient _clientSocket = default(TcpClient);
public TCPListener(int port)
{
_serverSocket = new TcpListener(port);
_serverSocket.Server.ReceiveTimeout = int.MaxValue;
_serverSocket.Server.SendTimeout = int.MaxValue;
_serverSocket.Start();
_clientSocket = _serverSocket.AcceptTcpClient();
}
public void Send(IInverterCommand message)
{
_networkStream = _clientSocket.GetStream();
var serialize = new Serializer();
var stream = serialize.SerializeMultipleObjects(message);
var _bytes = stream.GetBuffer();
if (_bytes.Length > _clientSocket.ReceiveBufferSize)
{
byte[] bytes = new byte[_clientSocket.ReceiveBufferSize];
for (int i = 0; i < _bytes.Length; i += _clientSocket.ReceiveBufferSize)
{
for (int j = 0; j < _clientSocket.ReceiveBufferSize && i + j != _bytes.Length; ++j)
{
bytes[j] = _bytes[i + j];
}
_networkStream.Write(bytes, 0, _clientSocket.ReceiveBufferSize);
}
}
else
{
_networkStream.Write(_bytes, 0, _bytes.Length);
}
Thread.Sleep(50);
_networkStream.Flush();
}
public IInverterCommand ReadCommand()
{
_networkStream = _clientSocket.GetStream();
var memoryStream = new MemoryStream();
do
{
var buffer = new byte[_clientSocket.ReceiveBufferSize];
int sizeRead = _networkStream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, sizeRead);
Thread.Sleep(50);
} while (_networkStream.DataAvailable);
_networkStream.Flush();
memoryStream.Position = 0;
var serializer = new Serializer();
return serializer.DeSerializeMultipleObject(memoryStream);
}
public void Dispose()
{
_clientSocket.Close();
_serverSocket.Stop();
}
}
Here is typically calling code at client side:
IInverterCommand command = new SoftwareUpdateInverterCommand();
tcpClient.Send(command, tcpId);
var thread = new Thread(tcpClient.ReadAsync);
thread.Start(tcpId);
if (!thread.Join(timeout))
{
thread.Abort();
tcpClient.DestroyConnection(tcpId);
return;
}
And the server side calling code:
//Recieving CMD on software update
TcpListener = new TCPListener((int)port);
var command = TcpListener.ReadCommand();
//Sending OK back to server
command.Message = "OK";
TcpListener.Send(command);
I recommend you go read up on the subject of thread synchronization and what it means and how to use it. Also what happens when you share things between threads that are not thread safe.
You have no thread synchronization in your code at all, yet you read and write to a Dictionary (not safe) from at least 2 different threads without any synchronization.
That alone can lead to bugs that are very hard to trace and lots of different phenomenon you rather not deal with.
You really need to go reading on how to do threading properly.
First i am n00b in socket programming. So i decided to write simple data over lan tcp server
My server code that handles incomming data is
private void HandleClientComm(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
clientStream.ReadTimeout = 10;
int size = 4096 * 1000;
byte[] message = new byte[size];
byte[] All = new byte[0];
int bytesRead;
string error = "";
lock (this)
{
while (true)
{
All = new byte[0];
while (true)
{
bytesRead = 0;
try
{
bytesRead = clientStream.Read(message, 0, size);
All = AddBArrays(All, message, bytesRead);
}
catch
{
break;
}
if (bytesRead == 0)
{
break;
}
}
if (All.Length > 0)
{
Message m = (Message)Tools.ByteArrayToObject(All);
OnRecived(new RecivedArgs("localhost", (Message)Tools.ByteArrayToObject(All)));
}
}
tcpClient.Close();
}
}
byte[] AddBArrays(byte[] ar1, byte[] ar2, int read)
{
byte[] concat = new byte[ar1.Length + read];
if (ar1.Length != 0)
System.Buffer.BlockCopy(ar1, 0, concat, 0, ar1.Length);
System.Buffer.BlockCopy(ar2, 0, concat, ar1.Length, read);
return concat;
}
it works but have some issues. It fales receiving files bigger then 100 mbs or smthng and also if i send data very often interval < 800 then data is lost. how should i improve my code? The large file issue is not so important the primary issue is the data loss in fast data sending.
tnx for help
Ok i now updated the code by the suggestions
private void HandleClientComm(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
clientStream.ReadTimeout = 10;
int size = 4096 * 1000;
List<byte> Test = new List<byte>();
byte[] message = new byte[size];
byte[] All = new byte[0];
int bytesRead;
while (true)
{
//All = new byte[0];
while (true)
{
bytesRead = 0;
try
{
bytesRead = clientStream.Read(message, 0, size);
for (int i = 0; i < bytesRead; i++)
{
Test.Add(message[i]);
}
}
catch
{
break;
}
if (bytesRead == 0)
{
break;
}
}
if (Test.Count > 0)
{
Message m = (Message)Tools.ByteArrayToObject(Test.ToArray());
OnRecived(new RecivedArgs("localhost", m));
Test = new List<byte>();
}
}
tcpClient.Close();
}
but the issues still there
Edit--> large file issue fixed it was just a 'System.OutOfMemoryException' but it didn't throw a error.
The All byte array you should change to a List<byte>. You are creating instances like crazy right now. The GC is probably working a lot more than it needs to. This might be slowing it down so much that it can't keep up.
Not really related to sockets:
Make size a const
NEVER lock this. Create a private field that you can lock. In fact, I don't even think you need a lock here.
remove the error string.
OK i solved the problem.
I simple send to much data to fast so data loss was unavoidable.
My optimized code is
private void HandleClientComm(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
int size = 1;
byte[] message = new byte[1];
int bytesRead;
while (true)
{
bytesRead = 0;
if (clientStream.DataAvailable)
bytesRead = clientStream.Read(message, 0, 1);
if (bytesRead > 0)
{
OnRecived(new RecivedArgs("tick", null));
}
Thread.Sleep(1);
}
}
i have tested intervals as low as 1 ms and no data loss :)
thanks for your help