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.
Related
PS: sorry my English I can understand but i'm not so good to write. corrections are very welcome
First of all, I read some answers here and already know my problem...
Well I'm here because I'll need to make some pained changes into my server if there is no other solution ...
Here we go.
I have a server and a client listening and answering in the same port.
Inside my server, I have only one thread that reads, processes and sends the result. No problem here, it's fine, but my client has multiple threads that is doing the same, and it's causing wrong messages like:
one thread send a message and wait the answer,
other thread send other message and the first thread capt it as an answer, so the real answer of the first is gives to the second, so all 2 receive wrong messages and cause a big confusion on client.
I'm almost sure that I'll need to use a port to read and one to write or a semaphore, but if I can get around it, it will be very helpful.
Any ideas?
My communication class:
public SenderAndRequester(string ipAdress, int port)
{
client = new TcpClient();
IPEndPoint ip_end = new IPEndPoint(IPAddress.Parse(ipAdress), port);
client.Connect(ip_end);
if (client.Connected)
{
stw = new StreamWriter(client.GetStream());
str = new StreamReader(client.GetStream());
stw.AutoFlush = true;
str.DiscardBufferedData();
}
}
public string communicate(string message)
{
var comming = str.ReadLineAsync();
stw.WriteLine(message);
return comming.Result;
}
and here the class that uses it
public MyConstructor(){
com = new Communicator(new SenderAndRequester(ip, port));
while (!com.InitServer(firstVar,secondVar,...)) ;
//code continue ...
mnt = new Task(Tracker, ctsMonitor.Token, TaskCreationOptions.LongRunning);
mnt.Start();
}
class main thread ...
private bool nextStatus()
{
//code continue..
if (!com.RequestNewStatus())
{
_Error = com.Error + " on communicator";
return false;
}
status = com.ServerStatus;
// code continue ...
return true;
}
and one of various other threads
private void Tracker()
{
while (!ctsMonitor.IsCancellationRequested)
{
//code continue
refresh = com.RequestCriticalData();
Thread.Sleep(100);
}
}
I have one main form class and another class. In the second class, I have a thread loop:
public void StartListening()
{
listening = true;
listener = new Thread(new ThreadStart(DoListening));
listener.Start();
}
// Listening for udp datagrams thread loop
/*=====================================================*/
private void DoListening()
{
while (listening)
{
IPEndPoint remoteIPEndPoint = new IPEndPoint(IPAddress.Any, port);
byte[] content = udpClient.Receive(ref remoteIPEndPoint);
if (content.Length > 0)
{
string message = Encoding.ASCII.GetString(content);
delegMessage(message);
}
}
}
// Stop listening for udp datagrams
/*=====================================================*/
public void StopListening()
{
lock (locker)
{
listening = false;
}
}
In main form class, I start this listening in class constructor
udp.StartListening();
And than, in this main form class, I have key hook event, too. In this event, I wan to stop thread running in the second class.
private void hook_KeyPressed(int key)
{
if (key == (int)Keys.LMenu)
altPressed = true;
if (key == (int)Keys.F4 && altPressed == true)
udp.StopListening();
}
Unfortunetely, the thread is still running.
Do you have some ideas about this??
Thank you very much.
Your thread is blocking at the byte[] content = udpClient.Receive(ref remoteIPEndPoint); line. The Receive method blocks until something is received.
You should use the asynchronous version (BeginReceive) instead.
Also, another flaw in your code - you check for the stopping condition without any synchronization. Here:
private void DoListening()
{
while (listening){ //this condition could stuck forever in 'false'
}
Actually, without a memory barrier, there is no guarantee, that a thread, that is running DoListening will ever see the change to listening var from other thread. You should at least use locking here (which provides memory barrier)
As #igelineau pointed out - your code is blocking on the receive call. If you don;t want to go down the async route (which I'd recommend) just send something to the udp port in your stop listening method.
I am writing a small multi-threaded network server. All classical stuff: it listens for incoming connections, accepts them and then serves them in different threads. Also, this server sometimes will have to restart, and to do so it must a) stop listening, b) kick out all connected clients, c) adjust some settings/wait, d) resume listening.
Well, I pretty much don't know a thing about developing multi-threaded programs, so I am looking for help. Here's what I came to (core stuff only):
class Server
{
class MyClient
{
Server server;
TcpClient client;
bool hasToFinish = false;
public MyClient(Server server, TcpClient client)
{
this.server = server;
this.client = client;
}
public void Go()
{
while (!hasToFinish)
{
// do all cool stuff
}
CleanUp();
}
private void CleanUp()
{
// finish all stuff
client.Close();
server.myClients.Remove(this);
}
public void Finish()
{
hasToFinish = true;
}
}
bool running = false;
TcpListener listener;
HashSet<MyClient> myClients = new HashSet<MyClient>();
public void Start()
{
if (running)
return;
myClients.Clear();
listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1234);
listener.Start();
listener.BeginAcceptTcpClient(AcceptClient, this);
running = true;
}
public void Stop()
{
if (!running)
return;
listener.Stop();
foreach (MyClient client in myClients)
{
client.Finish();
}
myClients.Clear();
running = false;
}
public void AcceptClient(IAsyncResult ar)
{
MyClient client = new MyClient(this, ((TcpListener)ar.AsyncState).EndAcceptTcpClient(ar));
myClients.Add(client);
client.Go();
}
}
It's absolutely unsatisfactory. There is no sychronizing (I just don't know where to put it!), and calling Server.Stop() doesn't make MyClient-s to stop immediately. How do I fix these problems?
The code looks quite clean, we can make it thread-safe with simple modifications.
There are three parts of the problem, the "client", the "server" and the client-server interaction.
Client first, the Go() method is invoked by one thread (let's call it A) and the Finish() method is invoke by another thread (B). When thread B modify hasToFinish field, thread A may not see the modification immediately because the variable may be cached in the CPU cache. We can fix it by making hasToFinish field "volatile", which force thread B to publish the variable change to thread A when update.
Now the server class. I recommend you to synchronise three methods on the "Server" instance like the example below. It makes sure Start and Stop are called sequentially and the variables they changes are published across threads.
The client-server interaction need to be addressed as well. In your code, Client remove its reference from the Server but the server clear all clients references when Finish() any way. It looks redundant to me. If we can remove the part of code in client, we have nothing to worry about. If you choose to keep the logic in the client rather in the server for what ever reason, create a public method call RemoveClient(Client client) in the Server class and synchronise it against the Server instance. Then let the client to invoke this method instead of manipulating the HashSet directly.
I hope this solve your problem.
public void Start()
{
lock(this)
{
if (running)
return;
myClients.Clear();
listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1234);
listener.Start();
listener.BeginAcceptTcpClient(AcceptClient, this);
running = true;
}
}
public void Stop()
{
lock(this)
{
if (!running)
return;
listener.Stop();
foreach (MyClient client in myClients)
{
client.Finish();
}
myClients.Clear();
running = false;
}
}
public void AcceptClient(IAsyncResult ar)
{
lock(this)
{
MyClient client = new MyClient(this, ((TcpListener)ar.AsyncState).EndAcceptTcpClient(ar));
myClients.Add(client);
client.Go();
}
}
I have around 5000 modem (thin clients), and I want to communicate with them, one of a my method is like this : string GetModemData(modemID), now I have an open port in server that listens to modem and I'm using socket programming to send data to modems (calling related function), but when i want send data to multiple modem in a same time and get response from them, I don't know what should i do? I can send data to one modem and waiting for its response and then send another data to other modems (sequential), but the problem is client should be wait long time to get answer(may be some different client want to get some information from modems so they all will be wait into the Q or something like this), I think one way to solving this problem is to use multiple port and listen for each modem to related port, but it takes too many ports and also may be memory usage going up and exceed my available memory space, so some lost may be occurred (is this true?). what should to do ? I'd thinking about Parallelism, but i think its not related i should to wait for one port, because i don't know should to pass current received data to which client. I'm using asp.net.
currently I'm doing like this:
private void StartListener()
{
ModemTcpListener = new TcpListener(ModemPort);
//ClientTcpListener = new TcpListener(ClientPort);
ModemTcpListener.Start();
ModemTcpListener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptModemCallback), ModemTcpListener);
}
and in return
private void DoReadModemCallback(IAsyncResult ar)
{
try
{
bool bRet = ar.AsyncWaitHandle.WaitOne(420000);
Modem modem = ar.AsyncState as Modem;
if (!bRet || modem == null)
{
return;
}
}
catch{}
// now send data to which client?????? if i'm going to use async????
}
and :
private void DoAcceptModemCallback(IAsyncResult ar)
{
try
{
ModemTcpListener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptModemCallback), ModemTcpListener);
TcpClient tcpClient = ModemTcpListener.EndAcceptTcpClient(ar);
Modem modem= new Modem(tcpClient, "");
tcpClient.GetStream().BeginRead(modem.Buffer, 0, tcpClient.ReceiveBufferSize, new AsyncCallback(DoReadModemCallback), modem);
ModemTcpListener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptModemCallback), ModemTcpListener);
Log.Write("a Modem connect ...");
}
catch (Exception ex)
{
}
}
Heres an example keeping track of all your clients. I've compacted it for readability. You should really split it up into multiple classes.
I'm using Pool (which I just created and commited) and SimpleServer. Both classes are part of a library that I'm currently building (but far from done).
Don't be afraid of having 5000 sockets open, they do not consume much resources when you are using asynchronous operations.
public class SuperServer
{
private List<ClientContext> _clients = new List<ClientContext>();
private SimpleServer _server;
private Pool<byte[]> _bufferPool;
public SuperServer()
{
// Create a buffer pool to be able to reuse buffers
// since your clients will most likely connect and disconnect
// often.
//
// The pool takes a anonymous function which should return a new buffer.
_bufferPool = new Pool<byte[]>(() => new byte[65535]);
}
public void Start(IPEndPoint listenAddress)
{
_server = new SimpleServer(listenAddress, OnAcceptedSocket);
// Allow five connections to be queued (to be accepted)
_server.Start(5);
}
// you should handle exceptions for the BeginSend
// and remove the client accordingly.
public void SendToAll(byte[] info)
{
lock (_clients)
{
foreach (var client in _clients)
client.Socket.BeginSend(info, 0, info.Length, SocketFlags.None, null, null);
}
}
// Server have accepted a new client.
private void OnAcceptedSocket(Socket socket)
{
var context = new ClientContext();
context.Inbuffer = _bufferPool.Dequeue();
context.Socket = socket;
lock (_clients)
_clients.Add(context);
// this method will eat very few resources and
// there should be no problem having 5000 waiting sockets.
context.Socket.BeginReceive(context.Inbuffer, 0, context.Inbuffer.Length, SocketFlags.None, OnRead,
context);
}
//Woho! You have received data from one of the clients.
private void OnRead(IAsyncResult ar)
{
var context = (ClientContext) ar.AsyncState;
try
{
var bytesRead = context.Socket.EndReceive(ar);
if (bytesRead == 0)
{
HandleClientDisconnection(context);
return;
}
// process context.Inbuffer here.
}
catch (Exception err)
{
//log exception here.
HandleClientDisconnection(context);
return;
}
// use a new try/catch to make sure that we start
// read again event if processing of last bytes failed.
try
{
context.Socket.BeginReceive(context.Inbuffer, 0, context.Inbuffer.Length, SocketFlags.None, OnRead,
context);
}
catch (Exception err)
{
//log exception here.
HandleClientDisconnection(context);
}
}
// A client have disconnected.
private void HandleClientDisconnection(ClientContext context)
{
_bufferPool.Enqueue(context.Inbuffer);
try
{
context.Socket.Close();
lock (_clients)
_clients.Remove(context);
}
catch(Exception err)
{
//log exception
}
}
// One of your modems
// add your own state info.
private class ClientContext
{
public byte[] Inbuffer;
public Socket Socket;
}
}
Used classes:
Pool: http://fadd.codeplex.com/SourceControl/changeset/view/58858#1054902
SimpleServer: http://fadd.codeplex.com/SourceControl/changeset/view/58859#1054893
You need to use the asynchronous tcp/ip methods. This article shows how:
http://www.codeproject.com/KB/IP/asyncsockets.aspx
The critical piece is the BeginReceive() and related callback functions. Any more q's, please leave comments to this answer ;) BEST OF LUCK!
You need multi threading, whenever a client establishes a connection to the server start a new thread for it and start communication send/receive.
Here are some articles explaining multithreading in c#,
c-sharpcorner
codeproject
And here's a sample server application with multithreading,
http://www.dotnetspider.com/resources/2829-A-multi-readed-server-C-which-finds-prime-num.aspx
We're evaluating db4o (an OO-DBMS from http://www.db4o.com). We've put together a performance test for client/server mode, where we spin up a server, then hammer it with several clients at once. It seems like the server can only process one client's query at a time.
Have we missed a configuration switch somewhere that allows for this scenario? Server implementation is below. The client connects, queries (read-only), and disconnects per operation, and operations run one immediately after the other from several worker threads in the client process. We see same behaviour if we spin up one client process with one worker each against the same server.
Any suggestions?
Edit: We've now discovered, and tried out, the Lazy and Snapshot QueryModes, and although this alleviates the blocking server problem (partially), we still see significant concurrency problems when our clients (we run 40 concurrent test-clients that wait 1-300ms before issuing a random operation-request) hammer on the server. There appear to be exceptions emanating from the LINQ provider and from the IO internals :-(
public class Db4oServer : ServerConfiguration, IMessageRecipient
{
private bool stop;
#region IMessageRecipient Members
public void ProcessMessage(IMessageContext con, object message)
{
if (message is StopDb4oServer)
{
Close();
}
}
#endregion
public static void Main(string[] args)
{
//Ingestion.Do();
new Db4oServer().Run(true, true);
}
public void Run(bool shouldIndex, bool shouldOptimizeNativeQueries)
{
lock (this)
{
var cfg = Db4oFactory.NewConfiguration();
if (shouldIndex)
{
cfg.ObjectClass(typeof (Sequence))
.ObjectField("<ChannelID>k__BackingField")
.Indexed(true);
cfg.ObjectClass(typeof (Vlip))
.ObjectField("<ChannelID>k__BackingField")
.Indexed(true);
}
if (shouldOptimizeNativeQueries)
{
cfg.OptimizeNativeQueries(true);
}
var server = Db4oFactory.OpenServer(cfg, FILE, PORT);
server.GrantAccess("0", "kieran");
server.GrantAccess("1", "kieran");
server.GrantAccess("2", "kieran");
server.GrantAccess("3", "kieran");
//server.Ext().Configure().ClientServer().SingleThreadedClient(false);
server.Ext().Configure().MessageLevel(3);
server.Ext().Configure().Diagnostic().AddListener(new DiagnosticToConsole());
server.Ext().Configure().ClientServer().SetMessageRecipient(this);
try
{
if (!stop)
{
Monitor.Wait(this);
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
server.Close();
}
}
public void Close()
{
lock (this)
{
stop = true;
Monitor.PulseAll(this);
}
}
}
Well, there is something on the db40 servers that doesn't allow too many clients on at a time since it is too much for some to handle. You also locked it which did nothing to help in this case.