SocketServer.cs 6.9 KB
Newer Older
JasonWcx's avatar
JasonWcx 已提交
1
using System;
JasonWcx's avatar
JasonWcx 已提交
2
using System.Collections.Concurrent;
JasonWcx's avatar
JasonWcx 已提交
3 4 5 6
using System.Net.Sockets;

namespace Mozi.HttpEmbedded
{
JasonWcx's avatar
JasonWcx 已提交
7
    //TODO 加入定时器并利用POLL判断远端是否断开
JasonWcx's avatar
JasonWcx 已提交
8
    //TODO 实现链接复用
JasonWcx's avatar
JasonWcx 已提交
9
    //TODO 解决接收文件内存占用过大,无法及时释放的问题
JasonWcx's avatar
JasonWcx 已提交
10 11 12 13 14
    /// <summary>
    /// 异步单线程
    /// </summary>
    public class SocketServer
    {
JasonWcx's avatar
JasonWcx 已提交
15 16
        protected int _iport = 80;

JasonWcx's avatar
JasonWcx 已提交
17
        protected int _maxListenCount = 65535;
JasonWcx's avatar
JasonWcx 已提交
18
        protected readonly ConcurrentDictionary<string, Socket> _socketDocker;
JasonWcx's avatar
JasonWcx 已提交
19
        protected Socket _sc;
JasonWcx's avatar
JasonWcx 已提交
20

JasonWcx's avatar
JasonWcx 已提交
21 22 23 24 25 26 27 28
        /// <summary>
        /// 服务器启动事件
        /// </summary>
        public event ServerStart OnServerStart;
        /// <summary>
        /// 客户端连接事件
        /// </summary>
        public event ClientConnect OnClientConnect;
29 30 31
        /// <summary>
        /// 客户端断开连接时间
        /// </summary>
JasonWcx's avatar
JasonWcx 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
        public event ClientDisConnect AfterClientDisConnect;
        /// <summary>
        /// 数据接收开始事件
        /// </summary>
        public event ReceiveStart OnReceiveStart;
        /// <summary>
        /// 数据接收完成事件
        /// </summary>
        public event ReceiveEnd AfterReceiveEnd;
        /// <summary>
        /// 服务器停用事件
        /// </summary>
        public event AfterServerStop AfterServerStop;

        /// <summary>
        /// 端口
        /// </summary>
JasonWcx's avatar
JasonWcx 已提交
49
        public int Port
JasonWcx's avatar
JasonWcx 已提交
50
        {
JasonWcx's avatar
JasonWcx 已提交
51
            get { return _iport; }
JasonWcx's avatar
JasonWcx 已提交
52 53 54
        }
        public Socket SocketMain
        {
55
            get { return _sc; }
JasonWcx's avatar
JasonWcx 已提交
56 57
        }

JasonWcx's avatar
JasonWcx 已提交
58
        public SocketServer()
JasonWcx's avatar
JasonWcx 已提交
59
        {
JasonWcx's avatar
JasonWcx 已提交
60
            _socketDocker = new ConcurrentDictionary<string, Socket>();
JasonWcx's avatar
JasonWcx 已提交
61 62 63 64 65 66 67 68 69
        }

