SocketServer.cs 6.6 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 15
    /// <summary>
    /// 异步单线程
    /// </summary>
    public class SocketServer
    {
        //private static SocketServer _mSocketServer;
16

JasonWcx's avatar
JasonWcx 已提交
17 18
        protected int _iport = 80;

JasonWcx's avatar
JasonWcx 已提交
19
        protected int _maxListenCount = 120;
JasonWcx's avatar
JasonWcx 已提交
20
        protected readonly ConcurrentDictionary<string, Socket> _socketDocker;
JasonWcx's avatar
JasonWcx 已提交
21
        protected Socket _sc;
JasonWcx's avatar
JasonWcx 已提交
22

JasonWcx's avatar
JasonWcx 已提交
23 24 25 26 27 28 29 30
        /// <summary>
        /// 服务器启动事件
        /// </summary>
        public event ServerStart OnServerStart;
        /// <summary>
        /// 客户端连接事件
        /// </summary>
        public event ClientConnect OnClientConnect;
31 32 33
        /// <summary>
        /// 客户端断开连接时间
        /// </summary>
JasonWcx's avatar
JasonWcx 已提交
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
        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 已提交
51
        public int Port
JasonWcx's avatar
JasonWcx 已提交
52
        {
JasonWcx's avatar
JasonWcx 已提交
53
            get { return _iport; }
JasonWcx's avatar
JasonWcx 已提交
54 55 56
        }
        public Socket SocketMain
        {
57
            get { return _sc; }
JasonWcx's avatar
JasonWcx 已提交
58 59
        }

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

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

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

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


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

                if (iByteRead > 0)
167 168 169
                {
                    //置空数据连接
                    so.ResetBuffer(iByteRead);
JasonWcx's avatar
JasonWcx 已提交
170 171
                    if (client.Available > 0)
                    {
172
                        //Thread.Sleep(10);
JasonWcx's avatar
重构  
JasonWcx 已提交
173
                        client.BeginReceive(so.Buffer, 0, so.Buffer.Length, SocketFlags.None, CallbackReceived, so);
JasonWcx's avatar
JasonWcx 已提交
174 175 176
                    }
                    else
                    {
JasonWcx's avatar
JasonWcx 已提交
177
                        InvokeAfterReceiveEnd(so, client);
JasonWcx's avatar
JasonWcx 已提交
178 179
                    }
                }
JasonWcx's avatar
JasonWcx 已提交
180
                else
181
                {
JasonWcx's avatar
JasonWcx 已提交
182
                    InvokeAfterReceiveEnd(so, client);
JasonWcx's avatar
JasonWcx 已提交
183
                }
184
            }
JasonWcx's avatar
JasonWcx 已提交
185
            else
186
            {
JasonWcx's avatar
JasonWcx 已提交
187 188 189
                InvokeAfterReceiveEnd(so, client);
            }
        }
JasonWcx's avatar
JasonWcx 已提交
190 191
        private void InvokeAfterReceiveEnd(StateObject so, Socket client)
        {
JasonWcx's avatar
JasonWcx 已提交
192 193 194
            RemoveClientSocket(so);
            if (AfterReceiveEnd != null)
            {
JasonWcx's avatar
JasonWcx 已提交
195
                AfterReceiveEnd(this,
JasonWcx's avatar
JasonWcx 已提交
196 197 198 199 200
                    new DataTransferArgs()
                    {
                        Data = so.Data.ToArray(),
                        IP = so.IP,
                        Port = so.RemotePort,
JasonWcx's avatar
JasonWcx 已提交
201
                        Socket = so.WorkSocket,
JasonWcx's avatar
JasonWcx 已提交
202 203
                        Client = client,
                        State = so
JasonWcx's avatar
JasonWcx 已提交
204
                    });
205
            }
JasonWcx's avatar
JasonWcx 已提交
206
        }
JasonWcx's avatar
JasonWcx 已提交
207 208 209 210 211 212
        //TODO 此处开启Socket状态监听,对断开的链接进行关闭销毁
        private void RemoveClientSocket(StateObject so)
        {
            Socket client;
            _socketDocker.TryRemove(so.Id, out client);
        }
JasonWcx's avatar
JasonWcx 已提交
213 214
    }
}