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
}
}