public class StateObject  
{  
    public Socket workSocket = null;  
    public const int BufferSize = 100;  
    public byte[] buffer = new byte[BufferSize];  
    public List<byte> buffers = new List<byte>();  
    //是不是和服务器的链接  
    public bool IsServerCon = false;  
}  
/// <summary>  
/// 打洞节点客户端 实现的功能:  
/// 连接服务器获取对方节点ip   
/// 请求对方ip(打洞)  
/// 根据条件判断是监听连接还是监听等待连接  
/// </summary>  
public class PeerClient : IPeerClient  
{  
    //ManualResetEvent xxxxDone =  new ManualResetEvent(false);  
    //Semaphore   
    /// <summary>  
    /// 当前链接  
    /// </summary>  
    public Socket Client { get;private set; }  
    #region 服务端  
    public string ServerHostName { get;private set; }  
    public int ServerPort { get; private set; }  
    #endregion  
  
    #region 接收和通知事件  
    public delegate void EventMsg(object sender, string e);  
    //接收事件  
    public event EventMsg ReceivedMsg;  
    //通知消息  
    public event EventMsg NoticeMsg;  
    #endregion  
    //本地绑定的节点  
    private IPEndPoint LocalEP;  
    public PeerClient(string hostname, int port)    {  
        Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  
        this.ServerHostName = hostname;  
        this.ServerPort = port;  
    }  
  
