am trying to use Socket.Select in multi-threaded application , and I don't wanna use async socket , so here is my code :-
public class Server {
private TcpListener m_listener;
private IConnectionFactory m_factory;
private List<Socket> Sockets = new List<Socket>();
private ConcurrentDictionary<Socket, Connection> Clients = new ConcurrentDictionary<Socket, Connection>();
private ConcurrentDictionary<string, ConcurrentQueue<Socket>> Threads = new ConcurrentDictionary<string, ConcurrentQueue<Socket>>();
private int maxsockets = 0;
private object sync = new object();
public Server(string ip, int port, IConnectionFactory factory, int maxsockets) {
IPAddress ipa = IPAddress.Parse(ip);
TcpListener listener = new TcpListener(ipa, port);
this.maxsockets = maxsockets;
m_listener = listener;
m_factory = factory;
int threads = maxsockets <= 100 ? 1 : maxsockets / 100;
for (int i = 0; i < threads; i++) {
Thread th = new Thread(HandelSockets);
th.Name = i.ToString();
Threads.TryAdd(i.ToString(), new ConcurrentQueue<Socket>());
th.Start();
}
}
public void HandelSockets() {
ConcurrentQueue<Socket> queue = Threads[Thread.CurrentThread.Name];
Connection temp;
Socket sock;
while (true) {
try {
lock (sync) {
while (queue.TryDequeue(out sock)) {
if (Clients.TryGetValue(sock, out temp)) {
if (!temp.Alive) {
Sockets.Remove(sock);
Clients.TryRemove(sock, out temp);
} else {
temp.Receive();
Console.WriteLine("recved from thread num : " + Thread.CurrentThread.Name);
}
}
}
}
} catch { }
Thread.Sleep(1);
}
}
public void Run() {
m_listener.Start();
Console.WriteLine("listen started");
Sockets.Add(m_listener.Server);
while (true) {
try {
var temp_list = Sockets.ToList();
Socket.Select(temp_list, null, null, 1000);
foreach (Socket socket in temp_list) {
if (socket == m_listener.Server) {
var sock = m_listener.AcceptSocket();
Sockets.Add(sock);
Connection conn = m_factory.Create(sock);
Clients.TryAdd(sock, conn);
} else if (Clients.Count >= maxsockets) {
Clients[socket].OnMaxConnections();
break;
} else {
if (!Threads.Values.Any(x => x.Contains(socket))) {
var quque = Threads.Values.Where(x => !x.Contains(socket)).OrderBy(x => x.Count).First();
lock (sync) {
quque.Enqueue(socket);
}
}
break;
}
}
} catch {
}
}
}
}
problem is after a while one of the connected sockets will be delayed , one of the sockets will stop sending or receiving until one of the other sockets do so!, or it might take a few secs/minutes until it comeback to receive and send!
I have no clue why would that happen!? maybe the way I select a queue with? , I hope someone can point me to what could make that delay happen thanks.
lock (sync) {
while (queue.TryDequeue(out sock)) {
if (Clients.TryGetValue(sock, out temp)) {
if (!temp.Alive) {
Sockets.Remove(sock);
Clients.TryRemove(sock, out temp);
} else {
temp.Receive();
Console.WriteLine("recved from thread num : " + Thread.CurrentThread.Name);
}
}
}
}
I can't see your implementation of Connection.Receive but if it's a blocking call i.e. doesn't return until it has received data, then that thread would be holding onto a lock on the sync object which is causing your other threads to wait and therefore causing the delay.
Now that you know what is causing the problem you can make the necessary changes, however i highly recommend you use the async methods as it'll perform better and remove the need for locking entirely.
Edit: oh i just realised this is an old question
Related
So I have a really really weird issue. This is going to take a tiny bit of a wall to explain fully..
I'm running some game servers for a Unity game called SCP: Secret Laboratory, and have a custom-made C# Discord Bot that helps my staff moderate both in-game and on Discord, by controlling bans, logging player warnings, etc, etc.
I'm trying to make the two platforms communicate with each other over a TCP port over the local machine address (127.0.0.1).
I've setup the listener on the bot, to whom the individual servers (there are 7) will connect to when they are ready.
I've also set it up so that if the connection is broken from either side, the servers will close the TcpClient, start a new one, and attempt to connect, while the listener just resumes listening on that port for a connection again, after closing the client on it's side.
I've tested it several times by closing the client, the socket, or just rebooting the program on either end, and they seem to without fail reconnect flawlessly.
Here's my issue..
While the game servers remain empty, everything is fine. After players start to connect, anywhere from 5mins to an hour will go by, then suddenly, seemingly at random, the server no longer 'hears' the bot when it talks across the connection, however, the bot itself does not hit an error when trying to send data, the server just never receives any. What's stranger, is the server will continue to send it's own data over the connection, and the bot does receive that data.
To attempt to 'reset' the connection with a new client when this happens, I make the servers send an initial heartbeat to the bot when they first connect. This tells the bot that the connection is established on the server's end, and begins a looping thread that will send an AYT message to the server every 10s, to which the server must reply.
If the bot sends 3 AYT messages without getting a response back, it will close the TcpClient, and start listening for a new one.
At this time, the server detects the connection was closed, disposes of it's client, instantiates a new one, and tries to connect successfully. The bot then receives it's initial heartbeat, and starts the AYT timer for that client again, but the server continues to not receive them. Or any data whatsoever sent from the bot, even though the bot still receives data from the server during this time.
The only solution to fix the problem at that point, is to fully restart the game server, after which it will connect to the bot and work perfectly fine, until it.. just doesn't anymore.
For reference, pastebins of the code used are below.
The bot "listener" side
public class ProcessSTT
{
private static ConcurrentDictionary<int, TcpClient> bag = new ConcurrentDictionary<int, TcpClient>();
private static ConcurrentDictionary<int, int> heartbeats = new ConcurrentDictionary<int, int>();
public static void SendData(string data, int port, ulong channel = 0)
{
try
{
BinaryFormatter formatter = new BinaryFormatter();
SerializedData.SerializedData serializedData =
new SerializedData.SerializedData { Data = data, Port = port, Channel = channel };
//Console.WriteLine($"Sending {serializedData.Data}");
if (!bag.ContainsKey(port))
{
Console.WriteLine($"STT: Bag does not contain {port}");
return;
}
if (bag[port] != null && bag[port].Connected)
formatter.Serialize(bag[port].GetStream(), serializedData);
else
{
Console.WriteLine($"Error - Bag {port} is null or not connected.");
if (bag.TryRemove(port, out TcpClient client))
client.Dispose();
}
}
catch (IOException s)
{
Console.WriteLine($"STT: Socket exception, removing..");
KeyValuePair<int, TcpClient> thingything = default;
foreach (var thing in bag)
if (thing.Key == port)
thingything = thing;
if (bag.TryRemove(thingything.Key, out TcpClient _client))
{
_client.Close();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private static List<TcpListener> listener = new List<TcpListener>();
public static void Init()
{
for (int i = 1; i < 8; i++)
{
TcpListener list = new TcpListener(IPAddress.Loopback, 11900 + i);
Console.WriteLine($"STT: Listener started for port {11900 + i}");
listener.Add(list);
list.Start();
ThreadPool.QueueUserWorkItem(ListenForConn, list);
}
}
public static async Task Heartbeat(int port)
{
await Task.Delay(10000);
for (;;)
{
Console.WriteLine("STT: Starting Heartbeat");
if (heartbeats[port] > 3)
{
Console.WriteLine($"STT: Removing {port} due to heartbeat timeout.");
if (bag.TryRemove(port, out TcpClient client))
client.Close();
heartbeats.TryRemove(port, out int _);
return;
}
heartbeats[port]++;
Console.WriteLine($"STT: Sending heartbeat to: {port}");
if (!bag[port].Connected)
{
Console.WriteLine($"STT: {port} is null, removing.");
if (bag.TryRemove(port, out TcpClient client))
client.Close();
return;
}
SendData("ping", port, 653737934150959115);
await Task.Delay(10000);
}
}
public static void ListenForConn(object token)
{
Console.WriteLine("STT: Listener started.");
TcpListener listen = token as TcpListener;
for (;;)
{
try
{
TcpClient thing = listen.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(ListenOn, thing);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
public static async Task ReceiveData(SerializedData.SerializedData data, TcpClient client)
{
try
{
if (data == null)
{
Console.WriteLine("STT: Received data null");
return;
}
if (data.Data == "ping")
{
if (!bag.ContainsKey(data.Port))
{
Console.WriteLine($"STT: Adding {data.Port}");
bag.TryAdd(data.Port, client);
}
if (!bag[data.Port].Connected || bag[data.Port] == null)
{
Console.WriteLine($"STT: Bag {data.Port} not connected or null, removing.");
if (bag.TryRemove(data.Port, out TcpClient cli))
{
cli?.Close();
}
}
Console.WriteLine($"STT: Received heartbeat for: {data.Port}");
if (!heartbeats.ContainsKey(data.Port))
{
Heartbeat(data.Port);
heartbeats.TryAdd(data.Port, 0);
}
else
heartbeats[data.Port]--;
return;
}
Console.WriteLine(data.Data);
data.Data = data.Data.Substring(data.Data.IndexOf('#') + 1);
//Console.WriteLine("Getting guild.");
SocketGuild guild = Bot.Discord.GetGuild(478381106798788639);
//Console.WriteLine("Getting channel");
SocketTextChannel chan = guild.GetTextChannel(data.Channel);
//Console.WriteLine("Sending message.");
await chan.SendMessageAsync($"Server {data.Port -= 7770}: {data.Data}");
if (data.Port == 7771)
{
DiscordWebhookClient webhook = new DiscordWebhookClient(
"");
await webhook.SendMessageAsync($"{data.Data}");
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
public static void ListenOn(object token)
{
TcpClient client = token as TcpClient;
try
{
BinaryFormatter formatter = new BinaryFormatter();
for (;;)
{
SerializedData.SerializedData serializedData;
if (!client.Connected)
{
Console.WriteLine($"Client not connected..");
client.Close();
continue;
}
serializedData = formatter.Deserialize(client.GetStream()) as SerializedData.SerializedData;
new Thread(() => ReceiveData(serializedData, client)).Start()
}
}
catch (SerializationException s)
{
Console.WriteLine($"STT: Serialization exception, removing..");
KeyValuePair<int, TcpClient> thingything = default;
foreach (var thing in bag)
if (thing.Value == client)
thingything = thing;
if (bag.TryRemove(thingything.Key, out TcpClient _client))
{
_client.Close();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
The server "speaker" side
public class ProcessSTT
{
private static TcpClient tcpClient;
public static readonly ConcurrentQueue<SerializedData.SerializedData> dataQueue = new ConcurrentQueue<SerializedData.SerializedData>();
private static Thread _init;
private static bool _locked;
public static void Init()
{
if (_locked)
return;
_locked = true;
Thread.Sleep(1000);
try
{
Plugin.Log($"STT: Starting INIT.");
tcpClient?.Close();
tcpClient = new TcpClient();
while (!tcpClient.Connected)
{
Plugin.Log($"STT: While loop start");
Thread.Sleep(2000);
try
{
tcpClient.Connect("127.0.0.1", ServerConsole.Port + 4130);
}
catch (SocketException)
{
tcpClient.Client.Disconnect(false);
}
catch (Exception e)
{
Plugin.Log($"STT: {e}");
}
}
Thread thread = new Thread(ReceiveData);
thread.Start();
SendData("ping", 0);
_locked = false;
}
catch (IOException i)
{
_init = new Thread(Init);
_init.Start();
}
catch (Exception e)
{
ServerConsole.AddLog(e.ToString());
}
}
public static void SendData(string data, ulong channel)
{
try
{
if (!tcpClient.Connected)
throw new InvalidOperationException("Tcp Client not connected!");
SerializedData.SerializedData serializedData = new SerializedData.SerializedData
{
Data = data, Port = ServerConsole.Port, Channel = channel
};
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(tcpClient.GetStream(), serializedData);
ServerConsole.AddLog($"Sent {data}");
}
catch (IOException i)
{
_init = new Thread(Init);
_init.Start();
}
catch (Exception e)
{
ServerConsole.AddLog(e.ToString());
}
}
public static void ReceiveData()
{
try
{
if (!tcpClient.Connected)
throw new InvalidOperationException("Tcp Client not connected!");
BinaryFormatter formatter = new BinaryFormatter();
for (;;)
{
SerializedData.SerializedData deserialize =
formatter.Deserialize(tcpClient.GetStream()) as SerializedData.SerializedData;
if (deserialize == null)
continue;
dataQueue.Enqueue(deserialize);
}
}
catch (SerializationException s)
{
_init = new Thread(Init);
_init.Start();
}
catch (IOException e)
{
_init = new Thread(Init);
_init.Start();
}
catch (Exception e)
{
ServerConsole.AddLog(e.ToString());
}
}
}
public class HandleQueue
{
public static ulong channelid;
public static void HandleQueuedItems()
{
while (ProcessSTT.dataQueue.TryDequeue(out SerializedData.SerializedData result))
{
string command = result.Data;
Plugin.Log($"STT: Received {result.Data} for {result.Port}");
if (result.Port != ServerConsole.Port)
return;
if (result.Data == "ping")
{
Plugin.Log("STT: BLART Heartbeat received.");
ProcessSTT.SendData("ping", 0);
return;
}
channelid = result.Channel;
try
{
GameCore.Console.singleton.TypeCommand($"/{command}", new BlartSender());
}
catch (Exception e)
{
ServerConsole.AddLog(e.ToString());
}
}
}
public static IEnumerator<float> Handle()
{
for (;;)
{
HandleQueuedItems();
yield return Timing.WaitForSeconds(1f);
}
}
}
public class BlartSender : CommandSender
{
public override void RaReply(string text, bool success, bool logToConsole, string overrideDisplay)
{
ProcessSTT.SendData($"{text}", HandleQueue.channelid);
}
public override void Print(string text)
{
ProcessSTT.SendData($"{text}", HandleQueue.channelid);
}
public override string SenderId => "BLART";
public override string Nickname => "BLART";
public override ulong Permissions => ServerStatic.GetPermissionsHandler().FullPerm;
public override byte KickPower => Byte.MaxValue;
public override bool FullPermissions => true;
}`
I'm trying to create two client threads which are connected to a server. When there are two connections (Two entries in the threadsArray), I want the start to be announced. The code is never hitting the threadRequest.annouceStart() call however.
Through debugging I have determined that the first thread that is created is being stopped while the server is listening for another connection in the form of the second client. Is it this "freeze" as the server hangs waiting for another connection that is stopping the first thread?
static void Main(string[] args)
{
runServer();
}
static void runServer()
{
TcpListener listener;
Socket connection;
Handler threadRequest;
string defaultName = "";
int defaultScore = 0;
int i = 0;
Thread[] threadsArray = new Thread[2];
try
{
listener = new TcpListener(IPAddress.Any, 43);
listener.Start();
Console.WriteLine("Quiz Server launched");
Console.WriteLine("A default user has been created for testing purposes");
while(true) //main game loop
{
connection = listener.AcceptSocket();
threadRequest = new Handler();
threadsArray[i] = new Thread(() => threadRequest.clientInteraction(connection, teamInformation));
threadsArray[i].Name = "Team" + (i + 1);
threadsArray[i].Start();
i++;
if (threadsArray[1] != null)
{
if (threadsArray[1].ThreadState == ThreadState.Running
&& threadsArray[0].ThreadState == ThreadState.Running)
{
foreach (Thread thread in threadsArray)
{
threadRequest.announceStart(connection, teamInformation);
}
}
}
}
}
catch (Exception e)
{
Console.WriteLine("Exception: " + e.ToString());
Console.ReadKey();
}
}
edit: added Handler class definition.
class Handler {
public static string interactionType;
public static string pTeamName;
public static string pAnswer;
public void announceStart(Socket connection, ConcurrentDictionary<string, int> teamInformation)...
public void clientInteraction(Socket connection, ConcurrentDictionary<string, int> teamInformation)...
public static void parseStrings(StreamReader sr, string recievedLine, out string pTeamName,
out string pAnswer)...
static void findInteractionType(out string interactionType, string clientString)...
}
Credit to Andrey Polyakov in comments.
"Thread stops if executing method returns. You should run infinite loop in such method for avoid thread termination" in regards to the clientInteraction() method.
I have an issue about the server-client communication.
I googled around but I did not find a solution to this.
Right now I am using 32feet in order to get in touch 2 or more (till 7) BT clients to 1 BT server.
I need to broadcast a message from the server to every device in the same time, but I don't know how to do it.
The only way I figured out was to use the list of connection in order to send the message one per time, but it means a delay between each message sent (around 100 ms per device). Unfortunately it means to have a large delay on the last one.
Can someone please give me an advice on how to solve this problem?
Is there a way to broadcast the message to all devices in the same time?
If it can be helpfull, here there is the handle of connection and reading from devices.
Thanks for your help
private void btnStartServer_Click(object sender, EventArgs e)
{
btnStartClient.Enabled = false;
ConnectAsServer();
}
private void ConnectAsServer()
{
connessioniServer = new List<BluetoothClient>();
// thread handshake
Thread bluetoothConnectionControlThread = new Thread(new ThreadStart(ServerControlThread));
bluetoothConnectionControlThread.IsBackground = true;
bluetoothConnectionControlThread.Start();
// thread connessione
Thread bluetoothServerThread = new Thread(new ThreadStart(ServerConnectThread));
bluetoothServerThread.IsBackground = true;
bluetoothServerThread.Start();
}
private void ServerControlThread()
{
while (true)
{
foreach (BluetoothClient cc in connessioniServer)
{
if (!cc.Connected)
{
connessioniServer.Remove(cc);
break;
}
}
updateConnList();
Thread.Sleep(0);
}
}
Guid mUUID = new Guid("fc5ffc49-00e3-4c8b-9cf1-6b72aad1001a");
private void ServerConnectThread()
{
updateUI("server started");
BluetoothListener blueListener = new BluetoothListener(mUUID);
blueListener.Start();
while (true)
{
BluetoothClient conn = blueListener.AcceptBluetoothClient();
connessioniServer.Add(conn);
Thread appoggio = new Thread(new ParameterizedThreadStart(ThreadAscoltoClient));
appoggio.IsBackground = true;
appoggio.Start(conn);
updateUI(conn.RemoteMachineName+" has connected");
}
}
private void ThreadAscoltoClient(object obj)
{
BluetoothClient clientServer = (BluetoothClient)obj;
Stream streamServer = clientServer.GetStream();
streamServer.ReadTimeout=1000;
while (clientServer.Connected)
{
try
{
int bytesDaLeggere = clientServer.Available;
if (bytesDaLeggere > 0)
{
byte[] bytesLetti = new byte[bytesDaLeggere];
int byteLetti = 0;
while (bytesDaLeggere > 0)
{
int bytesDavveroLetti = streamServer.Read(bytesLetti, byteLetti, bytesDaLeggere);
bytesDaLeggere -= bytesDavveroLetti;
byteLetti += bytesDavveroLetti;
}
updateUI("message sent from "+clientServer.RemoteMachineName+": " + System.Text.Encoding.Default.GetString(bytesLetti));
}
}
catch { }
Thread.Sleep(0);
}
updateUI(clientServer.RemoteMachineName + " has gone");
}
private void updateUI(string message)
{
Func<int> del = delegate()
{
textBox1.AppendText(message + System.Environment.NewLine);
return 0;
};
Invoke(del);
}
private void updateConnList()
{
Func<int> del = delegate()
{
listaSensori.Items.Clear();
foreach (BluetoothClient d in connessioniServer)
{
listaSensori.Items.Add(d.RemoteMachineName);
}
return 0;
};
try
{
Invoke(del);
}
catch { }
}
I don't exactly understand how you do it right now (the italian names are not helping...) but maybe my solution can help you.
first of all, bluetooth classic does not support broadcast. so you have to deliver at one at a time.
i do connect to 7 serial port devices at a time, using 7 threads. then i tell every thread to send data. this is very close to same time, but of course not exactly.
let me know if that helps or if you need a code example.
I connect to my (C#) server and from an App built in Corona SDK but for the second person can never connect.
I have tried using different IP's i.e. two cellphones with external IP's with no difference.
This is how my server listener works:
server.cs
void Listener()
{
while (isRunning)
{
try
{
Socket socket = listener.AcceptSocket();
foreach (var worker in workers)
if (worker.IsAvailable)
{
worker.ProcessNewSocket(socket);
break;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
public void Run()
{
Console.WriteLine("Server.Run()");
listener.Start();
isRunning = true;
foreach (var worker in workers)
{
Thread t = new Thread(worker.Run);
t.Start();
}
Listener();
}
ServerWorker.cs
public void ProcessNewSocket(Socket socket)
{
var pc = new PlayerClient(this);
sockets.Add(socket, pc);
}
// this method will be called in cycle
public void Run()
{
while (server.IsRunning)
{
List<Socket> readList = sockets.Keys.ToList(); //List of sockets that have some data from client available for reading.
List<Socket> writeList = sockets.Keys.ToList(); //List of sockets that are ready to write (send) to the client. An action was made to a table and the change was sent to PlayerClient.Write and is now stored in the queue (MemoreStream)
List<Socket> errorList = sockets.Keys.ToList();
if (readList.Count() != 0 || writeList.Count() != 0 || errorList.Count() != 0)
{
// for providing security you can use System.Net.Security.SslStream here when read/write data,
// see http://msdn.microsoft.com/ru-ru/library/system.net.security.sslstream(v=vs.110).aspx
Socket currentSocket = null;
// foreach socket with events
try
{
foreach (var s in readList)
{
currentSocket = s;
//TODO: Get the actual length of the message.
byte[] data = new byte[2048];
s.Receive(data);
sockets[s].OnData(data);
}
foreach (var s in writeList)
{
currentSocket = s;
if (sockets[s].IsWriteDataAvailable())
{
s.Send(sockets[s].GetWriteBuffer());
sockets[s].ClearWriteBuffer();
}
}
foreach (var s in errorList)
{
//OnError
}
}
// we got exception, depending on the type...
catch (SocketException ex)
{
//Console.WriteLine(ex.ToString());
// send client error message, this is not always possible(for example network failure)
// maybe we would like to notify his opponent about connection failure
// terminate connection
if (ex.ErrorCode == (int)SocketError.ConnectionAborted || ex.ErrorCode == (int)SocketError.ConnectionReset)
RemoveSocket(currentSocket);
else
Console.WriteLine("Other problem .. " + ex.ErrorCode.ToString());
}
}
}
}
I'm new in network programming so I'm not really sure what to do. I have read about using ASync but first I would like to know if there is something I can do with this code and/or if I should change it completely?
I think the "BREAK" statement in your Listener() Block;
foreach (var worker in workers)
if (worker.IsAvailable)
{
worker.ProcessNewSocket(socket);
break; // this BREAK WILL END YOUR loop AFTER first CLIENT FOUND.
}
So, try removing break as;
foreach (var worker in workers)
{
if (worker.IsAvailable)
{
worker.ProcessNewSocket(socket);
}
}
I have tcpclient object and i want to determine if it's connected or not.
i use connected property of tcpclient but it returns the state of last operation. so its not useful.
then i use this code :
bool flag;
flag = (tcp.Client.Poll(10000, SelectMode.SelectWrite));
and
if( tcp.Client.Poll( 0, SelectMode.SelectRead ) )
{
byte[] buff = new byte[1];
if( tcp.Client.Receive( buff, SocketFlags.Peek ) == 0 )
{
flag = false;
}
}
but it does not work properly.
Any idea?
this is my code in server side :
private ArrayList _ClientList = new ArrayList();
public ClsServer(int port)
{
_TCPListener = new TcpListener(IPAddress.Any, port);
_TCPListener.Start();
Thread ListenThread = new Thread(new ThreadStart(ListenForClients));
ListenThread.IsBackground = true;
ListenThread.Start();
}
private void ListenForClients()
{
while (true)
{
//blocks until a client has connected to the server
TcpClient client = this._TCPListener.AcceptTcpClient();
client.ReceiveTimeout = 0;
//create a thread to handle communication with connected client
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.IsBackground = true;
clientThread.Start(client);
}
}
private void HandleClientComm(object client)
{
try
{
TcpClient tcpClient = (TcpClient)client;
AddObject(tcpclient);
int bytesRead;
string message = "";
byte[] RecievedPack = new byte[1024 * 1000];
NetworkStream clientStream = tcpClient.GetStream();
while (true)
{
bytesRead = 0;
try
{
////blocks until a client sends a message
bytesRead = clientStream.Read(RecievedPack, 0, RecievedPack.Length);
int Len = BitConverter.ToInt32(RecievedPack, 0);
message = UTF8Encoding.UTF8.GetString(RecievedPack, 0, Len);
}
catch (Exception er)
{
//When Client is disconnected
if (er.GetType() == typeof(IOException))
{
RemoveObject(client);
break;
}
}
//message has successfully been received
// do something
}
RemoveObject(client);
}
catch(Exception e)
{
// RemoveObject(client);
}
}
private void AddObject(object obj)
{
int totalcount, index;
totalcount = _ClientList.Count;
index = 0;
while (index < totalcount)
{
TcpClient alcobj = (TcpClient)_ClientList[index];
try
{
if (IPAddress.Equals(((IPEndPoint)alcobj.Client.RemoteEndPoint).Address,
((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address))
{
_ClientList.Remove(alcobj);
break;
}
index++;
}
catch (Exception er)
{
if (er.GetType() == typeof(ObjectDisposedException))
RemoveObject(alcobj);
}
finally
{
totalcount = _ClientList.Count;
}
}
_ClientList.Add(obj);
}
private void RemoveObject(object obj)
{
if (_ClientList.IndexOf(obj) > -1)
{
_ClientList.Remove(obj);
SendClientState(IP, false);
}
}
and this is the client side :
public bool IsConnected
{
try
{
if (_TcpClient != null && _TcpClient.Client != null && _TcpClient.Client.Connected)
{
// Detect if client disconnected
if (_TcpClient.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (_TcpClient.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Client disconnected
return false;
}
else
{
return true;
}
}
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
private void clsClient()
{
if(!IsConnected())
{
Connecttoserver()
}
}
private void ConnectToServer()
{
try
{
NetworkStream _NetworkStream = _TcpClient.GetStream();
byte[] _RecievedPack = new byte[1024 * 1000];
string _Message = string.Empty;
int _BytesRead;
int _Length;
while (true)
{
_BytesRead = _NetworkStream.Read(_RecievedPack, 0, _RecievedPack.Length);
_Length = BitConverter.ToInt32(_RecievedPack, 0);
_Message = UTF8Encoding.UTF8.GetString(_RecievedPack, 4, _Length);
if (_BytesRead != 0)
{
if (OnReceive != null)
// do something
_NetworkStream.Flush();
}
}
}
catch (Exception exp)
{
// do something
}
}
in client side, IsConnected() always return false and try to connecttoserver, so the server listener always try to add the client in a list
Use this code instead, I have tested it and using it in real production software:
public bool IsConnected
{
get
{
try
{
if (_tcpClient != null && _tcpClient.Client != null && _tcpClient.Client.Connected)
{
/* pear to the documentation on Poll:
* When passing SelectMode.SelectRead as a parameter to the Poll method it will return
* -either- true if Socket.Listen(Int32) has been called and a connection is pending;
* -or- true if data is available for reading;
* -or- true if the connection has been closed, reset, or terminated;
* otherwise, returns false
*/
// Detect if client disconnected
if (_tcpClient.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (_tcpClient.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Client disconnected
return false;
}
else
{
return true;
}
}
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
}
Edit: However you can't rely on just checking the connection and if true proceed, because it returning the status of connection at time this property executed, for instance after you check IsConnected and it returns true, and while you are in the middle of communication, the connection maybe lost there! we just use it in the first place to reduce the probability of failure, So you have to wrap the whole communication in a try/catch and expect the connection to be lost at any time!
I suggest not relaying on such methods.
For my opinion, the best way is to implement some kind of keep-alive mechanism.
Every X seconds, send a small message and wait for an answer.
You're probably disconnected when:
1. You catch an exception when trying to send a keep-alive message (if you're the client side).
2. You don't get a keep-alive message/response for some period of time.
I also suggest not to count on the built-in TCP keep-alive, I found it very not-reliable.
Updated: Found a nice post for this matter: Post
The only way to know whether or not the other end of a socket connection is still connected is by catching the result of a read or write operation or possibly catching an exception.
For more information please refer to this StackOverflow question:
Instantly detect client disconnection from server socket
Here is a small snippet of code that is simply using a Socket in non-blocking mode and connected to a server.
try
{
bytesRead = nambSok.Receive(message, 4096, SocketFlags.None);
}
catch (SocketException e)
{
//a socket error has occured
switch (e.SocketErrorCode)
{
case System.Net.Sockets.SocketError.TimedOut:
case System.Net.Sockets.SocketError.WouldBlock:
if (doDisconnect == false)
{
continue;
}
break;
case System.Net.Sockets.SocketError.ConnectionReset:
case System.Net.Sockets.SocketError.ConnectionAborted:
isConnected = false;
break;
}
}
if (bytesRead > 0)
{
/* do something with data */
}
The "keep-alive" method Lior Ohana proposed is also a great idea. Force each client to "check in" every X seconds. The client can detect the server is gone if an exception occurs on the write, and the server knows the client has gone away if a "keep-alive" message hasn't been received in X seconds.
I agree with Lior Ohana because I had this problem with remote devices that were used GPRS Tcp connection. when device is turn off or disconnected, it did not alert to the sever. There for I used this code: I send Specific time to the clients
void AnalyzeForHandShaking(Socket socketin, string Message)
{
Socket handler = socketin;
try
{
Message = Message.Trim();
if (!string.IsNullOrEmpty(Message.Trim()))
// if (Message.Equals("~"))
{
// string Serial = getSerialFromSocket(socketin).Trim();
DateTime dt = DateTime.Now;
if (handler!=null)
//if there is serial in hastable
if (!arrIPTimeHandShaking.ContainsKey(handler))
{
arrIPTimeHandShaking.Add(handler, dt);
}
else
{
arrIPTimeHandShaking[handler] = dt;
}
}
}
catch
{
}
}