I write an Windows C# application which can create up to 16 threads. Each thread creates a socket for a remote device. Each thread send commands to read the device status. (every 300 ms) It is OK when I create one or two threads to read the status. But when I create 10 threads to read device status, I will get wrong data in the socket receive buffer.
Please refer to the following for my socket driver code:
class SocketClient {
private IPAddress ipAddress;
private IPEndPoint remoteEP;
private Socket mSocket;
private SocketAsyncEventArgs e = new SocketAsyncEventArgs();
private System.Timers.Timer timer_connection;
private static byte[] response = new byte[1024];
private Boolean waittingConnectDone;
private Boolean boolConnected;
public SocketClient() {
}
private byte[] acknowledge = null;
public Boolean Connect(String IP, int Port) {
Boolean bRet = true;
ipAddress = IPAddress.Parse(IP);
remoteEP = new IPEndPoint(ipAddress, Port);
mSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//mSocket.ReceiveTimeout = GlobalVar.ethernet_timeout;
mSocket.ReceiveTimeout = 500;
try {
waittingConnectDone = false;
e.RemoteEndPoint = remoteEP;
e.UserToken = mSocket;
e.Completed += new EventHandler<SocketAsyncEventArgs>(e_Completed);
mSocket.ConnectAsync(e);
if (timer_connection != null) {
timer_connection.Dispose();
} else {
timer_connection = new System.Timers.Timer();
}
timer_connection.Interval = 2000;
timer_connection.Elapsed += new ElapsedEventHandler(timer_connection_Tick);
timer_connection.Start();
while (true) {
if (waittingConnectDone)
break;
Application.DoEvents();
}
bRet = boolConnected;
//sender.Connect(remoteEP);
} catch {
Debug.WriteLine("### Ethernet ### Connection Error!");
bRet = false;
}
return bRet;
}
private void e_Completed(object sender, SocketAsyncEventArgs e) {
boolConnected = true;
waittingConnectDone = true;
}
private void timer_connection_Tick(object sender, EventArgs e) {
if (!mSocket.Connected) {
timer_connection.Stop();
boolConnected = false;
waittingConnectDone = true;
}
}
public void Disconnect() {
try {
mSocket.Shutdown(SocketShutdown.Both);
mSocket.Close();
} catch {
}
}
Each Thread use following code to read the device status:
private byte[] acknowledge = null;
private static byte[] response = new byte[1024];
public byte[] sendCommand(byte[] Cmp_TxData) {
try {
bytesSent = mSocket.Send(Cmp_TxData);
bytesRec = mSocket.Receive(response);
acknowledge = new byte[bytesRec];
Array.Copy(response, 0, acknowledge, 0, bytesRec);
}
catch
{
acknowledge = null;
}
return acknowledge;
}
And the buffer data error is something like following:
TX --> 00-03-01-F4-00-03-44-14
RX <-- 00-00-00-00-00-00-00-00-00-00-00
Sometime I read correct data, but sometimes the data are all 0!
Is there anything wrong with my socket driver?
Really appreciate your help!!
I have not checked all of the posted code (I suspect there may be more errors, multithreading is hard if you try to do it all yourself), but this line:
private static byte[] response = new byte[1024];
Is definitely a source for the types of errors you report. This line declares a buffer that is shared between all threads.
So if multiple threads do this:
bytesRec = mSocket.Receive(response);
The data in this response buffer can be concurrently modified.
To resolve this specific error, you could ensure that you create a static buffer for each thread using [ThreadStatic] or ThreadLocal (see for example http://reedcopsey.com/2009/11/12/thread-specific-data-becomes-easier-in-net-4-0-via-threadlocalt/).
Related
I'm trying to learn Game Networking programming so I started a very simple async udp socket system using Unity, however when I added JSON serialization things got a little bit weird.
I can connect to the server and send packets with the SendPacket(string msg) method and it will receive it fine on the server side. And it will work fine as long as the size of the msg variable is the same size or bigger. So if I send a string "Hello" and then one "Hello World" will work fine and be able to print it on the other size. However if I was now to send another packet with the string "Hi" nothing would happen and no more connections will work on that socket neither sending new packets.
I have checked and I'm receiving the packet over the network and it's not empty it has the information however it seems to stop when it gets to the code:
Packet p = e.Buffer.FromJsonBinary<Packet>();
Which is on the OnConnect function on my server side.After that function It seems my server stops listening not accepting new connections and no other packets will be receive, I suspect it somehow stops my async process.
For the Json Serialization I'm using JsonUtility from Unity.
Any ideas what is happening?
Server Class
public class Server
{
private static string _protocolID = "hash";
private static ushort _port = 11000;
private Socket _socket;
private SocketAsyncEventArgs _event;
private List<Socket> _connections = new List<Socket>();
public void Start()
{
Listen();
}
private bool Listen()
{
// Create UDP Socket
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
// Set the socket into non-blocking mode
_socket.Blocking = false;
try
{
_socket.Bind(new IPEndPoint(IPAddress.Any, _port));
_event = new SocketAsyncEventArgs();
_event.Completed += OnConnect;
byte[] buffer = new byte[1024];
_event.SetBuffer(buffer, 0, 1024);
//_socket.ReceiveAsync(_event);
StartListening(_event);
}
catch (Exception e)
{
Debug.LogException(e);
return false;
}
return true;
}
private void StartListening(SocketAsyncEventArgs e)
{
//e.AcceptSocket = null;
_socket.ReceiveAsync(e);
}
private void OnConnect(object sender, SocketAsyncEventArgs e)
{
if (e.BytesTransferred > 0)
{
if (e.SocketError != SocketError.Success)
Debug.Log("ERROR"); // TODO: Close Socket
Packet p = e.Buffer.FromJsonBinary<Packet>();
if (p._protocolID != _protocolID)
Debug.Log("Protocol Error");
Debug.Log("Connect:" + p._msg);
if (!_socket.ReceiveAsync(e))
{
// Call completed synchonously
StartListening(e);
}
}
else
{
Debug.Log("No data");
}
}
}
Client Class
public class Client
{
private static string _protocolID = "hash";
private ushort _port = 11000;
private string _ip = "127.0.0.1";
private Socket _socket;
private SocketAsyncEventArgs _event;
public bool Connect()
{
// Create UDP Socket
_socket = new Socket(SocketType.Dgram, ProtocolType.Udp);
// Set the socket into non-blocking mode
_socket.Blocking = false;
IPEndPoint ip = new IPEndPoint(IPAddress.Parse(_ip), _port);
_event = new SocketAsyncEventArgs();
//_event.Completed += Callback;
try
{
_socket.Connect(ip);
Debug.Log($"Connection was to {_ip} was sucessfull");
}
catch(Exception e)
{
Debug.LogException(e);
Debug.Log("Couldn't connect Socket");
return false;
}
return true;
}
public void SendPacket<T>(T t)
{
try
{
if (_socket == null)
Debug.Log("Null socket");
// Encode the data string into a byte array.
byte[] buffer = t.ToJsonBinary();
_event.SetBuffer(buffer, 0, buffer.Length);
// Send the data through the socket.
//_socket.SendAsync(_event);
bool willRaiseEvent = _socket.SendAsync(_event);
//if (!willRaiseEvent)
//{
// SendPacket<T>(t);
//}
}
catch (SocketException e)
{a
Debug.LogException(e);
Debug.Log("Couldn't Send Packet");
}
}
public void SendPacket(string msg)
{
Packet packet = new Packet(_protocolID, msg);
SendPacket<Packet>(packet);
}
}
Json Serialization
public static byte[] ToJsonBinary(this object obj)
{
return Encoding.ASCII.GetBytes(JsonUtility.ToJson(obj));
}
public static T FromJsonBinary<T>(this byte[] data)
{
return JsonUtility.FromJson<T>(Encoding.ASCII.GetString(data));
}
Packet
[Serializable]
public struct Packet
{
public string _protocolID;
public string _msg;
public Packet(string protocolID, string msg)
{
_protocolID = protocolID;
_msg = msg;
}
}
Edit: I found out it's crashing due to Json Utility
System.ArgumentException: JSON parse error: The document root must not follow by other values.
Json string before being sent (top), Json string reiceived at the server (bottom)
It seems on the server the buffer isn't being cleared so it still has information in it.
Okay so it seems the problem was due to how SocketAsyncEventArgs works. Since I'm calling recursively and passing the same event it never clears it's buffer.
if (!_socket.ReceiveAsync(e))
{
OnConnect(null, e);
}
I found two solutions one was to do pass a new SocketAsyncEventArgs which will have a clear buffer. But I believe a better idea would be to make a pool to handle and reuse a set of predefined SocketAsyncEventArgs instead of constantly creating new ones.
Another solution I found was to simple set a new buffer as a way to clear the last one.
byte[] buffer = t.ToJsonBinary();
_event.SetBuffer(buffer, 0, buffer.Length);
This is my first TCP listener program,
I could receive, parse and display data successfully from another PC.
But can you please check why this listener is not receiving another data ?
I want to update it everytime time when a client sends data. But its not updating once received data.
Here is my code:
public partial class FeederControlMonitor : Form
{
public string Status = string.Empty;
public Thread T = null;
public FeederControlMonitor()
{
InitializeComponent();
}
private void FeederControlMonitor_Load(object sender, EventArgs e)
{
label1.Text = "Server is Running...";
ThreadStart Ts = new ThreadStart(StartReceiving);
T = new Thread(Ts);
T.Start();
}
public void StartReceiving()
{
ReceiveTCP(9100);
}
public void ReceiveTCP(int portN)
{
TcpListener Listener = null;
try
{
Listener = new TcpListener(IPAddress.Any, portN);
Listener.Start();
}
catch (Exception ex)
{
File.WriteAllText(#"C:\\Drive\\ex.txt", ex.Message);
Console.WriteLine(ex.Message);
}
try
{
Socket client = Listener.AcceptSocket();
byte[] data = new byte[10000];
int size = client.Receive(data);
while (true)
{
client.Close();
ParseData(System.Text.Encoding.Default.GetString(data));
}
Listener.Stop();
}
catch (Exception ex)
{
File.WriteAllText(#"C:\\Drive\\ex.txt", ex.Message);
}
}
public void ParseData(string data)
{
var useFulData = data.Substring(data.IndexOf("F1")).Replace(" ", "");
useFulData = useFulData.Remove(useFulData.IndexOf("<ETX>"));
string[] delimeters = { "<DEL>", "<ESC>" };
var listOfValues = useFulData.Split(delimeters, StringSplitOptions.None).ToList();
int pos = 0;
for (int i = 1; i < listOfValues.Count; i += 2, pos++)
{
listOfValues[pos] = listOfValues[i];
}
listOfValues.RemoveRange(pos, listOfValues.Count - pos);
txtTubeName.Text = listOfValues[0];
txtCID.Text = listOfValues[1];
txtLocation.Text = listOfValues[2];
txtGender.Text = listOfValues[3];
txtAge.Text = listOfValues[4];
}
private void btnExit_Click(object sender, EventArgs e)
{
T.Abort();
this.Close();
}
}
Thanks in advance.
To much to explain where are errors. Here is simple multithread TcpServer.
// Initialize listener.
IPAddress address = new IPAddress(new byte[] { 127, 0, 0, 1 });
TcpClient client;
// Bind to address and port.
TcpListener listener = new TcpListener(address, 12345);
// Start listener.
listener.Start();
// In endless cycle accepting incoming connections.
// Actually here must be something like while(_keepWork)
// and on some button code to make _keepWork = false to
// stop listening.
while (true)
{
client = listener.AcceptTcpClient();
// When client connected, starting BgWorker
// use "using" statement to automatically free objects after work.
using (BackgroundWorker bgWorker = new BackgroundWorker())
{
// EventHandler.
bgWorker.DoWork += BgWorker_DoWork;
// Past client as argument.
bgWorker.RunWorkerAsync(client);
}
}
And method to handle connection (Edit: with read data part):
private static void BgWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Get argument as TcpClient.
TcpClient client = e.Argument as TcpClient;
// Get stream from client.
NetworkStream netStream = client.GetStream();
// Input buffer to read from stream.
int inBuffSize = 1024;
byte[] inBuff = new byte[inBuffSize];
// Temporary buffer.
byte[] tempBuff;
// Result data recieved from client.
List<byte> data = new List<byte>();
// Read bytes from client into inputbuffer
int dataSize = netStream.Read(inBuff, 0, inBuffSize);
// If data recieved add to result.
while (dataSize > 0)
{
// Create new buffer.
tempBuff = new byte[dataSize];
// Copy data from inputBuffer to tempBuffer.
Array.Copy(inBuff, tempBuff, dataSize);
// Add to result.
data.AddRange(tempBuff);
// Read once more to check if any data still could be recieved.
dataSize = netStream.Read(inBuff, 0, inBuffSize);
}
}
Please see code below, it is not very stable in my windows service, after starting for a while, it throw the error:
System.Net.Sockets.SocketException: the connected party did not
properly respond after a period of time, or established connection
failed because connected host has failed to respond
The error was catched on : readsize = listensocket2.Receive(buffer_receive);
The project is on a data communication center, the clients are GPRS DTU socket client.
Please help!!!
Part I:
protected override void OnStart(string[] args)
{
IPEndPoint endpoint = new IPEndPoint(RemsSocket.myip, RemsSocket.webport);
Socket listensocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listensocket.Bind(endpoint);
listensocket.Listen(200);
Thread th = new Thread(new ParameterizedThreadStart(RemsSocket.listen_send));
th.IsBackground = true;
th.Start(listensocket);
}
Part II:
public static void listen_send(object source)
{
while (true)
{
done.Reset();
Socket listensocket = (Socket)source;
listensocket.BeginAccept(new AsyncCallback(acceptcallback_send), listensocket);
done.WaitOne();
}
}
public static void acceptcallback_send(IAsyncResult ar)
{
done.Set();
Socket socket = (Socket)ar.AsyncState;
Socket listensocket2 = socket.EndAccept(ar);
byte[] buffer_validate = new byte[buffersize];
string hostcode = "";
byte[] buffer_send = Hex.HexStringToByteArray(validate_code);
listensocket2.Send(buffer_send, 0, buffer_send.Length, SocketFlags.None);
listensocket2.Receive(buffer_validate);
int readsize = buffer_validate.Length; //listensocket2.EndReceive(ar);
if (readsize > 0)
{
bool success = FirstValidate(buffer_validate, ref hostcode);
bool validate = BLL.ExtHostBLL.CanCommunicate(hostcode);
if (success && validate)
{
LoopSendReceive(hostcode, listensocket2);
}
}
}
Part III:
public static void LoopSendReceive(string hostcode, Socket listensocket2)
{
listensocket2.Blocking = true;
byte[] buffer_receive = new byte[buffersize];
bool validate = BLL.ExtHostBLL.CanCommunicate(hostcode);
while (validate)
{
Thread.Sleep(500);
int readsize;
bool success;
IPEndPoint clientipe = (IPEndPoint)listensocket2.RemoteEndPoint;
byte commandByte = Hex.HexStringToByteArray(command_8);
listensocket2.Send(commandByte, 0, commandByte.Length, SocketFlags.None);
listensocket2.ReceiveTimeout = 5000;
int countSocketException = 0;
readsize = listensocket2.Receive(buffer_receive);
if (readsize != 0)
{
clientipe = (IPEndPoint)listensocket2.RemoteEndPoint;
ClientDto client = hostList.FirstOrDefault(c => c.Ip == clientipe.Address.ToString());
ParseChannel.AlertStatus[] data;
ParseChannel channel = new ParseChannel();
success = channel.Parse(buffer_receive, client, out data);
if (!success)
{
continue;
}
SaveData(data, client);
}
}
}
I have searched many examples and tutorials and what not but I cant for the life of me figure out what im doing wrong here... If I send several messages to this server I made only the first is printed in the Console.Writeline command and the rest is never printed... I must be doing something fundametally wrong but I really cant find it ... :S
This is the server code:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms.VisualStyles;
namespace HL7_Manager
{
public class MonitorServer
{
private int _port;
private Socket _serverSocket;
private List<ClientObject> _clients;
public bool IsConnected { get; set; }
public MonitorServer(int port)
{
_port = port;
_clients = new List<ClientObject>();
}
public void StartListening()
{
_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Thread listenThread = new Thread(new ThreadStart(ListenerThread));
listenThread.IsBackground = true;
listenThread.Start();
}
public void StopListening()
{
IsConnected = true;
_serverSocket.Close();
while (_clients.Count > 0)
{
_clients[0].KeepProcessing = false;
_clients[0].ClientSocket.Close();
_clients.RemoveAt(0);
}
}
private void ListenerThread()
{
_serverSocket.Bind(new IPEndPoint(IPAddress.Any, _port));
_serverSocket.Listen(100);
Console.WriteLine("Listening on port 8000");
while (true)
{
Socket clientSocket = _serverSocket.Accept();
ClientObject client = new ClientObject();
client.KeepProcessing = true;
client.ClientSocket = clientSocket;
_clients.Add(client);
ParameterizedThreadStart ptStart = new ParameterizedThreadStart(ProcessClientThread);
Thread processThread = new Thread(ptStart);
processThread.IsBackground = true;
processThread.Start(client);
clientSocket = null;
client = null;
}
}
private void ProcessClientThread(object clientObj)
{
Console.WriteLine("Client connected");
ClientObject client = (ClientObject) clientObj;
Socket clientSocket = client.ClientSocket;
byte[] buffer = new byte[clientSocket.ReceiveBufferSize];
int receiveCount = 0;
while (client.KeepProcessing)
{
try
{
receiveCount = clientSocket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
Console.WriteLine(Encoding.ASCII.GetString(buffer));
}
catch (Exception ex)
{
if (!client.KeepProcessing)
return;
Console.WriteLine(ex.Message);
}
}
clientSocket.Close();
_clients.Remove(client);
}
}
}
Here is the method you should definitely change and how to change it.
private void ProcessClientThread(object clientObj)
{
Console.WriteLine("Client connected");
ClientObject client = (ClientObject)clientObj;
Socket clientSocket = client.ClientSocket;
byte[] buffer = new byte[clientSocket.ReceiveBufferSize];
int receiveCount = 0;
while (client.KeepProcessing)
{
try
{
receiveCount = clientSocket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
if (receiveCount == 0)
break; //the client has closed the stream
var ret = Encoding.ASCII.GetString(buffer, 0, receiveCount);
Console.WriteLine(ret);
}
catch (Exception ex)
{
if (!client.KeepProcessing)
return;
Console.WriteLine(ex.Message);
}
}
clientSocket.Close();
_clients.Remove(client);
}
Check how many bytes you really received.
TCP is a streaming protocol this means that if you client is doing several sends of small messages right one after the other, you will receive them in one go at the receiver.
If there happens to be a null character in your receive buffer you might think you did not receive all those string, but actually you did.
Check this by inspecting how many bytes you received and by checking the buffer content.
If you made this mistake there might be some deeper problem in your code. The fact that TCP is streaming makes it a bit more complex
I have currently-working code which sends raw data to a printer by writing a temporary file, then using File.Copy() to send it to the printer. File.Copy() supports both local ports, like LPT1 and shared printers like \\FRONTCOUNTER\LabelPrinter.
However, now I'm trying to get it working with a printer that's directly on the network: 192.168.2.100, and I can't figure out the format to use.
File.Copy(filename, #"LPT1", true); // Works, on the FRONTCOUNTER computer
File.Copy(filename, #"\\FRONTCOUNTER\LabelPrinter", true); // Works from any computer
File.Copy(filename, #"\\192.168.2.100", true); // New printer, Does not work
I know it's possible to "Add a printer" from each computer, but I'm hoping to avoid that - the second line of code above works from any computer on the network automatically, with no configuration required. I also know it's possible to P/Invoke the windows print spooler, and if that's my only option I may take it, but that's much more code overhead than I'd like to have.
Ideally, someone will have either a way to make File.Copy() work or a similar C# statement which will accept a network IP.
You can use sockets and send the data straight to that IP address. Should pretty much be the same as File.Copy. I just tried it out and that worked.
I just sent some text but here is the code that I used
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientSocket.NoDelay = true;
IPAddress ip = IPAddress.Parse("192.168.192.6");
IPEndPoint ipep = new IPEndPoint(ip, 9100);
clientSocket.Connect(ipep);
byte[] fileBytes = File.ReadAllBytes("test.txt");
clientSocket.Send(fileBytes);
clientSocket.Close();
try this code:
public class PrintHelper
{
private readonly IPAddress PrinterIPAddress;
private readonly byte[] FileData;
private readonly int PortNumber;
private ManualResetEvent connectDoneEvent { get; set; }
private ManualResetEvent sendDoneEvent { get; set; }
public PrintHelper(byte[] fileData, string printerIPAddress, int portNumber = 9100)
{
FileData = fileData;
PortNumber = portNumber;
if (!IPAddress.TryParse(printerIPAddress, out PrinterIPAddress))
throw new Exception("Wrong IP Address!");
}
public PrintHelper(byte[] fileData, IPAddress printerIPAddress, int portNumber = 9100)
{
FileData = fileData;
PortNumber = portNumber;
PrinterIPAddress = printerIPAddress;
}
/// <inheritDoc />
public bool PrintData()
{
//this line is Optional for checking before send data
if (!NetworkHelper.CheckIPAddressAndPortNumber(PrinterIPAddress, PortNumber))
return false;
IPEndPoint remoteEP = new IPEndPoint(PrinterIPAddress, PortNumber);
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.NoDelay = true;
connectDoneEvent = new ManualResetEvent(false);
sendDoneEvent = new ManualResetEvent(false);
try
{
client.BeginConnect(remoteEP, new AsyncCallback(connectCallback), client);
connectDoneEvent.WaitOne();
client.BeginSend(FileData, 0, FileData.Length, 0, new AsyncCallback(sendCallback), client);
sendDoneEvent.WaitOne();
return true;
}
catch
{
return false;
}
finally
{
// Shutdown the client
this.shutDownClient(client);
}
}
private void connectCallback(IAsyncResult ar)
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
// Signal that the connection has been made.
connectDoneEvent.Set();
}
private void sendCallback(IAsyncResult ar)
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
// Signal that all bytes have been sent.
sendDoneEvent.Set();
}
private void shutDownClient(Socket client)
{
client.Shutdown(SocketShutdown.Both);
client.Close();
}
}
Network Helper class:
public static class NetworkHelper
{
public static bool CheckIPAddressAndPortNumber(IPAddress ipAddress, int portNumber)
{
return PingIPAddress(ipAddress) && CheckPortNumber(ipAddress, portNumber);
}
public static bool PingIPAddress(IPAddress iPAddress)
{
var ping = new Ping();
PingReply pingReply = ping.Send(iPAddress);
if (pingReply.Status == IPStatus.Success)
{
//Server is alive
return true;
}
else
return false;
}
public static bool CheckPortNumber(IPAddress iPAddress, int portNumber)
{
var retVal = false;
try
{
using (TcpClient tcpClient = new TcpClient())
{
tcpClient.Connect(iPAddress, portNumber);
retVal = tcpClient.Connected;
tcpClient.Close();
}
return retVal;
}
catch (Exception)
{
return retVal;
}
}
}