FCL 系列 - 4. FCL.Net.dll

 

使用.NET 的 Socket 对象,Select 模型。

自定义封包: 包长度+消息体

客户端-服务端架构,有心跳包机制。

 

 

客户端源代码:

using Wagwei.FCL.Net.Implementation;

namespace Wagwei.FCL.Net.Socket
{
    /// <summary> Socket 客户端类<remarks>
    /// <para></para>
    /// </remarks>
    /// </summary>
    [DeveloperInfo("Wagwei")]
    public class ClientOp
    {
        /* ###### 客户端基础信息 ###### */
        public string m_sServerIP { get; set; }
        public int m_nPort { get; set; }
        public string m_sUserName { get; set; }
        public string m_sUserPwd { get; set; }


        /* ###### Socket客户端 ###### */
        public System.Net.Sockets.Socket m_socketClient;


        /* ###### 接受消息 ###### */
        Thread m_threadRecv;//线程
        ManualResetEvent m_mreReceive = new ManualResetEvent(false);
        ManualResetEvent m_mreReceiveDone1 = new ManualResetEvent(false);
        ManualResetEvent m_mreReceiveDone2 = new ManualResetEvent(false);
        public int m_nReceivePollingInterval = 100;//接受消息的轮询间隔


        /* ###### 自动连接 ###### */
        Thread m_threadAutoConnect;//线程
        ManualResetEvent m_mreAutoConnect = new ManualResetEvent(false);
        public int m_nAutoConnectPollingInterval = 5000;//断线重连的轮询间隔
        int m_nKeepAliveSeconds = 20;//30;//心跳允许时间
        DateTime m_datatimeKeepAlive = DateTime.Now; //心跳时间

        /* ###### 消息事件 ###### */
        public delegate void DelegateHaveText(Text text);
        public event DelegateHaveText m_eventHaveText;

        Queue<Text> _queueHaveText;
        object _objLockQueueHaveText;

        Thread m_threadHaveText;
        ManualResetEvent m_mreHaveText = new ManualResetEvent(false);

        /* ###### 提示信息事件 ###### */
        public delegate void DelegateInfo(string info);
        public event DelegateInfo m_eventInfo;


        static ClientOp()
        {
            //Wagwei.FCL.Core.Implementation.Internal.WagLicense.Check();
        }

        public ClientOp()
        {
            _queueHaveText = new Queue<Text>();
            _objLockQueueHaveText = new object();
        }

        public ClientOp(string serverIP, int port, string userName, string userPwd)
            : this()
        {
            m_sServerIP = serverIP;
            m_nPort = port;
            m_sUserName = userName;
            m_sUserPwd = userPwd;
        }

