Tcpclientオブジェクトがあり、接続されているかどうかを確認したい。 tcpclientのconnectedプロパティを使用しますが、最後の操作の状態を返します。そのため、役に立ちません。
それから私はこのコードを使用します:
bool flag;
flag = (tcp.Client.Poll(10000, SelectMode.SelectWrite));
そして
if( tcp.Client.Poll( 0, SelectMode.SelectRead ) )
{
byte[] buff = new byte[1];
if( tcp.Client.Receive( buff, SocketFlags.Peek ) == 0 )
{
flag = false;
}
}
しかし、正しく機能しません。
何か案が?
これはサーバー側の私のコードです:
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);
}
}
そしてこれはクライアント側です:
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
}
}
クライアント側では、IsConnected()は常にfalseを返してconnecttoserverを試行するため、サーバーリスナーは常にクライアントをリストに追加しようとします
代わりにこのコードを使用してください。私はそれをテストし、実際の製品ソフトウェアで使用しました。
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;
}
}
}
編集:ただし、接続を確認するだけでは信頼できず、trueの場合は続行します。これはこのプロパティの実行時に接続のステータスを返す、たとえば確認した後IsConnected
とtrueが返され、通信の最中に接続が失われた可能性があります。失敗の可能性を減らすためにそもそもそれを使用するだけなので、通信全体をtry/catchでラップし、いつでも接続が失われることを予期する必要があります。
私はそのような方法を中継しないことをお勧めします。私の意見では、最良の方法は、ある種のキープアライブメカニズムを実装することです。 X秒ごとに小さなメッセージを送信し、回答を待ちます。次の場合、おそらく切断されます。1.キープアライブメッセージを送信しようとしたときに例外をキャッチした(クライアント側の場合)。 2.しばらくの間、キープアライブメッセージ/応答を受信しません。
また、組み込みのTCPキープアライブを当てにしないことをお勧めします。非常に信頼できないことがわかりました。
更新:この問題に関する素晴らしい投稿が見つかりました: 投稿
ソケット接続のもう一方の端がまだ接続されているかどうかを確認する唯一の方法は、読み取りまたは書き込み操作の結果をキャッチするか、例外をキャッチすることです。
詳細については、このStackOverflowの質問を参照してください: サーバーソケットからのクライアントの切断を即座に検出
以下は、ノンブロッキングモードでSocket
を使用し、サーバーに接続している小さなコードスニペットです。
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 */
}
Lior Ohanaが提案した "キープアライブ"メソッドも素晴らしいアイデアです。各クライアントにX秒ごとに「チェックイン」するように強制します。クライアントは、書き込みで例外が発生した場合にサーバーがなくなったことを検出でき、サーバーは、「キープアライブ」メッセージがX秒以内に受信されなかった場合にクライアントが消えたことを認識します。
GPRS Tcp接続を使用したリモートデバイスでこの問題が発生したため、Lior Ohanaに同意します。デバイスの電源が切れているか切断されている場合、サーバーに警告しませんでした。そこで私はこのコードを使用しました:私はクライアントに特定の時間を送ります
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
{
}
}