TcpClient

发布时间 2023-07-06 16:11:02作者: cchong005
public class TcpClient
{
    public event Action<byte[]> OnReveive = delegate { };
    private ManualResetEvent sendDone = new ManualResetEvent(false);
    private ManualResetEvent receiveDone = new ManualResetEvent(false);
    /// <summary>
    /// 有可能涉及到返回数据一个 byte[] 装不下的情况,所以设计了一个 List 来拼数据
    /// </summary>
    List<byte[]> bufferList = new List<byte[]>();
    int bufferLength = 1024;
    Socket _ClientSocket;
    IPEndPoint _EndPoint;
    public TcpClient(string ipAddress, int port)
    {
        _ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPAddress ip = IPAddress.Parse(ipAddress);
        _EndPoint = new IPEndPoint(ip, port);
    }
    public bool Connect()
    {
        _ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _ClientSocket.Connect(_EndPoint);
        return _ClientSocket.Connected;
    }
    public bool Close()
    {
        // 关闭Socket连接
        _ClientSocket.Shutdown(SocketShutdown.Both);
        _ClientSocket.Close();
        return !_ClientSocket.Connected;
    }
    public TcpClient(byte[] ipAddress, int port)
    {
        IPAddress ip = new IPAddress(ipAddress);
        _EndPoint = new IPEndPoint(ip, port);
    }

    /// <summary>
    /// 发送信息
    /// </summary>
    /// <remarks>
    /// 无法在send时,同步接收数据返回, 调用 Receive 并不会马上向 buff 中填数据, 
    /// 调用 receive 后马上返回 buffer,数据是不正确的<br/>
    /// 虽然用了异步的发送和接收,但是由于加锁的缘故,这是个同步方法<br/>
    /// 不枷锁的receive,数据可能会乱序
    /// </remarks>
    /// <param name="data"></param>
    public void Send(byte[] data)
    {
        if (!_ClientSocket.Connected)
            Connect();

        _ClientSocket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(SendCallback), _ClientSocket);
        sendDone.WaitOne();

        // 接收数据
        byte[] receiveBuffer = new byte[bufferLength];
        bufferList.Add(receiveBuffer);
        _ClientSocket.BeginReceive(bufferList.Last(), 0, bufferList.Last().Length, 0, new AsyncCallback(ReceiveCallback), _ClientSocket);
        receiveDone.WaitOne();
    }
    /// <summary>
    /// 发送的回调
    /// </summary>
    /// <remarks>
    /// 这里可以用来处理发送信息,发送失败可以在这里重新发送
    /// </remarks>
    /// <param name="ar"></param>
    private void SendCallback(IAsyncResult ar)
    {
        try
        {
            Socket clientSocket = (Socket)ar.AsyncState;
            int bytesSent = clientSocket.EndSend(ar);
            sendDone.Set();
        }
        catch (Exception ex)
        {
            throw;
        }
    }

    /// <summary>
    /// receive的回调
    /// </summary>
    /// <param name="ar"></param>
    private void ReceiveCallback(IAsyncResult ar)
    {
        try
        {
            Socket? clientSocket =ar.AsyncState as Socket;
            int bytesReceiveCount = clientSocket?.EndReceive(ar) ?? 0;

            if (bytesReceiveCount < bufferLength)
            {
                // bytesReceiveCount 小于 buffer 的长度,说明数据已经完整获取
                // 把最后一个buffer 按 bytesReceiveCount 裁剪一下,丢弃多余的部分
                // 再把 bufferList 里所有的 buffer 拼接一下,通过回调函数返回,然后放开接收锁

                bufferList[bufferList.Count - 1] = bufferList.Last().Take(bytesReceiveCount).ToArray();
                OnReveive?.Invoke(bufferList.SelectMany(b => b).ToArray());
                bufferList.Clear();
                receiveDone.Set();
            }
            else
            {
                // bytesReceiveCount 等于 buffer 长度,有可能有数据未获取
                // 再重新receive 一遍,获取后面的 buffer
                byte[] newBuffer = new byte[bufferLength];
                bufferList.Add(newBuffer);
                clientSocket?.BeginReceive(bufferList.Last(), 0, bufferList.Last().Length, 0, new AsyncCallback(ReceiveCallback), clientSocket);
            }
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}