        /// <summary>
        /// 身份验证
        /// </summary>
        /// <param name="args">ServerIp + UserName + UserPwd + Port</param>
        /// <returns></returns>
        public void Logon(params string[] args).../// <summary>
        /// 自动连接
        /// </summary>
        public void EnabledAutoConnect(bool flg).../// <summary>
        /// 启动或停止接受消息
        /// </summary>
        public void EnabledReceive(bool flag).../// <summary>
        /// 启动或停止接受消息通知
        /// </summary>
        /// <param name="flag"></param>
        public void EnabledHaveText(bool flag)...void HaveText().../// <summary>
        /// 异步发送消息, 发送速度不可太快
        /// </summary>
        public void SendTextAsync(Text text)...

/// <summary> /// 同步发送消息, 发送速度不可太快 /// <remarks> /// 返回值: -1 未连接, 0 暂时无法发送, 1 发送成功, 2 发送失败 /// </remarks> /// </summary> public int SendText(Text text) { if (this.m_socketClient != null) { if (this.m_socketClient.Connected) { if (this.m_socketClient.Poll(1000, SelectMode.SelectWrite)) { try { m_socketClient.Send(BinarySerializer.Serialize( text, SocketCommon.m_PacketLenSize, SocketCommon.m_PacketLenSize)); //返回值, 已发送到 Socket 的字节数。 } catch (Exception ex) { string sErr = "SendText: " + ex.Message; this.SetEventInfo(sErr); SocketCommon.AppendLog("exceptions.log", sErr); return 2; } return 1; } } return 0; } return -1; } void SendCallback(IAsyncResult ar) { try { System.Net.Sockets.Socket client = (System.Net.Sockets.Socket)ar.AsyncState; client.EndSend(ar); } catch (Exception ex) { throw new Exception(ex.Message); } } void ReceiveText() { while (true) { this.m_mreReceive.WaitOne(); if (this.m_socketClient != null) { try { if (m_socketClient.Connected) { if (m_socketClient.Poll(1000, SelectMode.SelectRead)) { //注: Receive时返回的字节数,不一定等于要求读取的字节数。 //系统只是在数据包到达时,尽可能的读取要求的字节数 RecvState state = new RecvState(); state.m_socketWork = m_socketClient; m_mreReceiveDone1.Reset(); m_mreReceiveDone2.Reset(); m_socketClient.BeginReceive(state.m_arraySize, 0, SocketCommon.m_PacketLenSize, SocketFlags.None, new AsyncCallback(ReceiveCallBackSize), state); m_mreReceiveDone1.WaitOne(); state.m_nLenAllData = BitConverter.ToInt32(state.m_arraySize, 0); m_socketClient.BeginReceive(state.m_arrayData, 0, state.m_nLenAllData, SocketFlags.None, new AsyncCallback(ReceiveCallbackData), state); m_mreReceiveDone2.WaitOne(); /* @@@@@@ 有新消息 @@@@@@ */ byte[] arrayRealData = new byte[state.m_nLenAllData]; for (int i = 0; i < state.m_nLenAllData; i++) { arrayRealData[i] = state.m_arrayData[i]; } Text text = (Text)BinarySerializer.Deserialize(arrayRealData); if (text.m_enumCmdType.Equals(CMD_SERVER.TEXT)) { //-------------------------------------------------------- /*被NEW替换*/ /* //OLD if (this.m_eventHaveText != null) { this.m_eventHaveText(text);//事件的阻塞会导致下次心跳时间的迟来 } */ //NEW if (this.m_eventHaveText != null) { lock (_objLockQueueHaveText) { _queueHaveText.Enqueue(text); } } //-------------------------------------------------------- } else if (text.m_enumCmdType.Equals(CMD_SERVER.KEEP_ALIVE)) { this.m_datatimeKeepAlive = DateTime.Now; //text.m_datetimeSend;如果服务器时间与本机时间不一致则有问题 } /* @@@@@@@@@@@@@@@@@@@@@@ */ } } else { //不可设定异常 //bug //throw new Exception("连接已断开"); // System.Threading.Thread.Sleep(this.m_nReceivePollingInterval); continue; } } catch (Exception ex) { string sErr = "ReceiveText: " + ex.Message; this.SetEventInfo(sErr); SocketCommon.AppendLog("receive.log", sErr); //break;//bug 不可退出循环 continue; } } else { System.Threading.Thread.Sleep(this.m_nReceivePollingInterval); continue; } } } private void ReceiveCallBackSize(IAsyncResult ar) { RecvState state = (RecvState)ar.AsyncState; System.Net.Sockets.Socket client = state.m_socketWork; int bytesRead = client.EndReceive(ar); if (bytesRead > 0) { state.m_nLenSize += bytesRead; if (state.m_nLenSize == SocketCommon.m_PacketLenSize) { m_mreReceiveDone1.Set(); state.m_nLenAllData = BitConverter.ToInt32(state.m_arraySize, 0); } else { m_socketClient.BeginReceive(state.m_arraySize, state.m_nLenSize, SocketCommon.m_PacketLenSize - state.m_nLenSize, SocketFlags.None, new AsyncCallback(ReceiveCallbackData), state); } } else if (bytesRead == 0) { //m_mreReceiveDone1.Set(); string sErr = "ReceiveCallBackSize: the peer has performed an orderly shutdown"; //throw new Exception(sErr); this.SetEventInfo(sErr); SocketCommon.AppendLog("exceptions.log", sErr); } else { //m_mreReceiveDone1.Set(); string sErr = "ReceiveCallBackSize: an error occurred"; //throw new Exception(sErr); this.SetEventInfo(sErr); SocketCommon.AppendLog("exceptions.log", sErr); } } private void ReceiveCallbackData(IAsyncResult ar).../// <summary> /// 错误通知 /// </summary> private void SetEventInfo(string info).../// <summary> /// 关闭 /// </summary> public void Close() }

 

服务端源代码:

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Windows.Forms;

using Wagwei.FCL.Net.Implementation;

namespace Wagwei.FCL.Net.Socket
{
    //特别说明:
    //(1) 当客户端连接过多, 消息转发量过大时候, 会导致传输的数据包严重延时, 这也导致了心跳异常, 此时禁用心跳则不会持续报错
    //(2) 


