Does .Net Remoting opens multiple connections or just one ?
let say I have a server and a client. if the client creates multiple SingleCall objects. then for every object is there going to be a new connection or there going to be a single connection for every object ?
I can't find the answer anywhere.
Long answer is: it depends on many things.
Short answer is yes. Yes Yes.
Let's make an experiment and see the most common default situation.
We have 2 console apps and 1 common class library which is referred by both console apps.
The 1st console app has the role of the client and the 2nd one has that of the server.
First, here's what the commonly depended upon class library contains:
public interface IFactory {
string Hello(string name);
}
Now for some server code. Here's the start up:
private static TcpChannel channel;
static void Main(string[] args) {
BinaryClientFormatterSinkProvider clientProv = new BinaryClientFormatterSinkProvider();
BinaryServerFormatterSinkProvider serverProv = new BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel = TypeFilterLevel.Full;
channel = new TcpChannel(
properties: new Hashtable {
{ #"port", 2013 }
},
clientSinkProvider: clientProv,
serverSinkProvider: serverProv
);
ChannelServices.RegisterChannel(channel, false);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(Factory), "Factory.rem", WellKnownObjectMode.SingleCall);
Console.WriteLine("Server started...");
Console.WriteLine("Press any key to stop...");
Console.ReadKey(intercept: true);
}
We just mentioned a class called Factory.
RemotingConfiguration.RegisterWellKnownServiceType(typeof(Factory), "Factory.rem", WellKnownObjectMode.SingleCall);
You guessed it. It's the IFactory implementation:
private sealed class Factory : MarshalByRefObject, IFactory {
#region IFactory Members
string IFactory.Hello(string name) {
return #"Hello " + name + #" !";
}
#endregion
}
Now for some client:
static void Main(string[] args) {
Console.WriteLine("Press any key to connect...");
Console.ReadKey(intercept: true);
IFactory factory = Activator.GetObject(typeof(IFactory), #"tcp://127.0.0.1:2013/Factory.rem") as IFactory;
EventWaitHandle signal = new EventWaitHandle(initialState: false, mode: EventResetMode.ManualReset);
ThreadStart action = () => {
signal.WaitOne();
var result = factory.Hello("Eduard");
Console.WriteLine(result);
};
foreach (var i in Enumerable.Range(0, 99))
new Thread(action) { IsBackground = true }.Start();
Console.WriteLine("Press any key to bombard server...");
Console.ReadKey(intercept: true);
signal.Set();
Console.ReadKey(intercept: true);
}
You already know all of these things, I'm sure. We obtain a transparent proxy to the SingleCall service on the other side (they're both on the same machine and we're using TCP port 2013):
IFactory factory = Activator.GetObject(typeof(IFactory), #"tcp://127.0.0.1:2013/Factory.rem") as IFactory;
Then, for "simulataneous-ness" reasons we create 100 threads, start them (which can take some time), but "hold them in a leash" (a signal is an essential means of synchronization of the OS) until we "pull the trigger":
EventWaitHandle signal = new EventWaitHandle(initialState: false, mode: EventResetMode.ManualReset);
ThreadStart action = () => {
signal.WaitOne();
var result = factory.Hello("Eduard");
Console.WriteLine(result);
};
foreach (var i in Enumerable.Range(0, 99))
new Thread(action) { IsBackground = true }.Start();
So although all 100 threads have been created AND started, they are all waiting in the following invocation:
signal.WaitOne();
That way we can get the to start at the same time better, otherwise the creation and starting of threads itself would have made their actual execution more or less sequential.
We ask the user to decide when to "bombard the server" with 100 Hello invocations:
Console.WriteLine("Press any key to bombard server...");
Console.ReadKey(intercept: true);
signal.Set();
And this is what happens:
1) We start the server console app and let it run in peace:
2) We start the client console app, "make the connection" by pressing any key (which is only a logical connection since it just creates a transparent proxy) but we postpone the "bombardment":
3) We start up Mark Russinovich's Process Explorer and use it to discover the client process in the process list, and while doing that we open it's properties window and select the TCP/IP tab:
4) We hit any key in the client console app, and .. TA DAA !!
You get a lot of connections in Process Explorer.
Are they a hundred ? Sometimes yes, sometimes no.
It's a connection pool, that's for sure.
After a short while (5 to 10 seconds) of idleness they close down which is a very good thing (because the .NET Remoting stack is implemented that way).
I hope this experiment generally answered your question.
In more specific cases and in a more rigorous sense you should check out the documentation and read about the various channels that you might use in your .NET Remoting apps (there are loads of them out there, what you've seen here is just the regular TcpChannel officially provided by Microsoft, it depends on what your .NET Remoting configuration says, on whether you're hosting the server in IIS or not, etc).
The number of network connections depends on remoting channel you use. The default TcpChannel opens as many network connections as many threads in your program try to access the server during one point of time.
For single-threaded applications, TcpChannel uses one network connection.
As an opposite example, a third party remoting channel IiopChannel uses multiplexing and thus allows to have just a few network connections for many hundreds of active threads.
Related
I wrote this code that works perfectly, but I fear that ping every 2 seconds consumes too many resources or can create some problems with internet connection.
new Thread(() =>
{
if (CheckInternetConnection() == false)
{
Dispatcher.Invoke(new Action(delegate
{
//internet access lost
}));
}
else
{
Dispatcher.Invoke(new Action(delegate
{
//internet access
}));
}
Thread.Sleep(2000);
}).Start();
[DllImport("wininet.dll")]
private extern static bool InternetGetConnectedState(out int Description, int ReservedValue);
public static bool CheckInternetConnection()
{
int output = 0;
return InternetGetConnectedState(out output, 0);
}
These are two events that don't work in all occasions (only when IP or network card changes)
NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged
NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
Can someone help me ?
Note : In regaurds to your original solution
NetworkChange.NetworkAvailabilityChanged works fine, but
there are a couple of caveats: 1) it doesn't tell you if you have
Internet access, it just tells you whether there's at least one
non-loopback network adapter working, and 2) there are often extra
network adapters installed for various reasons that leave the system
in a "network is available" state, even when your main
Internet-connected adapter is disabled/unavailable - thanks to Peter Duniho
Since networking is more than just your routers or network card, and is really every hop to where ever it is you are trying to connect to at any time. The easiest and most reliable way is just ping a well known source like google, or use some sort of heart beat to one of your internet services.
The reasons this is the only reliable way is that any number of connectivity issues can occur in between you and the outside world. Even major service providers can go down.
So an IMCP ping to a known server like Google, or calling OpenRead on a WebClient are 2 valid approaches. These calls are not expensive comparatively and can be put into a light weight timer or continual task.
As for your comments you can probably signal a custom event to denote the loss of network after a certain amount of fails to be safe
To answer your question
But I fear that ping every 2 seconds consumes too many resources or
can create some problems with internet connection.
Both methods are very inexpensive in regards to CPU and network traffic, any resources used should be very minimal
Note : Just make sure you are pinging or connecting to a server with high availability, this will
allow such shenanigans and not just block you
Ping Example
using System.Net.NetworkInformation;
// Implementation
using (var ping = new Ping())
{
var reply = ping.Send("www.google.com");
if (reply != null && reply.Status != IPStatus.Success)
{
// Raise an event
// you might want to check for consistent failures
// before signalling a the Internet is down
}
}
// Or if you wanted to get fancy ping multiple sources
private async Task<List<PingReply>> PingAsync(List<string> listOfIPs)
{
Ping pingSender = new Ping();
var tasks = listOfIPs.Select(ip => pingSender.SendPingAsync(ip, 2000));
var results = await Task.WhenAll(tasks);
return results.ToList();
}
Connection Example
using System.Net;
// Implementation
try
{
using (WebClient client = new WebClient())
{
using (client.OpenRead("http://www.google.com/"))
{
// success
}
}
}
catch
{
// Raise an event
// you might want to check for consistent failures
// before signalling the Internet is down
}
Note : Both these methods have an async variant that will return a
Task and can be awaited for an Asynchronous programming pattern better suited for IO bound tasks
Resources
Ping.Send Method
Ping.SendAsync Method
WebClient.OpenRead Method
WebClient.OpenReadAsync Method
NetworkInterface.GetIsNetworkAvailable() is unreliable... since it would return true even if all the networks are not connected to internet. The best approach to check for connectivity, in my opinion, is to ping a well known and fast online resource. For example:
public static Boolean InternetAvailable()
{
try
{
using (WebClient client = new WebClient())
{
using (client.OpenRead("http://www.google.com/"))
{
return true;
}
}
}
catch
{
return false;
}
}
Anyway, those two events you are subscribing don't work the way you think... actually they check for the hardware status of your network adapters... not whether they are connected to internet or not. They have the same drawback as NetworkInterface.GetIsNetworkAvailable(). Keep on checking for connectivity into a separate thread that pings a safe source and act accordingly. Your Interop solution is excellent too.
Doing ping to public resources brings extra calls to your app and adds a dependency on that website or whatever you would use in the loop.
What if you use this method: NetworkInterface.GetIsNetworkAvailable() ?
Would it be enough for your app's purposes?
I found it here https://learn.microsoft.com/en-us/dotnet/api/system.net.networkinformation.networkinterface.getisnetworkavailable?view=netframework-4.7.1#System_Net_NetworkInformation_NetworkInterface_GetIsNetworkAvailable
Collaborators have built a prototype using Processing that connects to a Sparkfun RFID reader, I think using a serial connection over USB. We've deployed the prototype into a number of trialists' homes and one common usage scenario I foolishly overlooked was user switching. Hence I am writing a wrapper that ensures only one instance of the prototype application is running across all users on the machine.
I’m testing out my first stab at this as a simple console app. Here’s the code:
static void Main(string[] args)
{
// http://stackoverflow.com/a/2590446/575530
var users = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
var rule = new EventWaitHandleAccessRule(users, EventWaitHandleRights.FullControl, AccessControlType.Allow);
var security = new EventWaitHandleSecurity();
security.AddAccessRule(rule);
bool createdStartup;
using (var whStartup = new EventWaitHandle(false, EventResetMode.AutoReset, "Global/AaltoTokensAppStartupEvent", out createdStartup, security))
{
bool createdShutdown;
using (var whShutdown = new EventWaitHandle(false, EventResetMode.AutoReset, "Global/AaltoTokensAppShutdownEvent", out createdShutdown, security))
{
Console.WriteLine("Let other instances shutdown");
whStartup.Set();
Console.WriteLine("If other instances exist wait for them to shutdown");
if (!createdShutdown)
{
whShutdown.WaitOne();
}
whShutdown.Reset();
Console.WriteLine("Start tray app");
var tokenProc = Process.Start(#"C:\Temp\FAMILY3_WIN\TokensApp.exe");
Console.WriteLine(tokenProc.ProcessName);
Console.WriteLine("Wait to see if another instance to tries to start");
whStartup.Reset();
whStartup.WaitOne();
Console.WriteLine("Shutdown if another instance starts");
//if (tokenProc != null) tokenProc.Kill();
foreach (var process in Process.GetProcesses())
{
if (process.ProcessName.StartsWith("javaw"))
{
process.Kill();
}
}
whShutdown.Set();
}
}
Console.WriteLine("Done...");
Console.ReadLine();
}
(N.B. I know there are issues with this code around (1) killing Java processes that are not the running prototype and (2) there’s no code to respond to lots of instances starting simultaneously, just two at a time. But that’s not what my question is about.)
Testing this under a single user account works fine. I can start my app, it in turn starts the prototype, and if I start a second instance of my app the first one kills the initial instance of the prototype before the second one starts another instance of the prototype afresh.
But if I try doing this from two different user accounts it fails (silently). If I
Start an instance of my application it starts the prototype
Switch user
Start an instance of my application then it starts the
prototype without my app from step 1 first shutting down the existing
instance.
Can anyone see what’s wrong with my code? How should I use EventWaitHandle across several simultaneous user sessions on the same machine?
Isn't it always the way, minutes after writing a long question the answer leaps to mind!
I got the slash the wrong way around in the name of the EventWaitHandle. For example replacing the constructor call:
new EventWaitHandle(false, EventResetMode.AutoReset, "Global/AaltoTokensAppShutdownEvent", out createdShutdown, security)
with this one:
new EventWaitHandle(false, EventResetMode.AutoReset, #"Global\AaltoTokensAppShutdownEvent", out createdShutdown, security)
fixes my problem.
I currently have a single application that needs to be started from a windows service that i am coding in .net 3.5. This application is currently running as the user who ran the service, in my case the SYSTEM user. If running as the SYSTEM user it does not show the application to the users desktop. Thoughts? advice?
//constructor
private Process ETCHNotify = new Process();
//StartService()
ETCHNotify.StartInfo.FileName = baseDir + "\\EtchNotify.exe";
ETCHNotify.StartInfo.UseShellExecute = false;
//BackgroundWorkerThread_DoWork()
if (!systemData.GetUserName().Equals(""))
{
// start ETCHNotify
try {
ETCHNotify.Start();
}
catch (Exception ex)
{
systemData.Run("ERR: Notify can't start: " + ex.Message);
}
}
I only execute the try/catch if the function i have written GetUserName() (which determines the username of the user running explorer.exe) is not null
again to reiterate: desired functionality is that this starts ETCHNotify in a state that allows it to interact with the currently logged in user as determined by GetUserName()
Collage of some post found around (this and this)
Note that as of Windows Vista, services are strictly forbidden from interacting directly with a user:
Important: Services cannot directly interact with a user as of Windows
Vista. Therefore, the techniques mentioned in the section titled Using
an Interactive Service should not be used in new code.
This "feature" is broken, and conventional wisdom dictates that you shouldn't have been relying on it anyway. Services are not meant to provide a UI or allow any type of direct user interaction. Microsoft has been cautioning that this feature be avoided since the early days of Windows NT because of the possible security risks.
There are some possible workarounds, however, if you absolutely must have this functionality. But I strongly urge you to consider its necessity carefully and explore alternative designs for your service.
Use WTSEnumerateSessions to find the right desktop, then CreateProcessAsUser to start the application on that desktop (you pass it the handle of the desktop as part of the STARTUPINFO structure) is correct.
However, I would strongly recommend against doing this. In some environments, such as Terminal Server hosts with many active users, determining which desktop is the 'active' one isn't easy, and may not even be possible.
A more conventional approach would be to put a shortcut to a small client app for your service in the global startup group. This app will then launch along with every user session, and can be used start other apps (if so desired) without any juggling of user credentials, sessions and/or desktops.
Ultimately in order to solve this i took the advice of #marco and the posts he mentioned. I have created the service to be entirely independent of the tray application that interacts with the user. I did however install the Tray application via registry 'start up' methods with the service. The Service installer will now install the application which interacts with the user as well... This was the safest and most complete method.
thanks for your help everyone.
I wasn't going to answer this since you already answered it, (and it's oh, what? going on 2.5 years OLD now!?) But there are ALWAYS those people who are searching for this same topic, and reading the answers...
In order to get my service to Interact with the Desktop, no matter WHAT desktop, nor, how MANY desktops, nor if the service was even running on the SAME COMPUTER as the desktop app!! None of that matters with what I got here... I won't bore you with the details, I'll just give you the meat and potatoes, and you and let me know if you want to see more...
Ok. First thing I did was create an Advertisement Service. This is a thread that the service runs, opens up a UDP socket to listen for broadcasts on the network. Then, using the same piece of code, I shared it with the client app, but it calls up Advertise.CLIENT, rather than Advertise.SERVER... The CLIENT opens the port I expect the service to be on, and broadcasts a message, "Hello... Is there anybody out there??", asking if they're there ANY servers listening, and if so, reply back to THIS IP address with your computer name, IP Address and port # where I can find the .NET remoting Services..." Then it waits a small amount of time-out time, gathers up the responses it gets, and if it's more than one, it presents the user with a dialog box and a list of services that responded... The Client then selects one, or, if only ONE responded, it will call Connect((TServerResponse) res); on that, to get connected up. At this point, the server is using Remoting Services with the WellKnownClientType, and WellKnownServerType to put itself out there...
I don't think you are too interested in my "Auto-Service locater", because a lot of people frown on UDP, even more so when your app start broadcasting on large networks. So, I'm assuming you'd be more interested in my RemotingHelper, that gets the client connected up to the server. It looks like this:
public static Object GetObject(Type type)
{
try {
if(_wellKnownTypes == null) {
InitTypeCache();
}
WellKnownClientTypeEntry entr = (WellKnownClientTypeEntry)_wellKnownTypes[type];
if(entr == null) {
throw new RemotingException("Type not found!");
}
return System.Activator.GetObject(entr.ObjectType, entr.ObjectUrl);
} catch(System.Net.Sockets.SocketException sex) {
DebugHelper.Debug.OutputDebugString("SocketException occured in RemotingHelper::GetObject(). Error: {0}.", sex.Message);
Disconnect();
if(Connect()) {
return GetObject(type);
}
}
return null;
}
private static void InitTypeCache()
{
if(m_AdvertiseServer == null) {
throw new RemotingException("AdvertisementServer cannot be null when connecting to a server.");
}
_wellKnownTypes = new Dictionary<Type, WellKnownClientTypeEntry>();
Dictionary<string, object> channelProperties = new Dictionary<string, object>();
channelProperties["port"] = 0;
channelProperties["name"] = m_AdvertiseServer.ChannelName;
Dictionary<string, object> binFormatterProperties = new Dictionary<string, object>();
binFormatterProperties["typeFilterLevel"] = "Full";
if(Environment.UserInteractive) {
BinaryServerFormatterSinkProvider binFormatterProvider = new BinaryServerFormatterSinkProvider(binFormatterProperties, null);
_serverChannel = new TcpServerChannel(channelProperties, binFormatterProvider);
// LEF: Only if we are coming form OUTSIDE the SERVICE do we want to register the channel, since the SERVICE already has this
// channel registered in this AppDomain.
ChannelServices.RegisterChannel(_serverChannel, false);
}
System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IPawnStatServiceStatus)));
RegisterType(typeof(IPawnStatServiceStatus),m_AdvertiseServer.RunningStatusURL.ToString());
System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IPawnStatService)));
RegisterType(typeof(IPawnStatService), m_AdvertiseServer.RunningServerURL.ToString());
System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IServiceConfiguration)));
RegisterType(typeof(IServiceConfiguration), m_AdvertiseServer.RunningConfigURL.ToString());
}
[SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration, RemotingConfiguration=true)]
public static void RegisterType(Type type, string serviceUrl)
{
WellKnownClientTypeEntry clientType = new WellKnownClientTypeEntry(type, serviceUrl);
if(clientType != RemotingConfiguration.IsWellKnownClientType(type)) {
RemotingConfiguration.RegisterWellKnownClientType(clientType);
}
_wellKnownTypes[type] = clientType;
}
public static bool Connect()
{
// Init the Advertisement Service, and Locate any listening services out there...
m_AdvertiseServer.InitClient();
if(m_AdvertiseServer.LocateServices(iTimeout)) {
if(!Connected) {
bConnected = true;
}
} else {
bConnected = false;
}
return Connected;
}
public static void Disconnect()
{
if(_wellKnownTypes != null) {
_wellKnownTypes.Clear();
}
_wellKnownTypes = null;
if(_serverChannel != null) {
if(Environment.UserInteractive) {
// LEF: Don't unregister the channel, because we are running from the service, and we don't want to unregister the channel...
ChannelServices.UnregisterChannel(_serverChannel);
// LEF: If we are coming from the SERVICE, we do *NOT* want to unregister the channel, since it is already registered!
_serverChannel = null;
}
}
bConnected = false;
}
}
So, THAT is meat of my remoting code, and allowed me to write a client that didn't have to be aware of where the services was installed, or how many services were running on the network. This allowed me to communicate with it over the network, or on the local machine. And it wasn't a problem to have two or more people running the app, however, yours might. Now, I have some complicated callback code in mine, where I register events to go across the remoting channel, so I have to have code that checks to see if the client is even still connected before I send the notification to the client that something happened. Plus, if you are running for more than one user, you might not want to use Singleton objects. It was fine for me, because the server OWNS the objects, and they are whatever the server SAYS they are. So, my STATS object, for example, is a Singleton. No reason to create an instance of it for EVERY connection, when everyone is going to see the same data, right?
I can provide more chunks of code if necessary. This is, of course, one TINY bit of the overall picture of what makes this work... Not to mention the subscription providers, and all that.
For the sake of completeness, I'm including the code chunk to keep your service connected for the life of the process.
public override object InitializeLifetimeService()
{
ILease lease = (ILease)base.InitializeLifetimeService();
if(lease.CurrentState == LeaseState.Initial) {
lease.InitialLeaseTime = TimeSpan.FromHours(24);
lease.SponsorshipTimeout = TimeSpan.FromSeconds(30);
lease.RenewOnCallTime = TimeSpan.FromHours(1);
}
return lease;
}
#region ISponsor Members
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure)]
public TimeSpan Renewal(ILease lease)
{
return TimeSpan.FromHours(12);
}
#endregion
If you include the ISponsor interface as part of your server object, you can implement the above code.
Hope SOME of this is useful.
When you register your service, you can tell it to allow interactions with the desktop. You can read this oldie link http://www.codeproject.com/KB/install/cswindowsservicedesktop.aspx
Also, don't forget that you can have multiple users logged in at the same time.
Apparently on Windows Vista and newer interacting with the desktop has been made more difficult. Read this for a potential solution: http://www.codeproject.com/KB/cs/ServiceDesktopInteraction.aspx
Hi
I want to use NamedPipeServerStream which is new from .NET 3.5 for namedpipe communication.
I want to write multi-threaded pipe server. is it handled by default or I should write code for that. my pipe server should handle multiple request at a time
any solution or code ?
You can write a multi threaded pipe server by repeatedly creating a NamedPipeServerStream and waiting for one connection, then spawning a thread for that instance of NamedPipeServerStream.
You can only have 254 concurrent clients though according to the .NET MSDN documentation linked below. For Win32 APIs though you can pass a special value to get unlimited based on system resources. It seems the MSDN documentation is wrong as noted below.
The below code is not tested so please do not simply copy and paste for production use without testing:
public class PipeServer
{
bool running;
Thread runningThread;
EventWaitHandle terminateHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
public string PipeName { get; set; }
void ServerLoop()
{
while (running)
{
ProcessNextClient();
}
terminateHandle.Set();
}
public void Run()
{
running = true;
runningThread = new Thread(ServerLoop);
runningThread.Start();
}
public void Stop()
{
running = false;
terminateHandle.WaitOne();
}
public virtual string ProcessRequest(string message)
{
return "";
}
public void ProcessClientThread(object o)
{
NamedPipeServerStream pipeStream = (NamedPipeServerStream)o;
//TODO FOR YOU: Write code for handling pipe client here
pipeStream.Close();
pipeStream.Dispose();
}
public void ProcessNextClient()
{
try
{
NamedPipeServerStream pipeStream = new NamedPipeServerStream(PipeName, PipeDirection.InOut, 254);
pipeStream.WaitForConnection();
//Spawn a new thread for each request and continue waiting
Thread t = new Thread(ProcessClientThread);
t.Start(pipeStream);
}
catch (Exception e)
{//If there are no more avail connections (254 is in use already) then just keep looping until one is avail
}
}
Each NamedPipeServerStream instance is a Stream implementation wrapping a handle to an instance of a named pipe. You can (and a multithreaded pipe server will) have multiple instances of NamedPipeServerStream for the same named pipe: each one wraps a handle to a different instance of the named pipe, servicing a different client. Named pipe instances (even for the same pipe) are kept separate by the operating system, so there is no need for any explicit coding to keep each client's communication with the server separate.
What you do need to code explicitly is the threading model for the server. The simplest approach to multithreading the server is explained in this SO answer, which includes a pseudo-code template. More scalable implementations, if large numbers of concurrent callers need to be supported, would use thread pooling and the asynchronous methods instead of creating a dedicated thread for each connection.
NamedPipeServerStream is a point to point connection. You have to synchronise the calls yourself - for example calls written to a queue and your server reads from the synchronised queue and makes the calls.
I read some C# chat source code & I see that: on chat server with a lot of connected clients, server listener will run in a separated thread & each connected client will also run in a separated thread.
Code examples:
Start server & begin listening in a separated thread:
public void StartListening()
{
// Get the IP of the first network device, however this can prove unreliable on certain configurations
IPAddress ipaLocal = ipAddress;
// Create the TCP listener object using the IP of the server and the specified port
tlsClient = new TcpListener(1986);
// Start the TCP listener and listen for connections
tlsClient.Start();
// The while loop will check for true in this before checking for connections
ServRunning = true;
// Start the new tread that hosts the listener
thrListener = new Thread(KeepListening);
thrListener.Start();
}
private void KeepListening()
{
// While the server is running
while (ServRunning == true)
{
// Accept a pending connection
tcpClient = tlsClient.AcceptTcpClient();
// Create a new instance of Connection
Connection newConnection = new Connection(tcpClient);
}
}
And a connection will also run in a separated thread:
public Connection(TcpClient tcpCon)
{
tcpClient = tcpCon;
// The thread that accepts the client and awaits messages
thrSender = new Thread(AcceptClient);
// The thread calls the AcceptClient() method
thrSender.Start();
}
So, if a chat server with 10000 connected clients, the chat server application will have 10002 threads (one main thread, one server thread & 10000 client threads). I think the chat server will be overhead with a big number of threads. Please help me a solution. Thanks.
UPDATE:
I believe chat examples are only for learning networking & they are not suitable in real-world model. Please give me a real-world solution. Thanks.
If you use .Net framework 2.0 SP2 or higher, than you may use new asyncrhronous sockets model based on IO Completion ports. In this case you shouldn't create your own threads, because IO Completion ports do all job for you.
Here some examples:
tcpServer = new System.Net.Sockets.TcpListener(IPAddress.Any, port);
tcpServer.Start();
tcpServer.BeginAcceptSocket(EndAcceptSocket, tcpServer);
private void EndAcceptSocket(IAsyncResult asyncResult)
{
TcpListener lister = (TcpListener)asyncResult.AsyncState;
Socket sock = lister.EndAcceptSocket(asyncResult);
//handle socket connection (you may add socket to you internal storage or something)
//start accepting another sockets
lister.BeginAcceptSocket(EndAcceptSocket, lister);
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
e.Completed += ReceiveCompleted;
e.SetBuffer(new byte[socketBufferSize], 0, socketBufferSize);
sock.ReceiveAsync(e);
}
void ReceiveCompleted(object sender, SocketAsyncEventArgs e)
{
var sock = (Socket)sender;
if (!sock.Connected)
{
//handle socket disconnection
}
var buf = new byte[size];
Array.Copy(e.Buffer, buf, size);
//handle received data
//start reading new data
sock.ReceiveAsync(e);
}
A standard mechanism to ease the burden is known as selection, which can multiplex multiple Socket instances to watch for the ones that are ready to be read or written to. See this document: http://codeidol.com/csharp/csharp-network/Csharp-Network-Programming-Classes/Csharp-Socket-Programming/ and scroll down to the section on select().
1) You'll NEVER want that many threads running - even if you could get them to run on your box (which you can't - each thread has a stack associated with it that takes real RAM and as you start more and more and more you'll run out of physical resources in your box and watch it blow up).
2) You'll want to look into thread pooling - using a smaller amount of threads to tackle a larger amount of work - typically reading from a queue of work that you try to get through as quickly as possible.
3) You'll want to look into io completion ports - a means of having a callback when io (likek a disk read or a network io) is waiting for you to take action - think of a thread (or pool of threads) dedicated to getting io notifications and then shoving the action to take for that io into a queue and then another pool of threads that take care of the actual messaging/logging/etc.
4) What happens when you scale beyond one machine? Which you hope to do if you're successful right? :-) Typically people dedicate a set of N machines to chat - then they hash based on a identifier for the user (think a GUID that represented the user - or a UserID/bigint depending on what corresponds to some internal authentication token that is consistent from login to login) which allows them to deterministically route the user's status/state information to a specific machine in that set of N boxes dedicated to messaging. So if a user that hashes to server N[2] needs to check if theri friends ar logged in it is easy to know for each of their friends exactly which machine their friend's status should be in because the backend consistently hashes those friends to the IM machine that corresponds to each userid hash. (i.e. you know just from the userid what server in the farm should be handling the IM status for that user.
Just dont' think you're gonna spin up a bunch of threads and that will save the day. It's sloppy and works only in very small numbers.
To make the matter worse you would also have to communicate between some arbitrary number of threads (it's a chat server, people want to talk to each other, not themselves.) I would suggest looking into UDP - can be done with a single thread on the server and fits the network activity well - people rarely write more then couple of sentences at a time in chat exchanges, which is very convenient for size-limited UDP datagrams.
There are other approaches of course, but one sure thing though is that you will never be able to do thread per socket at that scale.
I suggest you to read this great article on MSDN Magazine.
Describing:
Threaded Server
Select-Based Server
Asynchronous Server
codes in C# & VB.Net