    /// <summary>  
    /// 初始化客户端(包括启动)  
    /// </summary>  
    public void Init()    {  
        try  
        {  
            Client.Connect(ServerHostName, ServerPort);  
        }  
        catch (SocketException ex)  
        {  
            NoticeMsg?.Invoke(Client, $"连接服务器失败!{ex}!\r\n");  
            throw;  
        }  
        catch (Exception ex)  
        {  
            NoticeMsg?.Invoke(Client, $"连接服务器失败!{ex}!\r\n");  
            throw;  
        }  
        NoticeMsg?.Invoke(Client, $"连接上服务器了!\r\n");  
        var _localEndPoint = Client.LocalEndPoint.ToString();  
        LocalEP = new IPEndPoint(IPAddress.Parse(_localEndPoint.Split(':')[0])  
            , int.Parse(_localEndPoint.Split(':')[1]));  
        Receive(Client);  
    }  
    private void Receive(Socket client)    {  
        try  
        {  
            StateObject state = new StateObject();  
            state.workSocket = client;  
            state.IsServerCon = true;  
            client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,  
                new AsyncCallback(ReceiveCallback), state);  
        }  
        catch (Exception e)  
        {  
            NoticeMsg?.Invoke(Client, $"接收消息出错了{e}!\r\n");  
        }  
    }  
    private void ReceiveCallback(IAsyncResult ar)    {  
        try  
        {  
            var state = (StateObject)ar.AsyncState;  
            Socket _client = state.workSocket;  
            //因为到这边的经常Connected 还是true  
            //if (!_client.Connected)  
            //{  
            //    _client.Close();  
            //    return;  
            //}  
            SocketError error = SocketError.Success;  
            int bytesRead = _client.EndReceive(ar,out error);  
            if (error == SocketError.ConnectionReset)  
            {  
                NoticeMsg?.Invoke(Client, $"链接已经释放!\r\n");  
                _client.Close();  
                _client.Dispose();  
                return;  
            }  
            if (SocketError.Success!= error)  
            {  
               throw new SocketException((int)error);  
            }  
            var arr = state.buffer.AsQueryable().Take(bytesRead).ToArray();  
            state.buffers.AddRange(arr);  
            if (bytesRead >= state.buffer.Length)  
            {  
                _client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,  
                  new AsyncCallback(ReceiveCallback), state);  
                state.buffers.CopyTo(state.buffers.Count, state.buffer, 0, bytesRead);  
                //_client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,  
                //    new AsyncCallback(ReceiveCallback), state);  
            }  
            else  
            {  
                var _msg = Encoding.UTF8.GetString(state.buffers.ToArray());  
                ReceivedMsg?.Invoke(_client, _msg);  
                if (state.IsServerCon)  
                {  
                    _client.Shutdown(SocketShutdown.Both);  
                    _client.Close();  
                    int retryCon = _msg.Contains("_1") ? 1 : 100;  
                    _msg = _msg.Replace("_1", "").Replace("_2", "");  
                    TryConnection(_msg.Split(':')[0], int.Parse(_msg.Split(':')[1]), retryCon);  
                    return;  
                }  
                state = new StateObject();  
                state.IsServerCon = false;  
                state.workSocket = _client;  
                _client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,  
                 new AsyncCallback(ReceiveCallback), state);  
            }  
        }               
        catch (SocketException ex)  
        {  
            //10054  
            NoticeMsg?.Invoke(Client, $"链接已经释放!{ex}!\r\n");  
        }  
        catch (Exception e)  
        {  
            NoticeMsg?.Invoke(Client, $"接收消息出错了2{e}!\r\n");  
        }  
    }  
    /// <summary>  
    /// 打洞或者尝试链接  
    /// </summary>  
    private void TryConnection(string remoteHostname, int remotePort,int retryCon)    {  
        Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  
        Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);  
        var _iPRemotePoint = new IPEndPoint(IPAddress.Parse(remoteHostname), remotePort);  
        Client.Bind(LocalEP);  
        System.Threading.Thread.Sleep(retryCon==1?1:3*1000);  
        for (int i = 0; i < retryCon; i++)  
        {  
            try  
            {  
                Client.Connect(_iPRemotePoint);  
                NoticeMsg?.Invoke(Client, $"已经连接上:{remoteHostname}:{remotePort}!\r\n");  
                StateObject state = new StateObject();  
                state.workSocket = Client;  
                state.IsServerCon = false;  
                Client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,  
                 new AsyncCallback(ReceiveCallback), state);  
                return;  
            }  
            catch  
            {  
                NoticeMsg?.Invoke(Client, $"尝试第{i+1}次链接:{remoteHostname}:{remotePort}!\r\n");  
            }  
        }  
        if (retryCon==1)  
        {  
            Listening(LocalEP.Port);  
            return;  
        }  
        NoticeMsg?.Invoke(Client, $"尝试了{retryCon}次都没有办法连接到:{remoteHostname}:{remotePort},凉了!\r\n");   }  
  
    /// <summary>  
    /// 如果连接不成功,因为事先有打洞过了,根据条件监听 等待对方连接来  
    /// </summary>  
    private void Listening(int Port)    {  
        try  
        {  
            Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  
            Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);  
            Client.Bind(new IPEndPoint(IPAddress.Any, Port));Client.Listen((int)SocketOptionName.MaxConnections);  
            NoticeMsg?.Invoke(Client, $"开始侦听断开等待链接过来!\r\n");  
            StateObject state = new StateObject();  
            state.IsServerCon = false;  
            var _socket = Client.Accept();//只有一个链接 不用BeginAccept  
            Client.Close();//关系现有侦听  
            Client = _socket;  
            state.workSocket = Client;  
            NoticeMsg?.Invoke(Client, $"接收到来自{Client.RemoteEndPoint}的连接!\r\n");  
            Client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,  
                new AsyncCallback(ReceiveCallback), state);  
        }  
        catch (Exception ex)  
        {  
            NoticeMsg?.Invoke(Client, $"监听出错了{ex}凉了!\r\n");  
        }  
        //scoket.send  
    }  
    /// <summary>  
    /// 本例子只存在一个成功的链接,对成功的连接发送消息!  
    /// </summary>  
    /// <param name="strMsg"></param>  
    public void Send(string strMsg)    {   
        byte[] bytes = Encoding.UTF8.GetBytes(strMsg);   
        Client.BeginSend(bytes, 0, bytes.Length, 0,  
            new AsyncCallback(SendCallback), Client);  
    }  
    private void SendCallback(IAsyncResult ar)    {  
        try  
        {   
            Socket _socket = (Socket)ar.AsyncState;  
            //if(ar.IsCompleted)  
            _socket.EndSend(ar);   
        }  
        catch (Exception e)  
        {  
            NoticeMsg?.Invoke(Client, $"发送消息出错了{e}!\r\n");  
        }  
    }  
}