using System;using System.Net;using System.Net.Sockets;using System.Collections;using System.Threading;using System.Runtime.InteropServices;
namespace SocketComponent{ public delegate void SocketOperation(Socket soSocket); public delegate void SocketCloseOperation(string ip,int port); public delegate void SocketProcessData(StateObject state); /// <summary> /// Summary description for SocketBase. /// </summary> public class SocketBase { public SocketOperation processAfterAccept; public SocketOperation processAfterConnect; public SocketCloseOperation processAfterClose; public SocketProcessData processAfterReceive; public SocketOperation processAfterAcceptError;
public bool isAcceptLoop = false; private int socketTimeout; private static Logger logger = new Logger(typeof(SocketBase)); #region public method
public SocketBase() { socketTimeout = SG.SEND_RECEIVE_TIMEOUT; isAcceptLoop = true; }
/// <summary> /// Socket accept method /// </summary> /// <param name="ipAddr">ip address</param> /// <param name="port">listen port</param> /// <returns>Socket instance</returns> public Socket SocketAccept(string ipAddr,int port) { string FUN_NAME="SocketAccept: "; IPAddress ipAddress = null; IPHostEntry ipHostInfo = null; try { ipHostInfo = Dns.Resolve(Dns.GetHostName()); } catch(Exception e) { logger.Error(FUN_NAME+"get host IPAddress error.",e); return null; }
for (int i=0; i<ipHostInfo.AddressList.Length; i++) { ipAddress = ipHostInfo.AddressList[i]; if(ipAddr == null) { ipAddr = ipHostInfo.AddressList[i].ToString(); break; } if (ipAddr == ipAddress.ToString()) { break; } ipAddress = null; } if (ipAddress == null) { logger.Error(FUN_NAME+ipAddr+" is not this host's IPAddress."); return null; }
Socket soSocket = null; IPEndPoint iep ; try { iep= new IPEndPoint(IPAddress.Parse(ipAddr),port); } catch(Exception e) { logger.Error(FUN_NAME+" create IPEndPoint error.",e); return null; } // socket initialize try { // socket create soSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); // ip port bind soSocket.Bind(iep); // listen queue soSocket.Listen(1000); // accept stateobject //StateObject accObject = new StateObject(soSocket,ipAddr,port); // accept begin soSocket.BeginAccept( new AsyncCallback(AcceptCallback),soSocket ); } catch(Exception e) { logger.Warn(FUN_NAME+"error. ",e); SocketClose(soSocket); return null; } return soSocket; }
/// <summary> /// connect to server method /// </summary> /// <param name="ipAddr">remotehost ip address</param> public Socket SocketConnect(string ipAddr,int port) { string FUN_NAME="SocketConnect: "; //parametar check if(null == ipAddr || 0 == ipAddr.Length) { return null; } if(0 > port || 65535 < port) { return null; } Socket soSocket; IPEndPoint remoteEP;
try { IPAddress svIP = IPAddress.Parse(ipAddr); remoteEP = new IPEndPoint(svIP,port); soSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
StateObject so = new StateObject(soSocket,ipAddr,port); soSocket.BeginConnect(remoteEP,new AsyncCallback(ConnectCallback),so); } catch(Exception e) { logger.Warn(FUN_NAME+"error. ",e); return null; } return soSocket; }
/// <summary> /// Socket Syncronized Send Data method /// </summary> /// <param name="client">Socket which connect to server</param> /// <param name="byData">Data that send to Socket</param> public void SyncSendData(Socket client,byte[] byData) { string FUN_NAME="SyncSendData: ";
if( null==client ) { return; } if( null==byData || 0>byData.Length ) { return; }
try { logger.Debug(FUN_NAME+"begin to send data");
int bytesSent = client.Send(byData,0,byData.Length,SocketFlags.None); DateTime sendStartTime = System.DateTime.Now; if(bytesSent < byData.Length) { try { do { bytesSent+=client.Send(byData,bytesSent,byData.Length-bytesSent,SocketFlags.None); if(sendStartTime.AddMinutes(socketTimeout) <= System.DateTime.Now) { logger.Warn(FUN_NAME+"socket time out."); break; } }while(bytesSent < byData.Length); } catch(Exception e) { logger.Warn(FUN_NAME+"retry send data error .",e); SocketClose(client); } } } catch(Exception e) { logger.Warn(FUN_NAME+"error. ",e); SocketClose(client); } }
/// <summary> /// Socket Send Data method /// </summary> /// <param name="client">Socket which connect to server</param> /// <param name="byData">Data that send to Socket</param> public void SendData(Socket client, byte [] byData) { string FUN_NAME="SendData: ";
if( null==client ) { return; } if( null==byData || 0>byData.Length ) { return; }
try { StateObject sendState = new StateObject(client,byData); logger.Debug(FUN_NAME+"begin to send data"); client.BeginSend(byData, 0, byData.Length, 0,new AsyncCallback(SendCallback), sendState); } catch(Exception e) { logger.Warn(FUN_NAME+"error. ",e); SocketClose(client); } }
/// <summary> /// Receive Data From Socket /// </summary> public void ReceiveData( StateObject recState) { string FUN_NAME="ReceiveData: "; if( null == recState) { return; }
try { recState.WorkSocket.BeginReceive(recState.DataBuffer,0,recState.DataBuffer.Length, SocketFlags.None,new AsyncCallback(ReceiveCallback),recState); } catch(Exception e) { logger.Warn(FUN_NAME+"error. ",e); SocketClose(recState.WorkSocket); } }
/// <summary> /// Close Socket connection. /// </summary> /// <param name="s">Socket handle</param> public void SocketClose(Socket s) { string FUN_NAME="SocketClose: "; string IP = ""; int port = 0; if(null != s) { if(s.Connected) { try { IPEndPoint ipep = (IPEndPoint)s.RemoteEndPoint; IP = ipep.Address.ToString(); port = ipep.Port; } catch{} try { s.Shutdown( SocketShutdown.Both ); } catch(Exception e) { logger.Warn(FUN_NAME+"error. ",e); } } try { s.Close(); } catch(Exception e) { logger.Warn(FUN_NAME+"error. ",e); } processAfterClose(IP,port); } } #endregion
#region private method /// <summary> /// Async accept thread /// </summary> /// <param name="ar"></param> private void AcceptCallback(IAsyncResult ar) { string FUN_NAME="AcceptCallback: "; Socket svSock = null; Socket clSock = null; try { svSock = (Socket)ar.AsyncState; if(null == svSock) { return; } //get client socket clSock = svSock.EndAccept(ar); if(null!=clSock) { clSock.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.DontLinger,0); if(SG.KEEP_ALIVE) { SetKeepAlive(clSock,SG.KEEP_ALIVE_TIME,SG.KEEP_ALIVE_INTERVAL); } processAfterAccept(clSock); } //start to accept next connection request svSock.BeginAccept( new AsyncCallback(AcceptCallback),svSock ); } catch(ObjectDisposedException ode) { logger.Warn(FUN_NAME+"Server host is shut down.",ode); //socket has been closed SocketClose(svSock); SocketClose(clSock); // restart listen and accept if(isAcceptLoop) { processAfterAcceptError(null); } } catch (Exception e ) { logger.Warn(FUN_NAME+"error.",e); SocketClose(svSock); SocketClose(clSock); if(isAcceptLoop) { processAfterAcceptError(null); } } }
/// <summary> /// Async connect thread /// </summary> /// <param name="ar"></param> private void ConnectCallback(IAsyncResult ar) { string FUN_NAME="ConnectCallback: "; StateObject so = null; try { so = (StateObject)ar.AsyncState; Socket clSock = so.WorkSocket; if(null!=clSock) { clSock.EndConnect(ar); clSock.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.DontLinger,0); if(SG.KEEP_ALIVE) { SetKeepAlive(clSock,SG.KEEP_ALIVE_TIME,SG.KEEP_ALIVE_INTERVAL); } processAfterConnect(clSock); } } catch (Exception e ) { if(so!=null) { logger.Warn(FUN_NAME + "remote server "+so.svIP+":"+so.svPort+" is not online",e); } else { logger.Warn(FUN_NAME + "has other error.",e); } } }
/// <summary> /// Async send data to socket thread /// </summary> /// <param name="ar"></param> private void SendCallback(IAsyncResult ar) { string FUN_NAME="SendCallback: "; int bytesSent = 0; StateObject sendState; Socket sendSocket = null; try { sendState = (StateObject)ar.AsyncState; sendSocket = sendState.WorkSocket;
bytesSent = sendSocket.EndSend(ar);
DateTime sendStartTime = System.DateTime.Now; if(bytesSent < sendState.DataBuffer.Length) { try { do { bytesSent+=sendSocket.Send(sendState.DataBuffer,bytesSent, sendState.DataBuffer.Length-bytesSent,SocketFlags.None); if(sendStartTime.AddMinutes(socketTimeout) <= System.DateTime.Now) { logger.Warn(FUN_NAME+"socket time out."); break; } }while(bytesSent < sendState.DataBuffer.Length); } catch(Exception e) { logger.Warn(FUN_NAME+"retry send data error .",e); SocketClose(sendSocket); } } if(sendState.DataBuffer.Length!=bytesSent) { logger.Warn("Socket send data failure.Need send bytes is "+sendState.DataBuffer.Length .ToString()+".Have sent bytes is "+bytesSent.ToString()); } logger.Debug("Send data :",sendState.DataBuffer); logger.Debug(FUN_NAME+"end to send data."); } catch(ObjectDisposedException ode) { logger.Warn(FUN_NAME+"Remote host is shut down.",ode); SocketClose(sendSocket); } catch (Exception e ) { logger.Warn(FUN_NAME + "error.",e); SocketClose(sendSocket); } } /// <summary> /// Async receive data to socket thread /// </summary> /// <param name="ar"></param> private void ReceiveCallback(IAsyncResult ar) { string FUN_NAME="ReceiveCallback: "; int bytesReceive = 0; StateObject recState; Socket recSocket=null; try { recState =(StateObject)ar.AsyncState; recSocket = recState.WorkSocket;
bytesReceive = recSocket.EndReceive(ar); if( 0>=bytesReceive) { string remoteIP = ""; int remotePort = 0; if(recSocket.RemoteEndPoint!=null) { IPEndPoint ipep = (IPEndPoint)recSocket.RemoteEndPoint; remoteIP = ipep.Address.ToString(); remotePort = ipep.Port; } logger.Warn(FUN_NAME+"Remote host "+remoteIP+":"+remotePort.ToString()+" is shut down."); //remote host shut down SocketClose(recSocket); return; } DateTime recStartTime = System.DateTime.Now; if( bytesReceive < recState.DataBuffer.Length ) { try { do { bytesReceive+=recSocket.Receive(recState.DataBuffer,bytesReceive, recState.DataBuffer.Length-bytesReceive,SocketFlags.None); if(recStartTime.AddMinutes(socketTimeout) <= System.DateTime.Now) { logger.Warn(FUN_NAME+"socket time out."); break; } } while( bytesReceive < recState.DataBuffer.Length ); } catch(Exception e) { logger.Warn(FUN_NAME+"retry recieve data error .",e); SocketClose(recSocket); } } if( recState.DataBuffer.Length != bytesReceive) { logger.Warn("Socket read failure. Need receive bytes is "+recState.DataBuffer.Length .ToString()+". Have received bytes is "+bytesReceive.ToString()); SocketClose(recSocket); return; } processAfterReceive(recState); } catch (Exception e ) { logger.Warn(FUN_NAME + "error.",e); SocketClose(recSocket); } } /// <summary> /// SetKeepAlive of one socket /// </summary> /// <param name="socket">the socket object to SetKeepAlive</param> /// <param name="KeepAliveTime">SetKeepAliveTime(min)</param> /// <param name="KeepAliveInterval">SetKeepAliveInterval(s)</param> private void SetKeepAlive(Socket socket, int KeepAliveTime, int KeepAliveInterval) { ulong IOC_IN = 0x80000000 ; ulong IOC_VENDOR = 0x18000000 ; ulong SIO_KEEPALIVE_VALS = IOC_IN | IOC_VENDOR | 4; KeepAliveTime = KeepAliveTime *60*1000; //minute to millsecond KeepAliveInterval = KeepAliveInterval*1000; //second to millsecond /* the native structure struct tcp_keepalive { ULONG onoff; ULONG keepalivetime; ULONG keepaliveinterval; }; */ // marshal the equivalent of the native structure into a byte array uint dummy = 0; byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3]; BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0); BitConverter.GetBytes((uint)KeepAliveTime).CopyTo(inOptionValues, Marshal.SizeOf(dummy)); BitConverter.GetBytes((uint)KeepAliveInterval).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2); // call WSAIoctl via IOControl socket.IOControl((int)SIO_KEEPALIVE_VALS, inOptionValues, null); } #endregion
} }