        //TODO 测试此处是否有BUG
        /// <summary>
        /// 启动服务器
        /// </summary>
        /// <param name="port"></param>
        public void StartServer(int port)
        {
JasonWcx's avatar
JasonWcx 已提交
70
            _iport = port;
71
            if (_sc == null)
JasonWcx's avatar
JasonWcx 已提交
72
            {
73
                _sc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
JasonWcx's avatar
JasonWcx 已提交
74 75 76
            }
            else
            {
77
                _sc.Close();
JasonWcx's avatar
JasonWcx 已提交
78
            }
JasonWcx's avatar
JasonWcx 已提交
79
            System.Net.IPEndPoint endpoint = new System.Net.IPEndPoint(System.Net.IPAddress.Any, _iport);
JasonWcx's avatar
JasonWcx 已提交
80 81
            //允许端口复用
            _sc.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
82
            _sc.Bind(endpoint);
JasonWcx's avatar
JasonWcx 已提交
83
            _sc.Listen(_maxListenCount);
JasonWcx's avatar
JasonWcx 已提交
84
            //回调服务器启动事件
JasonWcx's avatar
JasonWcx 已提交
85
            if (OnServerStart != null)
JasonWcx's avatar
JasonWcx 已提交
86
            {
JasonWcx's avatar
JasonWcx 已提交
87
                OnServerStart(this, new ServerArgs() { StartTime = DateTime.Now, StopTime = DateTime.MinValue });
JasonWcx's avatar
JasonWcx 已提交
88
            }
89
            _sc.BeginAccept(new AsyncCallback(CallbackAccept), _sc);
JasonWcx's avatar
JasonWcx 已提交
90 91 92 93
        }
        /// <summary>
        /// 关闭服务器
        /// </summary>
JasonWcx's avatar
JasonWcx 已提交
94
        public void StopServer()
JasonWcx's avatar
JasonWcx 已提交
95 96 97 98
        {
            _socketDocker.Clear();
            try
            {
99
                _sc.Shutdown(SocketShutdown.Both);
JasonWcx's avatar
JasonWcx 已提交
100
                if (AfterServerStop != null)
JasonWcx's avatar
JasonWcx 已提交
101
                {
102
                    AfterServerStop(_sc, null);
JasonWcx's avatar
JasonWcx 已提交
103 104
                }
            }
JasonWcx's avatar
JasonWcx 已提交
105 106 107
            catch
            {

JasonWcx's avatar
JasonWcx 已提交
108 109 110 111 112 113
            }
        }
        /// <summary>
        /// 开始连接回调
        /// </summary>
        /// <param name="iar"></param>
JasonWcx's avatar
JasonWcx 已提交
114
        protected void CallbackAccept(IAsyncResult iar)
JasonWcx's avatar
JasonWcx 已提交
115
        {
JasonWcx's avatar
JasonWcx 已提交
116
            Socket server = (Socket)iar.AsyncState; 
JasonWcx's avatar
JasonWcx 已提交
117
            //接受新连接传入
JasonWcx's avatar
JasonWcx 已提交
118
            server.BeginAccept(CallbackAccept, server);
JasonWcx's avatar
JasonWcx 已提交
119

JasonWcx's avatar
JasonWcx 已提交
120 121 122
            Socket client = server.EndAccept(iar);


JasonWcx's avatar
JasonWcx 已提交
123
            if (OnClientConnect != null)
JasonWcx's avatar
JasonWcx 已提交
124
            {
JasonWcx's avatar
JasonWcx 已提交
125
                //TODO .NetCore不再支持异步委托,需要重新实现
JasonWcx's avatar
JasonWcx 已提交
126
                OnClientConnect(this, new ClientConnectArgs()
JasonWcx's avatar
JasonWcx 已提交
127 128
                {
                    Client = client
JasonWcx's avatar
JasonWcx 已提交
129
                });
JasonWcx's avatar
JasonWcx 已提交
130
            }
JasonWcx's avatar
JasonWcx 已提交
131 132
            StateObject so = new StateObject()
            {
JasonWcx's avatar
JasonWcx 已提交
133 134 135
                WorkSocket = client,
                Id = Guid.NewGuid().ToString(),
                IP = ((System.Net.IPEndPoint)client.RemoteEndPoint).Address.ToString(),
JasonWcx's avatar
JasonWcx 已提交
136
                ConnectTime = DateTime.Now,
JasonWcx's avatar
JasonWcx 已提交
137 138
                RemotePort = ((System.Net.IPEndPoint)client.RemoteEndPoint).Port,
            };
JasonWcx's avatar
JasonWcx 已提交
139
            _socketDocker.TryAdd(so.Id, client);
JasonWcx's avatar
JasonWcx 已提交
140 141
            try
            {
JasonWcx's avatar
重构  
JasonWcx 已提交
142
                client.BeginReceive(so.Buffer, 0, so.Buffer.Length, SocketFlags.None, CallbackReceived, so);
JasonWcx's avatar
JasonWcx 已提交
143 144
                if (OnReceiveStart != null)
                {
JasonWcx's avatar
JasonWcx 已提交
145
                    OnReceiveStart.BeginInvoke(this, new DataTransferArgs(), null, null);
JasonWcx's avatar
JasonWcx 已提交
146 147
                }
            }
JasonWcx's avatar
JasonWcx 已提交
148
            catch (Exception ex)
JasonWcx's avatar
JasonWcx 已提交
149 150 151 152 153 154 155 156
            {
                var ex2 = ex;
            }
        }
        /// <summary>
        /// 接收数据回调
        /// </summary>
        /// <param name="iar"></param>
JasonWcx's avatar
重构  
JasonWcx 已提交
157
        internal void CallbackReceived(IAsyncResult iar)
JasonWcx's avatar
JasonWcx 已提交
158 159 160
        {
            StateObject so = (StateObject)iar.AsyncState;
            Socket client = so.WorkSocket;
161 162
            if (client.Connected)
            {
JasonWcx's avatar
JasonWcx 已提交
163
                try
164
                {
JasonWcx's avatar
JasonWcx 已提交
165 166 167
                    int iByteRead = client.EndReceive(iar);

                    if (iByteRead > 0)
JasonWcx's avatar
JasonWcx 已提交
168
                    {
JasonWcx's avatar
JasonWcx 已提交
169 170 171 172 173 174 175 176 177 178 179
                        //置空数据缓冲区
                        so.ResetBuffer(iByteRead);
                        if (client.Available > 0)
                        {
                            //Thread.Sleep(10);
                            client.BeginReceive(so.Buffer, 0, so.Buffer.Length, SocketFlags.None, CallbackReceived, so);
                        }
                        else
                        {
                            InvokeAfterReceiveEnd(so, client);
                        }
JasonWcx's avatar
JasonWcx 已提交
180 181 182
                    }
                    else
                    {
JasonWcx's avatar
JasonWcx 已提交
183
                        InvokeAfterReceiveEnd(so, client);
JasonWcx's avatar
JasonWcx 已提交
184 185
                    }
                }
JasonWcx's avatar
JasonWcx 已提交
186
                finally
187
                {
JasonWcx's avatar
JasonWcx 已提交
188

JasonWcx's avatar
JasonWcx 已提交
189
                }
190
            }
JasonWcx's avatar
JasonWcx 已提交
191
            else
192
            {
JasonWcx's avatar
JasonWcx 已提交
193 194 195
                InvokeAfterReceiveEnd(so, client);
            }
        }
JasonWcx's avatar
JasonWcx 已提交
196 197
        private void InvokeAfterReceiveEnd(StateObject so, Socket client)
        {
JasonWcx's avatar
JasonWcx 已提交
198
            try
JasonWcx's avatar
JasonWcx 已提交
199
            {
JasonWcx's avatar
JasonWcx 已提交
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
                RemoveClientSocket(so);
                if (AfterReceiveEnd != null)
                {
                    AfterReceiveEnd(this,
                        new DataTransferArgs()
                        {
                            Data = so.Data.ToArray(),
                            IP = so.IP,
                            Port = so.RemotePort,
                            Socket = so.WorkSocket,
                            Client = client,
                            State = so
                        });
                }
            }finally{

216
            }
JasonWcx's avatar
JasonWcx 已提交
217
        }
JasonWcx's avatar
JasonWcx 已提交
218 219 220 221 222 223
        //TODO 此处开启Socket状态监听,对断开的链接进行关闭销毁
        private void RemoveClientSocket(StateObject so)
        {
            Socket client;
            _socketDocker.TryRemove(so.Id, out client);
        }
JasonWcx's avatar
JasonWcx 已提交
224 225
    }
}