Thread sending data to wrong client. Is this a thread safety issue? - c#

I'm currently developing a server that deals with clients using a consumer/producer approach with threads and a blocking collection as shown below:
public class NetworkClient
{
private bool _started = false;
private int _clientId;
private readonly Socket _socket;
private static AutoResetEvent _lastMessageWasAckd = new AutoResetEvent(true);
private static BlockingCollection<byte[]> _messageQueue = new BlockingCollection<byte[]>();
public NetworkClient(Socket socket, int clientId)
{
this._socket = socket;
this._clientId = clientId;
}
public int getClientID() {
return _clientId;
}
public void SendMessage(string message)
{
Console.WriteLine("Adding to player's sending queue " + _clientId);
_messageQueue.Add(Encoding.ASCII.GetBytes(message));
}
public void Start()
{
Thread receiver = new Thread(new ThreadStart(ReceivingThreadProc));
Thread sender = new Thread(new ThreadStart(SendingThreadProc));
receiver.Start();
sender.Start();
this._started = true;
}
public void Stop()
{
this._started = false;
}
private void ReceivingThreadProc()
{
byte[] bytes = new byte[1024];
string data;
try
{
while (_started && _socket.Connected)
{
int numByte = this._socket.Receive(bytes);
data = Encoding.ASCII.GetString(bytes, 0, numByte);
if (numByte == 0)
{
break;
}
if (data == "ACK")
{
_lastMessageWasAckd.Set();
continue;
}
// Acknowledge the message
_socket.Send(Encoding.ASCII.GetBytes("ACK"));
ServerReceiver.onEvent(this._clientId, data);
}
}
catch (Exception e)
{
this._socket.Close();
}
}
private void SendingThreadProc()
{
while (_started && _socket.Connected)
{
_lastMessageWasAckd.WaitOne();
byte[] message = _messageQueue.Take();
Console.WriteLine("Sending the following message to client number: " + _clientId);
Console.WriteLine(System.Text.Encoding.ASCII.GetString(message));
_socket.Send(message);
_lastMessageWasAckd.Reset();
}
}
}
There will be an instance of NetworkClient created for every client that connects for the server. The issue is that sometimes a message is queued to be sent to client 1 (this is confirmed by the Console.Writeline in the SendMessage method) however that message is sent to client 0 (Shown by the console writeline in the SendingThreadProc method) instead. Is this due to a thread safety issue, or am I missing something entirely? This typically happens when two messages are sent right after one another.
Any help would be greatly appreciated.
EDIT:
As many people rightly pointed out I haven't added where I call SendMessage I'll put this class down below:
class NetworkServer
{
private int latestClient = 0;
private ServerReceiver _serverReceiver;
private static readonly Dictionary<int, NetworkClient> clients = new Dictionary<int, NetworkClient>();
public NetworkServer()
{
IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, 5656);
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this._serverReceiver = new ServerReceiver();
this._serverReceiver.start();
try
{
listener.Bind(endpoint);
listener.Listen(10);
while (true)
{
Socket clientSocket = listener.Accept();
this.OnClientJoin(clientSocket);
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public static void SendClientMessage(int clientId, string package, CommandType commandType,
Dictionary<string, object> data = null)
{
if (data == null)
{
data = new Dictionary<string, object>();
}
SendClientMessageRaw(clientId, new NetworkCommand(clientId, package, commandType, data).ToJson());
}
public static void SendClientMessageRaw(int id, string message)
{
Console.WriteLine("Supposed to send to client number " + clients[id].getClientID());
clients[id].SendMessage(message);
}
private void OnClientJoin(Socket socket)
{
// Add client to array, perform handshake?
NetworkClient networkClient = new NetworkClient(socket, latestClient);
clients.Add(latestClient, networkClient);
Console.WriteLine("player added :" + latestClient);
networkClient.Start();
latestClient++;
if (latestClient == 2)
{
SendClientMessage(1, "Test", CommandType.Ping, null);
}
}

Could it be because your message queue is static and therefore shared between all NetworkClients? Meaning client A can pick up a message for client B?
Might be as easy as removing static from the properties.

Related

How to implement Socket server that accept more message

I want to send at real time some data from Android application to server.
So I'm building a simple socket Server in c# like this:
class Program
{
private Socket listener;
static void Main(string[] args)
{
Program p = new Program();
p.socketServer();
}
public void socketServer()
{
int MAXBUFFER = 1024;
Console.WriteLine("SOCKET STARTED");
listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(new IPEndPoint(IPAddress.Any, 8080));
listener.Listen(10);
while (true)
{
Console.WriteLine("WAITING CONNECTION");
Socket socket = listener.Accept();
string receivedMessage = string.Empty;
while (true)
{
byte[] receivedBytes = new byte[MAXBUFFER];
int numBytes = socket.Receive(receivedBytes);
receivedMessage += Encoding.ASCII.GetString(receivedBytes, 0, numBytes);
if (receivedMessage.IndexOf("\n") > -1)
{
Console.WriteLine("MESSAGE FROM CLIENT: {0}", receivedMessage);
//break;
}
}
Console.WriteLine("MESSAGE FROM CLIENT: {0}", receivedMessage);
string replyMessage = "MESSAGE RECEIVED";
byte[] replyBytes = Encoding.ASCII.GetBytes(replyMessage);
}
}
public void shutdownServer()
{
listener.Shutdown(SocketShutdown.Both);
listener.Close();
}
}
And I'm building this class to send data from Android application:
public class TcpClient {
public static final String SERVER_IP = "192.168.110.50"; // computer IP address
public static final int SERVER_PORT = 8080;
private String mServerMessage;
private OnMessageReceived mMessageListener = null;
private boolean mRun = false;
private PrintWriter mBufferOut;
private BufferedReader mBufferIn;
private Socket socket;
/**
* Constructor of the class. OnMessagedReceived listens for the messages received from server
*/
public TcpClient(OnMessageReceived listener) {
mMessageListener = listener;
}
/**
* Sends the message entered by client to the server
*
* #param message text entered by client
*/
public void sendMessage(String message) {
socket.isConnected();
if (mBufferOut != null /*&& !mBufferOut.checkError()*/) {
mBufferOut.println(message);
}
}
/**
* Close the connection and release the members
*/
public void stopClient() {
Log.i("Debug", "stopClient");
// send mesage that we are closing the connection
//sendMessage(Constants.CLOSED_CONNECTION + "Kazy");
mRun = false;
if (mBufferOut != null) {
mBufferOut.flush();
mBufferOut.close();
}
mMessageListener = null;
mBufferIn = null;
mBufferOut = null;
mServerMessage = null;
}
public void run() {
mRun = true;
try {
//here you must put your computer's IP address.
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
//create a socket to make the connection with the server
socket = new Socket(SERVER_IP, SERVER_PORT);
//socket.connect(socket.getRemoteSocketAddress());
socket.isConnected();
try {
Log.i("Debug", "inside try catch");
mBufferOut = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())),
true);
} catch (Exception e) {
Log.e("TCP", "S: Error", e);
} finally {
}
} catch (Exception e) {
Log.e("TCP", "C: Error", e);
}
}
public interface OnMessageReceived {
public void messageReceived(String message);
}
}
And in the MainActivity I use this code:
tcpClient = new TcpClient(new TcpClient.OnMessageReceived() {
#Override
//here the messageReceived method is implemented
public void messageReceived(String message) {
//this method calls the onProgressUpdate
//publishProgress(message);
}
});
tcpClient.run();
After connect to server i user this code to send message:
for(int i=0; i<10; i++){
tcpClient.sendMessage("test " + i);
}
The problem is that only the first message arrive to the server. I think that the problem is on the socket server that lost the connection with the client.