    //高并发指标: 1. 并发连接数 2. 每秒可创建多少连接 3. 其他

    /// <summary>Socket服务端类</summary>
    [DeveloperInfo("Wagwei")]
    public class ServerOp
    {
        public int m_nListeningInterval = 1200;//监听时间间隔, 建议大于500ms
        public Dictionary<string, System.Net.Sockets.Socket> m_dicSocket;

        Thread m_threadListener; //监听线程
        Thread m_threadReceiver; //接受(直接转发)线程
        Thread m_threadSend;     //间接转发线程

        ManualResetEvent m_mreListener = new ManualResetEvent(false);
        ManualResetEvent m_mreReceiver = new ManualResetEvent(false);
        ManualResetEvent m_mreSend = new ManualResetEvent(false);

        System.Net.Sockets.Socket m_socketListener; //监听Socket

        List<Text> m_listText;//未处理的信息

        //List<User> m_listUser;//在线所有客户端
        public delegate void DelegateClientAccount (string clientName,string type);
        public event DelegateClientAccount m_eventClientAccount;

        // ****** 信息(to server) ******
        public delegate void DelegateServerHaveText(Text text);
        public event DelegateServerHaveText m_eventServerHaveText;

        Queue<Text> m_queueHaveText;
        object m_objLockQueueHaveText;

        Thread m_threadHaveText;
        ManualResetEvent m_mreHaveText = new ManualResetEvent(false);

        // ****** 提示 ******
        public delegate void DelegateInfo(string info);
        public event DelegateInfo m_eventInfo;

        // ****** 验证 ******
        public delegate bool DelegateVerify();
        public event DelegateVerify m_eventVerify;

        static ServerOp()
        {
            //Wagwei.FCL.Core.Implementation.Internal.WagLicense.Check();
        }

        public ServerOp()
        {
            m_queueHaveText = new Queue<Text>();
            m_objLockQueueHaveText = new object();
        }

        /// <summary>初始化</summary>
        /// <param name="ip">IP地址</param>
        /// <param name="port">端口号</param>
        public void Init(string ip, int port)
        {
            IPEndPoint iPEndP = new IPEndPoint(IPAddress.Parse(ip), port);//port为0即为随机端口
            this.m_socketListener = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            this.m_socketListener.Bind(iPEndP);
            this.m_socketListener.Listen(120);//挂起连接数

            this.m_socketListener.ReceiveBufferSize = SocketCommon.m_ReceiveBufferSize;
            this.m_socketListener.SendBufferSize = SocketCommon.m_SendBufferSize;

            this.m_dicSocket = new Dictionary<string, System.Net.Sockets.Socket>(0);
            this.m_listText = new List<Text>();
            //this.m_listUser = new List<User>();

            //监听
            this.m_threadListener = new Thread(Listener);
            this.m_threadListener.Name = "thread_fcl_net_socket_listener";
            this.m_threadListener.Start();

            //接受消息
            this.m_threadReceiver = new Thread(ReceiveText);
            this.m_threadReceiver.Name = "thread_fcl_net_socket_receiver";
            this.m_threadReceiver.Start();

            //转发消息
            this.m_threadSend = new Thread(SwitchSendText);
            this.m_threadSend.Name = "thread_fcl_net_socket_send";
            this.m_threadSend.Start();
        }

