1. 程式人生 > >Socket 程式設計經驗談---如何處理socket連線後伺服器端或客戶端的斷開

Socket 程式設計經驗談---如何處理socket連線後伺服器端或客戶端的斷開

現象:伺服器端等待客戶斷連線,當socket連線建立後,如果客戶端異常斷開,伺服器會丟擲異常,從而導致程式執行中斷
目標:希望伺服器一直等待連線,客戶端中斷後程式不退出,而客戶端重新恢復後可以繼續保持連線
程式碼:
public class Receive
{
public static byte[] buffer= new byte[1024];

public static ManualResetEvent socketEvent = new ManualResetEvent(false);
public static Socket sListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public static Socket handler = null;
public static string ClientBroken = "An connection was forcibly closed by the remote host";

public static void receive()
{
try
{
Console.WriteLine("Main ThreadID:" + AppDomain.GetCurrentThreadId());
byte[] bytes = new byte[1024];

IPAddress ipAddr = IPAddress.Parse("127.0.0.1");

int Port = 10001;

IPEndPoint EPServer = new IPEndPoint(ipAddr, Port);

//Binding a socket
sListener.Bind(EPServer);

//Start listening
sListener.Listen(10);

while(true)
{
if (handler==null)
{
//first must make a connect
Console.WriteLine("waiting for a connection...");
//asychronous function for accepting connections
sListener.BeginAccept(new AsyncCallback(AcceptCallback), sListener);
socketEvent.WaitOne();
handler.BeginReceive(buffer,0,buffer.Length,0,new AsyncCallback(ReceiveCallback),handler);
socketEvent.WaitOne();
}
else
{
Console.WriteLine("waiting next message...");
socketEvent.Reset();
handler.BeginReceive(buffer,0,buffer.Length,0,new AsyncCallback(ReceiveCallback),handler);
socketEvent.WaitOne();
}
}
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.ReadLine();
}


public static void AcceptCallback(IAsyncResult ar)
{
try
{
Console.WriteLine("AcceptCallback Thread ID:" + AppDomain.GetCurrentThreadId());
Socket listener = (Socket)ar.AsyncState;
//new socket
handler = listener.EndAccept(ar);
handler.BeginReceive(buffer,0,buffer.Length,0,new AsyncCallback(ReceiveCallback),handler);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}


public static void ReceiveCallback(IAsyncResult ar)
{
string err_message=null;
try
{
Console.WriteLine("ReceiveCallback Thread ID:" + AppDomain.GetCurrentThreadId());

string content = String.Empty;
handler = (Socket)ar.AsyncState;

int bytesRead = handler.EndReceive(ar);

//if there is some data...
if (bytesRead>0)
{
//append it to the main string
content += Encoding.ASCII.GetString(buffer,0,bytesRead);

//if we encounter the end of message character
if (content.IndexOf((char)3)> -1 || content.IndexOf((char)16)>-1)
{
Console.WriteLine("Read "+content.Length+" bytes from socket. /n Data:"+content);
socketEvent.Set();
}
else
{
//otherwise receive the remaining data
handler.BeginReceive(buffer,0,buffer.Length,0,new AsyncCallback(ReceiveCallback),handler);
}
}

}
catch(Exception e)
{
err_message = e.Message;
if (err_message.IndexOf("An existing connection was forcibly closed by the remote host")> -1)
{
Console.WriteLine("An existing connection was forcibly closed by the remote host");
//handler.Shutdown(SocketShutdown.Both);
//handler.Close();

Console.WriteLine("waiting for a connection...");
//asychronous function for accepting connections
sListener.BeginAccept( new AsyncCallback(AcceptCallback), sListener);
}
else
{
Console.WriteLine(e.ToString());
}
}
}
}
說明:關鍵在於最後這段的異常處理,接收中斷後,伺服器端重新等待接收。

現象:客戶端與伺服器連線,當socket連線建立後,如果伺服器端異常斷開,客戶端會丟擲異常,從而導致程式執行中斷
目標:希望客戶端出現提示,伺服器端中斷後程式不退出,而伺服器端重新恢復後可以繼續保持連線
程式碼:
public class AsyncComm
{
public static string theResponse = "";
public static byte[] buffer = new byte[1024];

public static ManualResetEvent socketEvent = new ManualResetEvent(false);
public static Socket sClient= new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public static IPEndPoint EPServer = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 10001);

public static void send(string data)
{
byte[] byteData=null;
byteData = Encoding.ASCII.GetBytes(data);

try
{
if (!sClient.Connected)
{
Console.WriteLine(System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffff")+" "+"Connect begining......");
sClient.BeginConnect(EPServer, new AsyncCallback(ConnectCallback),sClient);
socketEvent.WaitOne();
}
sClient.BeginSend(byteData,0,byteData.Length,0,new AsyncCallback(SendCallback),sClient);
socketEvent.WaitOne();
}
catch (Exception e)
{
Console.WriteLine("Server side is broken...");
socketEvent.Reset();
return;
}
}

         public static void ConnectCallback(IAsyncResult ar)
{
try
{
Thread thr = Thread.CurrentThread;
Console.WriteLine("ConnectCallback Thread State:" + AppDomain.GetCurrentThreadId());

Socket sClient = (Socket)ar.AsyncState;
sClient.EndConnect(ar);
Console.WriteLine("Socket connected to " + sClient.RemoteEndPoint.ToString());
socketEvent.Set();
}
catch (Exception ex)
{
Console.WriteLine(System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffff")+"||"+AppDomain.GetCurrentThreadId()+"||3--Level 3 Server connection is broken, waiting for Level 3 Server connection......");
sClient= new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socketEvent.Set();
}
}

receive函式相同,可以參照寫出
說明:
在每次傳送或接收時檢測當前socket是否連線,如果沒有連線,就啟動連線,並阻塞執行緒等待ConnectCallback的返回