Sockets - Not sending/receiving data

I'm starting a socket program, and am in the process of setting up a Server and two types of Clients (a requester and an arbiter). I'm in the middle of testing the connections, but they aren't quite working. Right now I just have a button for each form: an "Accept" button for the Arbiter and "Request" for the Requester. Each button should cause a popup on the other form, but neither is working. Also, I've noticed that when I close all programs, the Server is still running in my processes. What am I doing wrong?
Below is the Server code:
namespace FPPLNotificationServer
{
class Server
{
static Socket listenerSocket;
static List<ClientData> _clients;
static void Main(string[] args)
{
Console.WriteLine("Starting server on " + Packet.GetIP4Address());
listenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_clients = new List<ClientData>();
IPEndPoint ip = new IPEndPoint(IPAddress.Parse(Packet.GetIP4Address()), 4242);
listenerSocket.Bind(ip);
Thread listenThread = new Thread(ListenThread);
listenThread.Start();
}
static void ListenThread()
{
for (;;)
{
listenerSocket.Listen(0);
_clients.Add(new ClientData(listenerSocket.Accept()));
}
}
public static void Data_IN(object cSocket)
{
Socket clientSocket = (Socket)cSocket;
byte[] Buffer;
int readBytes;
for (;;)
{
try
{
Buffer = new byte[clientSocket.SendBufferSize];
readBytes = clientSocket.Receive(Buffer);
if(readBytes > 0)
{
Packet packet = new Packet(Buffer);
DataManager(packet);
}
}catch(SocketException ex)
{
Console.WriteLine("Client Disconnected");
}
}
}
public static void DataManager(Packet p)
{
switch (p.packetType)
{
case Packet.PacketType.Notification:
foreach(ClientData c in _clients)
{
c.clientSocket.Send(p.ToBytes());
}
break;
}
}
}
class ClientData
{
public Socket clientSocket;
public Thread clientThread;
public string id;
public ClientData()
{
this.id = Guid.NewGuid().ToString();
clientThread = new Thread(Server.Data_IN);
clientThread.Start(clientSocket);
SendRegistrationPacket();
}
public ClientData(Socket clientSocket)
{
this.clientSocket = clientSocket;
this.id = Guid.NewGuid().ToString();
clientThread = new Thread(Server.Data_IN);
clientThread.Start(clientSocket);
SendRegistrationPacket();
}
public void SendRegistrationPacket()
{
Packet p = new Packet(Packet.PacketType.Registration, "server");
p.Gdata.Add(id);
clientSocket.Send(p.ToBytes());
}
}
}
ServerData
namespace FPPLNotificationServerData
{
[Serializable]
public class Packet
{
public List<String> Gdata;
public int packetInt;
public bool packetBool;
public string senderID;
public PacketType packetType;
public string PlantName, ProductSegment, ProductCustomer;
public int PlantNumber;
public string ProductNumber, ProductAltNumber;
public string ProductDiscription;
public int ProductLine;
public string ProductClass, ProductLocation;
public int ProductMcDFactor;
public Packet(PacketType type, String senderID)
{
Gdata = new List<string>();
this.senderID = senderID;
this.packetType = type;
}
public Packet(byte[] packetBytes)
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream(packetBytes);
Packet p = (Packet)bf.Deserialize(ms);
ms.Close();
this.Gdata = p.Gdata;
this.senderID = p.senderID;
this.packetType = p.packetType;
this.packetBool = p.packetBool;
this.packetInt = p.packetInt;
}
public byte[] ToBytes()
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, this);
byte[] bytes = ms.ToArray();
ms.Close();
return bytes;
}
public static string GetIP4Address()
{
IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
foreach(IPAddress i in ips)
{
if(i.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
return i.ToString();
}
}
return "127.0.0.1";
}
public enum PacketType
{
Registration,
Chat,
Notification,
Request,
ArbiterDecision,
Accept,
Decline
}
}
}
Request Class:
namespace FPPLRequestClient
{
public partial class frm_Request : Form
{
public static Socket master;
public static string name;
public static string id;
public bool isConnected;
public frm_Request()
{
InitializeComponent();
string IP = "127.0.0.1";
master = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipEP = new IPEndPoint(IPAddress.Parse(IP), 4242);
try
{
master.Connect(ipEP);
isConnected = true;
}
catch (Exception)
{
isConnected = false;
}
string connectionStatus = isConnected ? "Connected" : "Disconnected";
this.lbl_Status.Text = "Status: " + connectionStatus;
Thread t = new Thread(Data_IN);
t.Start();
}
void Data_IN()
{
byte[] Buffer;
int readBytes;
while (isConnected)
{
try
{
Buffer = new byte[master.SendBufferSize];
readBytes = master.Receive(Buffer);
if(readBytes > 0)
{
DataManager(new Packet(Buffer));
}
}catch(SocketException ex)
{
isConnected = false;
this.Dispose();
}
}
}//END DATA IN
void DataManager(Packet p)
{
switch (p.packetType)
{
case Packet.PacketType.Registration:
id = p.Gdata[0];
break;
case Packet.PacketType.Accept:
//MessageBox.Show(p.ProductNumber);
this.lbl_Status.Text = p.ProductNumber + " accepted";
Invalidate();
break;
}
}
private void btn_Request_Click(object sender, EventArgs e)
{
Packet p = new Packet(Packet.PacketType.Request, id);
p.ProductNumber = "123456";
master.Send(p.ToBytes());
}
}
}
Arbiter Class:
namespace FPPLArbiterClient
{
public partial class frm_Arbiter : Form
{
public static Socket master;
public static string name;
public static string id;
public bool isConnected;
public frm_Arbiter()
{
InitializeComponent();
string IP = "127.0.0.1";
master = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipEP = new IPEndPoint(IPAddress.Parse(IP), 4242);
try
{
master.Connect(ipEP);
isConnected = true;
}
catch (Exception)
{
isConnected = false;
}
string connectionStatus = isConnected ? "Connected" : "Disconnected";
this.lbl_Status.Text = "Status: " + connectionStatus;
Thread t = new Thread(Data_IN);
t.Start();
}
void Data_IN()
{
byte[] Buffer;
int readBytes;
while (isConnected)
{
try
{
Buffer = new byte[master.SendBufferSize];
readBytes = master.Receive(Buffer);
if(readBytes > 0)
{
DataManager(new Packet(Buffer));
}
}catch(SocketException ex)
{
isConnected = false;
this.Dispose();
}
}
}//END DATA IN
void DataManager(Packet p)
{
switch (p.packetType)
{
case Packet.PacketType.Registration:
id = p.Gdata[0];
break;
case Packet.PacketType.Request:
MessageBox.Show(p.ProductNumber + " Requested from " + p.senderID);
break;
}
}
private void btn_Accept_Click(object sender, EventArgs e)
{
MessageBox.Show("Sending acceptance of 126456");
Packet p = new Packet(Packet.PacketType.Accept, id);
p.ProductNumber = "123456";
master.Send(p.ToBytes());
}
}
}
This is my first dive into socket programming.
To start with your last question first, Receive will block until data becomes available unless you specify a timeout. Since your threads are foreground threads, this will prevent your application from terminating. See https://msdn.microsoft.com/en-us/library/8s4y8aff(v=vs.110).aspx. Either use a timeout, and/or make your threads background threads causing them to terminate when you close your application's main thread. Set the created thread's IsBackground property to true to achieve this. (Also, in the article above notice the paragraph about Shutdown and the Receive method returning an empty array. This is your hint to gracefully close the connection on your side).
The TCP/IP stack will send data at its own discretion (Nagle's algorithm), meaning you'll occasionally receive a buffer containing several or partial messages. Since you have "silent" error handling in your thread, perhaps your thread terminates prematurely because of a corrupted message? Place everything you receive in a buffer and check the buffer for complete messages in a separate step/thread before passing them on to your message handler.
No clear answers here I'm afraid, but if the check for corrupted messages doesn't help, look at the Socket samples on MSDN. It's probably just a tiny detail you're missing.
you are making a fundamental and common TCP error. TCP is a byte oriented streaming protocol, not message oriented. Your receive code assumes that it receives one Packet when it reads. This is not guaranteed, you might receive 1 byte, 20 bytes, or whatever. You must loop in the receive till you get all of one message. This means you have to know when you have read it all. Either there needs to be a header or some sentinel at the end.

Hot to call a method from another thread or fire a event from another thread which gets handled on the main thread

I know, the title might be confusing, but I can't really express it better in short.
Basically, I'm writing a TCP Server. I don't have a Windows Form to show, the only thing the user sees is a TrayIcon.
My TCP Server class creates a thread for listening for clients, and then additional thread for every client handling communication. When all communication is done, I want to call a method on the main thread.
I've done it by firing a event from the client communication thread which gets handled on the main thread, and all worked fine until I wanted to add desktop notifications to my application. I've build a notification using WPF (iControlNotification) and wanted to show it in the event handler I mentioned before, but I'm getting a error message saying something like "The calling thread has to be a STA Thread".
Here's some code (I removed unnecessary party):
static class Program {
[...]
[STAThread]
static void Main() {
Log("iControlServerApplication started.");
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
trayIcon = new TrayIcon();
trayIcon.Display();
NotificationManager = new iControlNotificationManager();
server = new TCPServer();
server.CommandReceived += new TCPServer.CommandReceivedEventHandler(tcpServer_CommandReceived);
if (server.Start()) {
NotificationManager.ShowNotfication("iControl Server Application", "Server started. " + plugins.Count + " plugins loaded.");
Application.Run();
} else {
MessageBox.Show("Port " + server.Port + " is already in use. Server could not be started.", ApplicationName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
[...]
static void tcpServer_CommandReceived(object source, TCPServer.CommandReceivedEventArgs e) {
string toolTipText = "[" + e.Client.IPAddress + "] >> " + e.Command;
NotificationManager.ShowNotfication("iControl Server Application", toolTipText);
foreach (IiControlPlugin plugin in plugins) {
plugin.Handle(e.SplittedCommands, e.Client);
}
}
[...]
}
-
class TCPServer {
public delegate void CommandReceivedEventHandler(object source, CommandReceivedEventArgs e);
public event CommandReceivedEventHandler CommandReceived;
public class CommandReceivedEventArgs : EventArgs {
private string _command;
private string[] _splittedCommands;
private iControlClient _client;
public CommandReceivedEventArgs(string command, iControlClient client) {
_command = command;
_splittedCommands = command.Split(new Char[]{' '});
_client = client;
}
public string Command { get { return _command; } }
public string[] SplittedCommands { get { return _splittedCommands; } }
public iControlClient Client { get { return _client; } }
}
public TCPServer() {
this.tcpListener = new TcpListener(IPAddress.Any, Port);
this.icClients = new Dictionary<String, iControlClient>();
}
public Boolean Start() {
if (PortIsAvailable(Port)) {
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
Program.Log("ListeningThread started.");
return true;
} else {
return false;
}
}
private void ListenForClients() {
this.tcpListener.Start();
TcpClient client;
while (this.keepListening) {
try {
client = this.tcpListener.AcceptTcpClient();
} catch {
break;
}
iControlClient icClient = new iControlClient(client);
icClient.Thread = new Thread(new ParameterizedThreadStart(HandleClientCommunication));
icClient.Thread.Start(icClient);
}
Program.Log("Stop listening.");
}
private void HandleClientCommunication(object client) {
iControlClient icClient = (iControlClient)client;
NetworkStream clientStream = icClient.TCP.GetStream();
clientStream.ReadTimeout = 10;
int bufflen = 4096;
byte[] message = new byte[bufflen];
int bytesRead;
while (this.keepReceiving && icClient.keepConnected) {
bytesRead = 0;
try {
bytesRead = clientStream.Read(message, 0, bufflen);
} catch {
break;
}
if (bytesRead == 0) {
break;
}
ProcessReceivedData(icClient, ParseData(message, bytesRead));
}
Program.Log("[" + icClient.IPAddress + "] Connection closed.");
icClient.TCP.Close();
this.icClients.Remove(icClient.IPAddress);
}
private void ProcessReceivedData(iControlClient icClient, String[] commands) {
Program.Log("[" + icClient.IPAddress + "] >> " + String.Join(" ", commands));
if (this.CommandReceived != null) {
CommandReceived(this, new CommandReceivedEventArgs(String.Join(" ", commands), icClient));
}
NetworkStream clientStream = icClient.TCP.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes("::ok");
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
icClient.keepConnected = false;
}
}
-
public class iControlNotificationManager {
private iControlNotifications _notifications;
public void ShowNotfication(string caption, string message) {
if ((Boolean)Program.GetSetting("notifications", true) == false) return;
Dispatcher.CurrentDispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(
() => {
iControlNotification noti = new iControlNotification(caption, message);
noti.Show();
}));
}
}
-
public class iControlNotification : Window {
private iControlNotificationModel _notification;
public iControlNotification(string caption, string message) { // Here's the error
InitializeComponent();
_notification = new iControlNotificationModel() {
Caption = caption,
Message = message
};
this.DataContext = _notification;
}
}
So how should I call tcpServer_CommandReceived so that the Notification Window can be shown the right way?
I'm really stuck here, I really appreciate any help on this!
//How to call a method from another thread :
a) You can invoke it in other thread by passing SynchronizationContext object to it:
void Method(object s)
{
SynchronizationContext sync = s as SynchronizationContext;
sync.Post(delegate { // what to do in other thread}, null);
}
Then in code you run this method in new task, passing your sync context as object (for example):
Task t = Task.Factory.StartNew(Method, SynchronizationContext.Current);
b) You can create extension method for this purpose (here is example that i used in win forms application to update UI):
public static class ControlExtensions
{
/// Executes the Action asynchronously on the UI thread, does not block execution on the calling thread.
public static void UIThread(this Control #this, Action code)
{
if (#this.InvokeRequired)
{
#this.BeginInvoke(code);
}
else
{
code.Invoke();
}
}
}

Multiple clients issue on C# TCP

I'm kind of new to this ask-answer thing, please let me know if I've done any mistakes. I have this server program, running on my laptop. Server is a console application;
class Program
{
static void Main(string[] args)
{
Server sv = new Server("192.168.2.69", 52025);
sv.OnClientAccept += new ClientAcceptHandler(sv_OnClientAccept);
sv.OnClientMessage += new ClientMessageHandler(sv_OnClientMessage);
sv.Start();
}
static void sv_OnClientMessage(Server s, System.Net.Sockets.TcpClient client, string message)
{
Console.WriteLine("Message received from client " + GetClientIp(client) + ": \"" + message + "\"");
string msgToAll = message;
s.SendToAllClients(msgToAll);
Console.WriteLine("Message sent: \"" + message + "\" to the clients:");
foreach (TcpClient cl in s.Clients)
{
Console.WriteLine(GetClientIp(cl));
}
}
static void sv_OnClientAccept(Server s, System.Net.Sockets.TcpClient client)
{
Console.WriteLine("Client Accepted : " + GetClientIp(client));
}
static string GetClientIp(TcpClient Client)
{
return ((IPEndPoint)Client.Client.RemoteEndPoint).Address.ToString();
}
}
The Server class;
public delegate void ClientAcceptHandler(Server s, TcpClient client);
public delegate void ClientMessageHandler(Server s, TcpClient client, string message);
public class Server
{
private List<TcpClient> clients = new List<TcpClient>();
object lockObjc;
private TcpListener server;
private string ip;
private int port;
private Thread accepter, listener;
public List<TcpClient> Clients { get { return this.clients; } set { this.clients = value; } }
public event ClientAcceptHandler OnClientAccept;
public event ClientMessageHandler OnClientMessage;
public Server(string IP, int Port)
{
ip = IP;
port = Port;
}
private void ClientAccepted(TcpClient Client)
{
if (OnClientAccept != null)
{
OnClientAccept.Invoke(this, Client);
}
}
private void ClientMessaged(TcpClient Client, string Message)
{
if (OnClientMessage != null)
{
OnClientMessage.Invoke(this, Client, Message);
}
}
public void Start()
{
lockObjc = new object();
IPAddress ipAddress = IPAddress.Parse(ip);//127.0.0.1 -> local || "192.168.2.69" -> laptop
server = new TcpListener(ipAddress, port);//2014 -> local || 52025
server.Start();
accepter = new Thread(AcceptClients);
accepter.Start();
listener = new Thread(Listen);
listener.Start();
}
private void AcceptClients()
{
while (true)
{
TcpClient attachedCl = server.AcceptTcpClient();
if (attachedCl != null)
{
lock (lockObjc)
{
if (!clients.Contains(attachedCl))
{
clients.Add(attachedCl);
ClientAccepted(attachedCl);
}
}
}
}
}
private void Listen()
{
while (true)
{
for (int i = 0; i < clients.Count; i++)
{
TcpClient client = clients[i];
lock (lockObjc)
{
try
{
StreamReader clientIn = new StreamReader(client.GetStream());
string msg = clientIn.ReadLine();
if (msg != string.Empty)
{
ClientMessaged(client, msg);
}
}
catch
{
}
}
}
}
}
private bool send(TcpClient Client, string Msg)
{
bool b = true;
try
{
TcpClient client = Client;
StreamWriter clientOut = new StreamWriter(client.GetStream());
clientOut.AutoFlush = true;
clientOut.WriteLine(Msg);
}
catch
{
b = false;
}
return b;
}
public bool SendToACLient(TcpClient Client, string Message)
{
return send(Client, Message);
}
public bool SendToAClient(int i, string Message)
{
return send(clients[i], Message);
}
public bool SendToAllClients(string Message)
{
bool b = true;
for (int i = 0; i < clients.Count; i++)
{
b = b && send(clients[i], Message);
}
return b;
}
public bool SendToAllClientsExcept(TcpClient Client, string Message)
{
int x = clients.IndexOf(Client);
bool b = true;
for (int i = 0; i < clients.Count; i++)
{
if (i != x)
{
b = b && send(clients[i], Message);
}
}
return b;
}
}
Since I'm really new to server-client relationships, I don't know if the server part is OK, but it seems OK to me programmatically (But I'm also amateur programer, no real education).
And the client part is;
public delegate void ServerMessageHandler(Client Receiver, string Message);
public class Client
{
private StreamReader clientIn;
private StreamWriter clientOut;
private TcpClient client;
private Thread listener;
private object lockObj;
public event ServerMessageHandler OnServerMessage;
public bool Connected { get { return (client != null && client.Connected); } }
public Client()
{
lockObj = new object();
client = new TcpClient();
listener = new Thread(Listen);
}
private void ServerMessaged(string Message)
{
if (OnServerMessage != null)
{
OnServerMessage.Invoke(this, Message);
}
}
private void Listen()
{
string serverMsg;
while (true)
{
try
{
serverMsg = clientIn.ReadLine();
if (serverMsg != string.Empty)
{
ServerMessaged(serverMsg);
}
}
catch
{
}
}
}
private void start()
{
listener.Start();
}
public bool Connect(string Ip, int Port)
{
client.Connect(Ip, Port);//"192.168.2.69", 52025
if (client.Connected)
{
clientIn = new StreamReader(client.GetStream());
clientOut = new StreamWriter(client.GetStream());
clientOut.AutoFlush = true;
start();
}
return client.Connected;
}
public void Send(string Message)
{
if (client.Connected)
{
clientOut.WriteLine(Message);
}
}
}
Here's the form that I run Client class;
public partial class Form1 : Form
{
string s;
Client c;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
c = new Client();
c.OnServerMessage += new ServerMessageHandler(c_OnServerMessage);
bool b = c.Connect("192.168.2.69", 52025);
if (b)
{
s += "Connected to the server: 192.168.2.69:52025\r\n";
}
timer1.Start();
}
void c_OnServerMessage(Client Receiver, string Message)
{
s += "Server: " + Message + "\r\n";
}
private void button1_Click(object sender, EventArgs e)
{
c.Send(textBox1.Text);
}
private void timer1_Tick(object sender, EventArgs e)
{
textBox2.Text = s;
}
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
c.Send(textBox1.Text);
}
}
}
This form has two textboxes, first one is the one which takes user input (textBox1), the second one is the one which shows server responds. Why I'm not using Console? Because I've tried it in a way which I was waiting an input from user (Console.ReadLine) which as far as I know locks the console until user presses enter, even tough I'm using a thread for listening the server, I wasn't able to show the server answer on console.
So, I decided to create my own console (sort-of). The reason I run a timer to show messages is I get an error when I try to change the textbox text from a method that is being called by a thread (which is the c_OnServerMessage method, being called, indirectly, from the Listener thread from Client class)
But the problem I get here is this: I run the server on my laptop, I also run one client on my laptop which goes perfectly fine until I open up another client on another computer. At that point things go this way: I send a message from a client, the message reaches the server and is sent back. When I send another message, that message does not reach the server until I send a message from Client 2. And when I send a message from Client 2, the server gets the message from Client 2 even tough I sent the message from Client 1 first.
So basically what happens is, if I run 2 clients, I cannot send 2 messages in a row from a client, it kind of acts like a chess game where there is a turn for playing. I really don't understand the reason.
This problem is probably from a basic mistake but I had to write this whole message to be able to express myself. Thank you for your patient and your helps.
EDIT:
Problem solved thanks to this: TCP server with multiple Clients
My problem was the Listen method from Server class. The Listen method is being called by a thread, but still it runs a for loop in itself. Which if loop stops and waits for something, thread wouldn't make any difference. Thanks again, i think i will keep question, because i do not know what should happen to solved problems.

Send a Message Box to a client in my Domain from C# Windows Form

I have got a windows forms project than copying files and folders to clients in my domain. When copying file or copying directory process to [x]clients in my domain. I want to send it a Messagebox that says "There is a new folder or file in your [xdirectory]".
I can't do it by Messenger service because of Messenger service is does not working in XP Sp2 so I need another way for this. Maybe a client/server side application could be make. There will be a listener app in client then I will send it a messagebox.show code then it will show us a messagebox. etc.
I did something similar once before. I got most of this code from somewhere else, but I cannot remember from where.
First the Server Code:
public class HSTcpServer
{
private TcpListener m_listener;
private IPAddress m_address = IPAddress.Any;
private int m_port;
private bool m_listening;
private object m_syncRoot = new object();
public event EventHandler<TcpMessageReceivedEventArgs> MessageReceived;
public HSTcpServer(int port)
{
m_port = port;
}
public IPAddress Address
{
get { return m_address; }
}
public int Port
{
get { return m_port; }
}
public bool Listening
{
get { return m_listening; }
}
public void Listen()
{
try
{
lock (m_syncRoot)
{
m_listener = new TcpListener(m_address, m_port);
// fire up the server
m_listener.Start();
// set listening bit
m_listening = true;
}
// Enter the listening loop.
do
{
Trace.Write("Looking for someone to talk to... ");
// Wait for connection
TcpClient newClient = m_listener.AcceptTcpClient();
//Trace.WriteLine("Connected to new client");
// queue a request to take care of the client
ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessClient), newClient);
}
while (m_listening);
}
catch (SocketException se)
{
Trace.WriteLine("SocketException: " + se.ToString());
}
finally
{
// shut it down
StopListening();
}
}
public void StopListening()
{
if (m_listening)
{
lock (m_syncRoot)
{
// set listening bit
m_listening = false;
// shut it down
m_listener.Stop();
}
}
}
private void sendMessage(string message)
{
// Copy to a temporary variable to be thread-safe.
EventHandler<TcpMessageReceivedEventArgs> messageReceived = MessageReceived;
if (messageReceived != null)
messageReceived(this, new TcpMessageReceivedEventArgs(message));
}
private void ProcessClient(object client)
{
TcpClient newClient = (TcpClient)client;
try
{
// Buffer for reading data
byte[] bytes = new byte[1024];
StringBuilder clientData = new StringBuilder();
// get the stream to talk to the client over
using (NetworkStream ns = newClient.GetStream())
{
// set initial read timeout to 1 minute to allow for connection
ns.ReadTimeout = 60000;
// Loop to receive all the data sent by the client.
int bytesRead = 0;
do
{
// read the data
try
{
bytesRead = ns.Read(bytes, 0, bytes.Length);
if (bytesRead > 0)
{
// Translate data bytes to an ASCII string and append
clientData.Append(Encoding.ASCII.GetString(bytes, 0, bytesRead));
// decrease read timeout to 1 second now that data is
// coming in
ns.ReadTimeout = 1000;
}
}
catch (IOException ioe)
{
// read timed out, all data has been retrieved
Trace.WriteLine("Read timed out: {0}", ioe.ToString());
bytesRead = 0;
}
}
while (bytesRead > 0);
bytes = Encoding.ASCII.GetBytes("clowns");
// Send back a response.
ns.Write(bytes, 0, bytes.Length);
sendMessage(clientData.ToString());
}
}
finally
{
// stop talking to client
if (newClient != null)
newClient.Close();
}
}
}
public class TcpMessageReceivedEventArgs : EventArgs
{
private string m_message;
public TcpMessageReceivedEventArgs(string message)
{
m_message = message;
}
public string Message
{
get
{
return m_message;
}
}
}
The Client Code:
class HSTcpClient
{
private TcpClient _client;
private IPAddress _address;
private int _port;
private IPEndPoint _endPoint;
private bool _disposed;
public HSTcpClient(IPAddress address, int port)
{
_address = address;
_port = port;
_endPoint = new IPEndPoint(_address, _port);
}
public void SendForwardedClientMessage(int senderId, int receiverId, int hsId)
{
SendMessage(senderId.ToString() + ":" + receiverId.ToString() + ":" + hsId.ToString());
}
public void SendUpdatedCGBMessage()
{
SendMessage("Update your CGB you clowns");
}
public void SendMessage(string msg)
{
try
{
_client = new TcpClient();
_client.Connect(_endPoint);
// Get the bytes to send for the message
byte[] bytes = Encoding.ASCII.GetBytes(msg);
// Get the stream to talk to the server on
using (NetworkStream ns = _client.GetStream())
{
// Send message
Trace.WriteLine("Sending message to server: " + msg);
ns.Write(bytes, 0, bytes.Length);
// Get the response
// Buffer to store the response bytes
bytes = new byte[1024];
// Display the response
int bytesRead = ns.Read(bytes, 0, bytes.Length);
string serverResponse = Encoding.ASCII.GetString(bytes, 0, bytesRead);
Trace.WriteLine("Server said: " + serverResponse);
}
}
catch (SocketException se)
{
Trace.WriteLine("There was an error talking to the server: " +
se.ToString());
}
finally
{
Dispose();
}
}
#region IDisposable Members
public void Dispose()
{
Dispose(true);
System.GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
if (_client != null)
_client.Close();
}
_disposed = true;
}
}
#endregion
}
Then to use, create a server variable in your form code:
private HSTcpServer m_server;
and I created a background worker thread:
private System.ComponentModel.BackgroundWorker hsTcpServerThread;
handled the DoWork event:
private void hsTcpServerThread_DoWork(object sender, DoWorkEventArgs e)
{
m_server = new HSTcpServer(<pick your port>);
m_server.MessageReceived += new EventHandler<TcpMessageReceivedEventArgs>(m_server_MessageReceived);
m_server.Listen();
}
Then handle the message recieved event:
void m_server_MessageReceived(object sender, TcpMessageReceivedEventArgs e)
{
//<your code here> - e contains message details
}
Then to send a message to the server:
HSTcpClient client = new HSTcpClient(<ip address>, <port you picked>);
client.SendForwardedClientMessage(<message details>);
Hopefully I got everything in there.
You can use the FileSystemWatcher in the client. That way you do not have to deal with remoting or listening on ports.
If you really want to communicate between the two machines you can do using remoting, or via the TcpListener.

Categories

Resources