        public void EnabledListener(bool enable)...public void EnabledReceiver(bool enabled)...public void EnabledSend(bool enabled)...#region to server

        /// <summary>启动或停止接受消息通知</summary>
        /// <param name="flag"></param>
        public void EnabledHaveText(bool flag)...void HaveText()
        {
            while (true)
            {
                this.m_mreHaveText.WaitOne();

                Text? text = null;
                lock (m_objLockQueueHaveText)
                {
                    if (m_queueHaveText.Count > 0)
                    {
                        text = m_queueHaveText.Dequeue();
                    }
                }
                if (text == null)
                {
                    Thread.Sleep(5);
                }
                else
                {
                    if (this.m_eventServerHaveText != null)
                    {
                        this.m_eventServerHaveText((Text)text);
                    }
                }
            }
        }

        #endregion

        /// <summary>监听连接</summary>
        void Listener()
        {
            //System.Net.Sockets.Socket currentSocket = null;
            while (true)
            {
                this.m_mreListener.WaitOne();

                try
                {
                    if (this.m_socketListener.Poll(1000, SelectMode.SelectRead))
                    {
                        System.Net.Sockets.Socket client = this.m_socketListener.Accept();//接受一个请求

                        //currentSocket = client;

                        client.SendBufferSize = SocketCommon.m_SendBufferSize;
                        client.ReceiveBufferSize = SocketCommon.m_ReceiveBufferSize;

                        if (!client.Poll(1000, SelectMode.SelectRead))
                        {
                            client.Disconnect(false);
                            client.Close();
                            goto END;
                        }

                        byte[] byteData = new byte[SocketCommon.m_PacketSize];
                        byte[] byteSize = new byte[SocketCommon.m_PacketLenSize];
                        int lenSize = client.Receive(byteSize, SocketCommon.m_PacketLenSize, SocketFlags.None);
                        int lenData = BitConverter.ToInt32(byteSize, 0);
                        client.Receive(byteData, lenData, SocketFlags.None);
                        byte[] arrayRealData = new byte[lenData];

                        for (int i = 0; i < lenData; i++)
                        {
                            arrayRealData[i] = byteData[i];
                        }

                        Text text = (Text)BinarySerializer.Deserialize(arrayRealData);

                        //身份验证
                        if (text.m_enumCmdType.Equals(CMD_SERVER.LOGON)
                            && text.m_sFrom != "server"
                            && (m_eventVerify == null || m_eventVerify()))
                        {
                        }
                        else
                        {
                            client.Disconnect(false);
                            client.Close();
                            goto END;
                        }

                        lock (this.m_dicSocket)
                        {
                            //重复登录
                            if (this.m_dicSocket.ContainsKey(text.m_sFrom))
                            {
                                if (this.m_dicSocket[text.m_sFrom].Poll(1000, SelectMode.SelectWrite))
                                {
                                    Text textToClient = new Text();
                                    textToClient.m_sFrom = "server";
                                    textToClient.m_asTo = new string[] { text.m_sFrom };
                                    textToClient.m_enumCmdType = CMD_LOGON_RESULT.FORCED_OFFLINE;
                                    textToClient.m_objContent = "被迫下线";
                                    this.m_dicSocket[text.m_sFrom].Send(BinarySerializer.Serialize(textToClient,
                                        SocketCommon.m_PacketLenSize,
                                        SocketCommon.m_PacketLenSize));
                                }
                                this.m_dicSocket[text.m_sFrom].Disconnect(false);
                                this.m_dicSocket[text.m_sFrom].Close();
                                this.m_dicSocket.Remove(text.m_sFrom);
                            }

                            //非重复登录
                            this.m_dicSocket.Add(text.m_sFrom, client);

                            if (m_eventClientAccount != null)
                            {
                                m_eventClientAccount(text.m_sFrom, "logon");
                            }

                            //立即发送心跳给客户端
                            //... 或者客户端主动发送心跳
                        }

                        continue;
                    }
                    else
                    {
                        System.Threading.Thread.Sleep(m_nListeningInterval);
                        continue;
                    }
                }
                catch (Exception ex)
                {
                    string s = ex.ToString();
                    this.SetEventOnRunError(ex.Message);
                    SocketCommon.AppendLog("listener.log", ex.ToString());
                    goto END;
                }
            END:
                System.Threading.Thread.Sleep(m_nListeningInterval);
            }
        }

