So I need to have my IP string to be used by 2 threads of my program. I don't know alot about classes and voids but at the time I have it kind of like this:
static void Main(string[] args)
{
string IP = "127.0.0.1"
}
and I want to use it in another thread:
static void th1T()
{
while (true)
{
var ping = new Ping();
ping.Send(IP);
}
}
How can I get that to work? I know I am doing something wrong, but don't know what i shall use instead.
The first issue is that IP is scoped to the method Main. You'll need to declare IP somewhere th1T can get to if you truly want to share it.
static string IP = "127.0.0.1"
static void Main(string[] args)
{
// do some work and start th1T()
}
static void th1T()
{
while (true)
{
var ping = new Ping();
ping.Send(IP);
}
}
Here you'll be able to reach IP even if th1T is running on a different thread. There are some other options as well though, like injecting the IP into th1T when the thread is started. To do that you'd have to change the signature of th1T to this:
static void th1T(object data)
and you'd have to change the code a little:
static void th1T(object data)
{
while (true)
{
var ping = new Ping();
ping.Send(data as string);
}
}
You could then start that on another thread like this:
Thread newThread = new Thread(th1T);
newThread.Start(IP);
Threading is very much a subjective subject. If the value you're referencing from another thread is being read only, like in your example, and it's only set once by the controller (the class starting the work), then it's absolutely valid to share the variable between threads. Many however will argue to their death on this.
We can only speak in generalities in respect to this issue. For example, in general it's more appropriate to inject the value to avoid race conditions and dead locks. But again, that would really depend on what you're doing with the value. You can't inject the value if you're reading a flag from one thread that's set by another. In that case you have to safely synchronize the value.
There's no need to continue because as you see the rabbit hole only gets deeper.
You should refactor your method that pings to accept a string as a parameter:
static void th1T(string IP)
{
while (true)
{
var ping = new Ping();
ping.Send(IP);
}
}
And when you call the method, pass in your IP.
What you're wanting to do amounts to a global variable, which is generally not a good idea.
Another approach you could use is a static class to store the value:
public static class Values
{
string IP { get; set; }
}
And in your method:
static void th1T()
{
while (true)
{
var ping = new Ping();
ping.Send(Values.IP);
}
}
I want to add that there are several issues that could arise from this approach. It is probably not a best practice to do it this way.
You haven't shown how you are starting the thread but in all cases you have the possibility to send the string as parameter to the thread. For example if you are manually spawning a new Thread you could pass it as parameter to the Start method:
static void Main(string[] args)
{
string IP = "127.0.0.1"
Thread t = new Thread(th1T);
t.Start(IP);
}
static void th1T(object value)
{
// The value parameter will contain the IP here
string ip = (string)value;
while (true)
{
var ping = new Ping();
ping.Send(ip);
}
}
Notice how the th1T method now takes an object parameter that you could cast back to the value being passed. In this example we have passed a simple string value but you could pass arbitrary complex objects.
If you are using Tasks you also have the possibility to pass parameters:
static void Main(string[] args)
{
string IP = "127.0.0.1"
Task.Factory.StartNew(th1T, IP);
}
static void th1T(object value)
{
// The value parameter will contain the IP here
string ip = (string)value;
while (true)
{
var ping = new Ping();
ping.Send(ip);
}
}
Related
I want to make a chat. The server is made in console app and the client is made in winforms.
In client I write a nickname and connect to server. The server receives name from client. I add all clients that connect to server in a Dictionary list with the (string)name and (TcpClient)Socket. After, I want to send to every client the client list.
When I debug on server, the Sockets appear with DualMode,EnableBroadcast error. In client when I have to receive the list it stops and doesn't do anything.
Server
namespace MyServer
{
class MyServer
{
public Dictionary<string, TcpClient> clientList = new Dictionary<string, TcpClient>();
TcpListener server = null;
NetworkStream stream = null;
StreamReader streamReader = null;
StreamWriter streamWriter = null;
TcpClient clientSocket;
String messageReceived;
int number_clients = 0;
public MyServer(TcpClient clientSocket_connect)
{
stream = clientSocket_connect.GetStream();
streamReader = new StreamReader(stream);
streamWriter = new StreamWriter(stream);
receiveMessage(clientSocket_connect); // receive messages
}
public MyServer()
{
Thread thread = new Thread(new ThreadStart(run));
thread.Start();
}
public void receiveMessage(TcpClient client_Socket)
{
messageReceived = streamReader.ReadLine();
if (messageReceived.Substring(messageReceived.Length - 4) == "user")
{
String name = messageReceived.Substring(0, messageReceived.Length - 4);
bool found = false;
foreach (var namefound in clientList.Keys)
{
if (namefound == name)
{
found = true;
streamWriter.WriteLine("The user already exists");
streamWriter.Flush();
}
}
if (!found)
{
//show who's connected
Console.WriteLine(name + " is online");
number_clients++;
clientList.Add(name, client_Socket);
//send to client clientlist
String send = null;
foreach (var key in clientList.Keys)
{
send += key + ".";
}
foreach (var value in clientList.Values)
{
TcpClient trimitereclientSocket = value;
if (trimitereclientSocket != null)
{
NetworkStream networkStream = trimitereclientSocket.GetStream();
StreamWriter networkWriter = new StreamWriter(networkStream);
networkWriter.WriteLine(send + "connected");
networkWriter.Flush();
}
}
}
}
}
void run()
{
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
server = new TcpListener(ipAddress, 8000);
server.Start();
Console.WriteLine("Server started!");
while (true)
{
clientSocket = server.AcceptTcpClient();
new MyServer(clientSocket);
}
}
}
static void Main(string[] args)
{
MyServer server = new MyServer();
}
}
Client
namespace MyClient
{
class MyClient
{
List<string> clientList = new List<string>();
TcpClient client = null;
NetworkStream stream = nul
l;
StreamReader streamReader = null;
StreamWriter streamWriter = null;
bool connected;
String received_message;
public MyClient()
{
client = new TcpClient("127.0.0.1", 8000);
stream = client.GetStream();
streamReader = new StreamReader(stream);
streamWriter = new StreamWriter(stream);
}
public void sendClientName(String name)
{
streamWriter.WriteLine(Convert.ToString(name));
streamWriter.Flush();
}
public List<ClientName> receiveClientList()
{
List<ClientName> val = new List<ClientName>();
string name = Convert.ToString(streamReader.ReadLine());
if (name.Substring(0, name.Length - 9) == "connected")
{
ClientName client = new ClientName();
client.Nume = name;
val.Add(client);
}
return val;
}
}
}
Client Form
public partial class Form1 : Form
{
MyClient client = new MyClient();
public Form1()
{
InitializeComponent();
Thread receiveClients = new Thread(new ThreadStart(getMessages));
}
private void btnConnect_Click(object sender, EventArgs e)
{
client.sendClientName(txtNickname.Text + "user");
}
public void getMessages()
{
while (true)
{
lbClientsConnected.Items.Add(client.receiveClientList());
}
}
}
I was unable to reproduce any error when running your code. I don't know what you mean by "the Sockets appear with DualMode,EnableBroadcast error". That said, there are a number of fixable problems with the code, including some that pertain directly to your concern that "when I have to receive the list it stops and doesn't do anything."
Probably the biggest issue with the code is that you simply never start the client's receiving thread. You need to call the Start() method on the Thread object after it's been created:
public Form1()
{
InitializeComponent();
Thread receiveClients = new Thread(new ThreadStart(getMessages));
// The receiving thread needs to be started
receiveClients.Start();
}
Now, even with that fixed, you have a few other problems. The next big issue is that you are parsing the received text incorrectly. In your code, where you should be looking for the text "connected" at the end of the string, you instead extract the other part of the text (with the list of client names).
Your receiveClientList() method should instead look like this:
private const string _kconnected = "connected";
public List<string> receiveClientList()
{
List<string> val = new List<string>();
string name = Convert.ToString(streamReader.ReadLine());
// Need to check the *end* of the string for "connected" text,
// not the beginning.
if (name.EndsWith(_kconnected))
{
name = name.Substring(0, name.Length - _kconnected.Length);
val.Add(name);
}
return val;
}
(You didn't share the ClientName class in your question, and really the example doesn't need it; a simple string value suffices for the purpose of this exercise. ALso, I've introduced the const string named _kconnected, to ensure that the string literal is used correctly in each place it's needed, as well as to simplify usage.)
But even with those two issues fixed, you've still got a couple in the Form code where you actually handle the return value of the receive method. First, you are passing the List<T> object that is returned from the receive method to the ListBox.Items.Add() method, which would just result in the ListBox displaying the type name for the object, rather than its elements.
Second, because the code is executing in a thread other than the UI thread that owns the ListBox object, you must wrap the call in a call to Control.Invoke(). Otherwise, you'll get a cross-thread operation exception.
Fixing those two issues, you get this:
public void getMessages()
{
while (true)
{
// Need to receive the data, and the call Invoke() to add the
// data to the ListBox. Also, if adding a List<T>, need to call
// AddRange(), not Add().
string[] receivedClientList = client.receiveClientList().ToArray();
Invoke((MethodInvoker)(() => listBox1.Items.AddRange(receivedClientList)));
}
With those changes, the code will process the message sent by the client, and return the list of clients. That should get you further along. That said, you still have a number of other problems, including some fairly fundamental ones:
The biggest issue is that when you accept a connection in the server, you create a whole new server object to handle that connection. There are a number of reasons this isn't a good idea, but the main one is that the rest of the code seems to conceptually assume that a single server object is tracking all of the clients, but each connection will result in its own collection of client objects, each collection having just one member (i.e. that client).
Note that once you've fixed this issue, you will have multiple threads all accessing a single dictionary data structure. You will need to learn how to use the lock statement to ensure safe shared use of the dictionary across multiple threads.
Another significant problem is that instead of using the streamWriter you created when you first accepted the connection, you create a whole new StreamWriter object (referenced in a local variable named networkWriter) to write to the socket. In this very simple example, it works fine, but between buffering and the lack of thread safety, this incorrectly-designed code could have serious data corruption problems.
Less problematic, but worth fixing, is that your server code completely fails to take advantage of the fact that you're storing the clients in a dictionary, as well as that .NET has useful helper functions for doing things like joining a bunch of strings together. I would write your server's receiveMessage() method something more like this:
private const string _kuser = "user";
public void receiveMessage(TcpClient client_Socket)
{
messageReceived = streamReader.ReadLine();
if (messageReceived.EndsWith(_kuser))
{
String name = messageReceived.Substring(0, messageReceived.Length - _kuser.Length);
if (clientList.ContainsKey(name))
{
streamWriter.WriteLine("The user already exists");
streamWriter.Flush();
return;
}
//show who's connected
Console.WriteLine(name + " is online");
number_clients++;
clientList.Add(name, client_Socket);
string send = string.Join(".", clientList.Keys);
foreach (var value in clientList.Values.Where(v => v != null))
{
// NOTE: I didn't change the problem noted in #2 above, instead just
// left the code the way you had it, mostly. Of course, in a fully
// corrected version of the code, your dictionary would contain not
// just `TcpClient` objects, but some client-specific object specific
// to your server implementation, in which the `TcpClient` object
// is found, along with the `StreamReader` and `StreamWriter` objects
// you've already created for that connection (and any other per-client
// data that you need to track). Then you would write to that already-
// existing `StreamWriter` object instead of creating a new one each
// time here.
NetworkStream networkStream = value.GetStream();
StreamWriter networkWriter = new StreamWriter(networkStream);
networkWriter.WriteLine(send + "connected");
networkWriter.Flush();
}
}
}
The above is not exhaustive by any means. Frankly, you probably should spend more time looking at existing examples of network-aware code, e.g. on MSDN and Stack Overflow, as well as on tutorials on web sites, blogs, or in books. Even when you write the server in a one-thread-per-connection way as you seem to be trying to do here, there are lots of little details you really need to get correct, and which you haven't so far.
But I do hope the above is enough to get you past your current hurdle, and on to the next big problem(s). :)
I've recently started with .Net remoting and I have managed to get working with some simple tutorials such as building a library dll that works as a calculator which the client can access and use(https://www.youtube.com/watch?v=Ve4AQnZ-_H0).
What I'm looking for to understand is how I could access current information that is held on the server. For example if I have this simple part running on the server:
int x = 0;
while (!Console.KeyAvailable)
{
x++;
System.Threading.Thread.Sleep(1000);
Console.WriteLine(x);
}
What I found so far is that the dll built is only returning a static result, such as with the calculator. I'd want to be able to tell how much x is on the server at any given time, through the client.
I don't know if I'm being clear enough but I'll try to explain better if it's needed.
In the following Server implementation demonstrates how you can keep state between calls.
// this gets instantiated by clients over remoting
public class Server:MarshalByRefObject
{
// server wide state
public static int Value;
// state only for this instance (that can be shared with several clients
// depending on its activation model)
private StringBuilder buildup;
// an instance
public Server()
{
buildup = new StringBuilder();
Console.WriteLine("server started");
}
// do something useful
public int DoWork(char ch)
{
Console.WriteLine("server received {0}", ch);
buildup.Append(ch);
return Value;
}
// return all typed chars
public string GetMessage()
{
Console.WriteLine("server GetMessage called") ;
return buildup.ToString();
}
// how long should this instance live
public override object InitializeLifetimeService()
{
// run forever
return null;
}
}
Notice the override InitializeLifetimeService. If you don't control this, your instance will get torn down after 5 minutes.
To use the above class we use the following code to get a listener up and running, including some of your logic. Don't forget to add an reference to the assembly System.Runtime.Remoting.
static void Main(string[] args)
{
// which port
var chn = new HttpChannel(1234);
ChannelServices.RegisterChannel(chn, false);
// Create only ONE Server instance
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(Server), "server", WellKnownObjectMode.Singleton);
Server.Value = 0;
while (!Console.KeyAvailable)
{
Server.Value++;
System.Threading.Thread.Sleep(1000);
Console.WriteLine(Server.Value);
}
}
When this code runs, it should listen on your local box on port 1234 for connections. On first run I had to disable the firewall, allow that port to pass the local firewall.
A client implementation that uses the Server might look like this. Don't forget to add an reference to the assembly System.Runtime.Remoting.
static void Main(string[] args)
{
var chn = new HttpChannel();
ChannelServices.RegisterChannel(chn, false);
RemotingConfiguration.RegisterWellKnownClientType(
typeof(Server),
"http://localhost:1234/server");
Console.WriteLine("Creating server...");
var s = new Server();
Console.WriteLine("type chars, press p to print, press x to stop");
var ch = Console.ReadKey();
while(ch.KeyChar != 'x')
{
switch(ch.KeyChar )
{
case 'p':
Console.WriteLine("msg: {0}", s.GetMessage());
break;
default:
Console.WriteLine("Got value {0} ", s.DoWork(ch.KeyChar));
break;
}
ch = Console.ReadKey();
}
Console.WriteLine("stopped");
}
If you compile and run this your result can look like this:
I'm trying to set up a specific scenario but, obviously, I'm having problems. My server is a site that primarily hosts a WCF service but I want to add an XSockets host there as well. I have the standard code in the bootstrap code file as per the instructions in the readme.txt. Upon a client connection, I am starting a worker thread which is basically a heartbeat that the client will monitor. The relevant code from the controller is as follows:
public class HeartbeatController : XSocketController
{
public void AddMessage(string message)
{
this.SendToAll(message, "addMessage");
}
}
Within my worker thread I am calling this:
string message = String.Format("pump", Math.Round(cpuCounter.NextValue());
ClientPool connection = ClientPool.GetInstance("ws://mywebsite:4502/HeartbeatController", "*");
connection.Send(message, "addMessage");
Currently I'm testing this with a console client which looks like this:
class Program
{
static XSocketClient socketClient;
static void Main(string[] args)
{
Console.WriteLine("Starting client...");
string url = "ws://mywebsite:4502/HeartbeatController";
socketClient = new XSocketClient(url, "*");
socketClient.OnOpen += socketClient_OnOpen;
socketClient.Open();
while (true)
{
// let it sit and display the "pump" messages
string input = Console.ReadLine();
if (input.Equals("Q", StringComparison.Ordinal))
{
break;
}
}
}
static void socketClient_OnOpen(object sender, EventArgs e)
{
Console.WriteLine("socketClient Opened");
socketClient.Bind("addMessage", OnAddMessage);
}
private static void OnAddMessage(ITextArgs textArgs)
{
Console.WriteLine("AddMessage :: {0}", textArgs.data);
}
}
On the client, if I put a breakpoint in the socketClient_OnOpen method it gets hit so I think it is connecting. But the pump message never makes it to the client.
Two Questions:
Is there anything obvious that I'm missing?
(Unrelated) Since many enterprises really don't like punching holes in their firewalls, is there any way to use port 80 with this setup (so that the client connection would look like "ws://mywebsite/HeartbeatController")?
Thanks for any help!
So to see what your pump actually was sending in to the server I added a custom pipeline.
public class MyPipeline : XSocketPipeline
{
//Incomming textmessage
public override void OnMessage(IXSocketController controller, ITextArgs e)
{
Console.WriteLine("IN " + e.data);
//Let the message continue into the server
base.OnMessage(controller, e);
}
//Outgoing textmessage
public override ITextArgs OnSend(IXSocketProtocol protocol, ITextArgs e)
{
Console.WriteLine("OUT " + e.data);
return base.OnSend(protocol, e);
}
}
Since I then saw that you was sending in a string that actually did not have a property named "message". The actionmethod "AddMessage" expects you to pass in a property message of type string. So you can solve this in two ways, both of them are simple.
Just replace the string parameter in the AddMessage with ITextArgs
public void AddMessage(ITextArgs message)
or...
Pass in a object from your worker thread instead of a string like this
connection.Send(new {message}, "addMessage");
So all you need to do to get it to work is to change this row
connection.Send(message, "addMessage");
with this row
connection.Send(new {message}, "addMessage");
EDIT: Btw, 4.0 is on the way and the client will be very much improved as well as the serverside stuff.
I'm currently trying to accomplish a project, which listens multiple ports and recieves incoming data.
DeviceListener class:
-I have a method called PortListener. It take two parameters whic are IP and Port addresses. It continously listens a specific port for incoming data.
-The other method is called StartListen which runs the PortListener in a thread.
Main program:
-It gets the port list and starts PortListener for each port using StartListen method.
Psuedo Like Code:
class DeviceListener()
{
private string PortListener(string ip, int port)
{
//listen ip and port.
//wait for data.
//recieve and return data.
RecieveData();
string data = recieved data;
return data;
}
public void StartListen(string ip, int port)
{
//start PortListener in a thread
Thread t = new Thread (() => PortListener(ip,port));
t.Start();
}
}
void Main()
{
ip = ip adress;
List portlist;
foreach (port in portlist)
{
string data = DeviceListener.StartListen(ip, port);
}
}
My problem is I just can't return the recieved data to my Main program.
Is there a way to get recieved datas for each PortListener thread?
You can do the following -
Add a queue of the data type you wish to receive. Best use the synchronized versions or create your own, depends on the .Net. See http://msdn.microsoft.com/en-us/library/dd997305(v=vs.110).aspx. Add each new stuff you get into the queue and make the queue public so other classes could access it. Other threads that wish to access incoming data can read can read from the queue and block if the queue is empty or poll by periodically checking if there are items present.
Another option is to use events (http://msdn.microsoft.com/en-us/library/aa645739(v=vs.71).aspx) Your class can expose an event with the relevant data present and invoke it upon data arrival. Other classes will register and will get notified of new incoming data.
The first option is better if you have a single reading entity, the second is better if multiple entities are interested in the data.
I solved the problem by using a delegate method.
class DeviceListener()
{
public delegate void PassData(string str);
public PassData passdata;
private void PortListener(string ip, int port)
{
//listen ip and port.
//wait for data.
//recieve and return data.
RecieveData();
string data = recieved data;
passdata(data);
}
public void StartListen(string ip, int port)
{
//start PortListener in a thread
Thread t = new Thread (() => PortListener(ip,port));
t.Start();
}
}
void Main()
{
DeviceListener.passdata = new DeviceListener.PassData(Some_Function);
ip = ip adress;
List portlist;
foreach (port in portlist)
{
DeviceListener.StartListen(ip, port);
}
private void Some_Function(string data)
{
//do something with returned string.
}
}
I have a TCP server that continually monitors for new incoming clients asynchronously and adds them to a client list:
public class TcpServer
{
public List<TcpClient> ClientsList = new List<TcpClient>();
protected TcpListener Server = new TcpListener(IPAddress.Any, 3000);
private _isMonitoring = false;
public TcpServer()
{
Server.Start();
Server.StartMonitoring();
}
public void StartMonitoring()
{
_isMonitoring = true;
Server.BeginAcceptTcpClient(HandleNewClient, null);
}
public void StopMonitoring()
{
_isMonitoring = false;
}
protected void HandleNewClient(IAsyncResult result)
{
if (_isMonitoring)
{
var client = Server.EndAcceptTcpClient(result);
ClientsList.Add(client);
StartMonitoring(); // repeats the monitoring
}
}
}
However, I'm having two issues with this code.
The first is the StartMonitoring() call in HandleNewClient(). Without it, the server will accept only one incoming connection and ignore any additional connections. What I'd like to do is have it continually monitor for new clients, but something rubs me wrong about the way I'm doing it now. Is there a better way of doing this?
The second is the _isMonitoring flag. I don't know how else to stop the async callback from activating and stop it from looping. Any advice on how this can be improved? I'd like to stick to using asynchronous callbacks and avoid having to manually create new threads running methods that have while (true) loops in them.
Basically, your StartMonitoring function, needs to loop - you'll only accept a single client at a time, and then you'd typically pass the request off to a worker thread, and then resume accepting new connections. The way its written, as you've stated, it will only accept a single client.
You'll want to expand on this to suit your startup/shutdown/terminate needs, but basically, what you're looking for is StartMonitoring to be more like this:
public void StartMonitoring()
{
_isMonitoring = true;
while (_isMonitoring)
Server.BeginAcceptTcpClient(HandleNewClient, null);
}
Note that if _isMonitoring is going to be set by another thread, you'd better mark it as volatile, or you'll likely never terminate the loops.