        /// <summary>接受消息</summary>
        void ReceiveText()
        {
            while (true)
            {
                this.m_mreReceiver.WaitOne();

                System.Net.Sockets.Socket socketCurrent = null;

                try
                {
                    List<System.Net.Sockets.Socket> tmp = new List<System.Net.Sockets.Socket>();

                    lock (this.m_dicSocket)
                    {
                        foreach (System.Net.Sockets.Socket s in this.m_dicSocket.Values)
                        {
                            tmp.Add(s);
                        }
                    }

                    if (tmp.Count > 0)
                    {
                        System.Net.Sockets.Socket.Select(tmp, null, null, 1000);//select

                        foreach (System.Net.Sockets.Socket socketOne in tmp)
                        {
                            socketCurrent = socketOne;

                            byte[] bytesData = new byte[SocketCommon.m_PacketSize];
                            byte[] bytesSize = new byte[SocketCommon.m_PacketLenSize];
                            int DataSize;
                            int recvSizeRtn = 0;
                            recvSizeRtn += socketOne.Receive(bytesSize, SocketCommon.m_PacketLenSize, SocketFlags.None);

                            if (recvSizeRtn > 0)
                            {
                                while (recvSizeRtn != SocketCommon.m_PacketLenSize)
                                {
                                    recvSizeRtn += socketOne.Receive(bytesSize,
                                        recvSizeRtn,
                                        SocketCommon.m_PacketLenSize - recvSizeRtn,
                                        SocketFlags.None);
                                }
                                DataSize = BitConverter.ToInt32(bytesSize, 0);
                            }
                            else if (recvSizeRtn == 0)
                            {
                                throw new Exception("the peer has performed an orderly shutdown");
                            }
                            else
                            {
                                throw new Exception("an error occurred");
                            }

                            int recvDataRtn = 0;
                            recvDataRtn += socketOne.Receive(bytesData, DataSize, SocketFlags.None);

                            if (recvDataRtn > 0)
                            {
                                while (recvDataRtn != DataSize)
                                {
                                    recvDataRtn += socketOne.Receive(bytesData,
                                        recvDataRtn,
                                        DataSize - recvDataRtn,
                                        SocketFlags.None);
                                }

                                byte[] aRealData = new byte[DataSize];

                                for (int i = 0; i < DataSize; i++)
                                {
                                    aRealData[i] = bytesData[i];
                                }

                                Text text = (Text)BinarySerializer.Deserialize(aRealData);

                                //当即转发, 否则保存到listText中
                                List<System.Net.Sockets.Socket> listSocket = new List<System.Net.Sockets.Socket>();
                                int count = text.m_asTo.Length;

                                lock (this.m_dicSocket)
                                {
                                    for (int i = 0; i < count; i++)
                                    {
                                        if (this.m_dicSocket.ContainsKey(text.m_asTo[i]))
                                        {
                                            listSocket.Add(this.m_dicSocket[text.m_asTo[i]]);
                                        }
                                        else
                                        {
                                            //如果接收方为服务器本身则另做处理
                                            if (text.m_asTo[i] == "server")
                                            {
                                                if (this.m_eventServerHaveText != null)
                                                {
                                                    //this.m_eventServerHaveText(text);

                                                    lock (m_objLockQueueHaveText)
                                                    {
                                                        m_queueHaveText.Enqueue(text);
                                                    }
                                                }//end if

                                                if (text.m_enumCmdType.Equals(CMD_SERVER.TEXT))
                                                {

                                                }//end if
                                                else if (text.m_enumCmdType.Equals(CMD_SERVER.KEEP_ALIVE))
                                                {
                                                    //if (text.m_sContentRemark == "wag")
                                                    //{
                                                    //    MessageBox.Show("wag");
                                                    //}

                                                    Text textKeepAlive = new Text();
                                                    textKeepAlive.m_sFrom = "server";
                                                    textKeepAlive.m_asTo = new string[] { text.m_sFrom };
                                                    textKeepAlive.m_enumCmdType = CMD_SERVER.KEEP_ALIVE;
                                                    textKeepAlive.m_datetimeSend = DateTime.Now;

                                                    socketOne.Send(BinarySerializer.Serialize(textKeepAlive,
                                                        SocketCommon.m_PacketLenSize,
                                                        SocketCommon.m_PacketLenSize));
                                                }//end else if
                                            }//end if
                                            else
                                            {
                                            }//end else

                                        }//end else
                                    }//end for
                                }//end lock

                                for (int i = 0; i < listSocket.Count; i++)
                                {
                                    Text _text = new Text();

                                    if (listSocket[i].Poll(1000, SelectMode.SelectWrite))
                                    {
                                        _text.m_sFrom = text.m_sFrom;
                                        _text.m_sFromIp = text.m_sFromIp;
                                        _text.m_asTo = new string[] { text.m_asTo[i] };
                                        _text.m_datetimeSend = text.m_datetimeSend;
                                        _text.m_typeContent = text.m_typeContent;
                                        _text.m_objContent = text.m_objContent;
                                        _text.m_sContentRemark = text.m_sContentRemark;
                                        _text.m_sUserPwd = text.m_sUserPwd;
                                        _text.m_enumCmdType = text.m_enumCmdType;

                                        listSocket[i].Send(BinarySerializer.Serialize(_text,
                                            SocketCommon.m_PacketLenSize,
                                            SocketCommon.m_PacketLenSize));
                                        continue;
                                    }//end if
                                }//end for

                            }
                            else if (recvDataRtn == 0)
                            {
                                throw new Exception("the peer has performed an orderly shutdown");
                            }
                            else
                            {
                                throw new Exception("an error occurred");
                            }

                        }//end foreach
                    }
                    else
                    {
                        System.Threading.Thread.Sleep(5);
                        continue;
                    }
                }
                catch (Exception ex)
                {
                    string s = ex.ToString();
                    this.SetEventOnRunError("ReceiveText: " + ex.ToString());
                    this.RemoveAbnormal(socketCurrent);
                    SocketCommon.AppendLog("receive.log", ex.ToString());

                    continue;
                }
            }
        }

        public void AppendOneText(Text text).../// <summary>转发消息</summary>
        void SwitchSendText().../// <summary>
        /// 移除不正常的socket对象, 并且将对应的用户移除
        /// </summary>
        private void RemoveAbnormal(System.Net.Sockets.Socket currentSocket).../// <summary>
        /// 错误通知
        /// </summary>
        private void SetEventOnRunError(string error).../// <summary>
        /// 关闭
        /// </summary>
        public void Close()...


    }
}

 

 

 

 

 

 

FCL 系列 - 4. FCL.Net.dll_第1张图片

 

你可能感兴趣的:(FCL 系列 - 4. FCL.Net.dll)