基于SocketAsyncEventArgs的版本


分享一下,项目中写的简单socket程序,不同方式的版本,这是个异步基于IOCP实现高性能的处理方式。IOCP就不多说了,高性能的完成端口,可以实现套接字对象的复用,降低开销,且基于端口共享性能据说很高,因此把之前的项目做个改动。下面来个代码走读:

功能介绍:

    此socket程序类似一个交换机,对下统一采用报文来交换数据,什么心跳、回执等各式各样十几种类,他负责接收各个client发送来的消息,向各个client回发确认帧、发送对设备任务请求等工作;对上,向数据库存入设备状态等数据,将应用层的任务,组织合适的报文,下发给client。

    他所处的角色和位置:有时候,接收到client的请求后直接转发给BS(本质就是存入数据库啦);有时候,接收到client的请求后 思考 一下,向应用层拉取数据,装配为 恰当的报文 后,回送给对应的client;轮询BS提供的业务接口,发现有对设备请求的任务后,装配为恰当的格式后,发送给对应的client。

    就像一个繁忙的机械手,有时对于收到的报文,仅仅是左手接,逻辑处理后,右手丢回去,必须满足,从哪里接的、需送回给正确的client;有时候,只是充当一个转发器,收到的内容不做任何逻辑处理,直接丢给第三方处理。虽然程序都已经运行多时,都没有想好一个恰当的名字来命名程序,他更像是一个桥、一个转换器。

    下面是主进程需要声明的符号;收件箱存入接收的消息,不做任何处理;会有相应的进程不间断的扫描收件队列,一旦发现内容,便开始处理,处理结果会存入对应的发件箱,同样有相应的进程不间断的处理,通常为发送至client端,或者发送至BS接口。

 1  private readonly Config _config = InitConfig.ReadConfig();//配置信息 端口 IP
 2 
 3         private static IDictionary<int, WcsEndpoint<Socket>> _wcsList = new Dictionary<int, WcsEndpoint<Socket>>();//记录有多少个端点连接进来
 4         private const int BufferSize = 1024;
 5         private static SocketTool _socketTool = new SocketTool();//完成分包、压缩、加密关键报文内容
 6         private static MessageFactory _messageFactory = new MessageFactory();//用于构造不同的消息报文
 7         private static TaskSendDown _taskStockIn = new TaskSendDown(_socketTool);//用于桥接浏览器,通知web端一些事情发生
 8         private static ConcurrentQueue<ReceiveEntity> _receiveMailBox = new ConcurrentQueue<ReceiveEntity>();//收件箱
 9         private static ConcurrentQueue<ReceiveEntity> _sendMailBox = new ConcurrentQueue<ReceiveEntity>();//发件箱
10         private static object _lockwcsList = new object();//同步锁
11         private static CachePool _sPoolyncDataPool = new CachePool();//扩展数据缓存
View Code
 1  var ip = IPAddress.Parse(_config.LocalIp);
 2             TcpListener.InstanceSocketTool = _socketTool;
 3             TcpListener.LockwcsList = _lockwcsList;
 4             TcpListener.WcsList = _wcsList;
 5             var core = new TcpListener
 6             {//将外部变量传递给核心处理类
 7                 MaxConnect = int.Parse(_config.MaxConnect),
 8                 ReceiveMailBox = _receiveMailBox,
 9                 SPoolyncDataPool = _sPoolyncDataPool,
10                 SendMailBox = _sendMailBox,
11                 TaskStockIn = _taskStockIn,
12             };
13             CallbackUpdateStrip("启动wcs监听;");//同步UI
14             var receiveBoxHandler = new Thread(core.HandReceiveMailbox); //收件邮箱侦听
15             receiveBoxHandler.Start();
16 
17             var sendBoxHandler = new Thread(core.HandleSendMailbox); //发件箱侦听
18             sendBoxHandler.Start();
19             core.Listen(new IPEndPoint(ip, int.Parse(_config.PortForWcs)));
View Code

核心socket实现,抄袭了别人的稍微改造一下,或者高端点重构了一下:

  1 using System;
  2 using System.Collections.Concurrent;
  3 using System.Collections.Generic;
  4 using System.Diagnostics;
  5 using System.Drawing;
  6 using System.Linq;
  7 using System.Net;
  8 using System.Net.Sockets;
  9 using System.Runtime.InteropServices;
 10 using System.Runtime.Remoting.Contexts;
 11 using System.Text;
 12 using System.Threading;
 13 using Newtonsoft.Json;
 14 using NovaMessageSwitch;
 15 using NovaMessageSwitch.Bll;
 16 using NovaMessageSwitch.message;
 17 using NovaMessageSwitch.Model;
 18 using NovaMessageSwitch.Tool;
 19 using NovaMessageSwitch.Tool.DataCache;
 20 using NovaMessageSwitch.Tool.Log;
 21 
 22 /****************************************************************
 23 *   作者:wxy
 24 *   CLR版本:4.0.30319.42000
 25 *   创建时间:2016/3/25 8:47:51
 26 *   2016
 27 *   描述说明:
 28 *   SocketAsyncEventArgs是异步的socket类,封装了IOCP,可以很方便实现non-blocking IO
 29 非阻塞IO对server性能和吞吐量有很大好处
 30 *   修改历史:
 31 *  IPAddress[] addressList = Dns.GetHostEntry(Environment.MachineName).AddressList;
 32                 new TcpListener().Listen(/new IPEndPoint(addressList[addressList.Length - 1]/, 9900));
 33      
 34                 Console.ReadKey();
 35 
 36 *
 37 *****************************************************************/
 38 namespace CompletePortB
 39 {
 40     public class TcpListener
 41     {
 42         private SocketAsyncEventArgs _args;
 43         private Socket _listenerSocket;
 44         private StringBuilder _buffers;
 45         private const int BufferSize = 1024;
 46 
 47         public TcpListener()
 48         {
 49             MaxConnect = 1000;
 50 
 51         }
 52 
 53         public int MaxConnect { get; set; }
 54         public static object LockwcsList { get; set; }
 55         public static IDictionary<int, WcsEndpoint<Socket>> WcsList { get; set; }
 56         public TaskSendDown TaskStockIn { get; set; }
 57         public CachePool SPoolyncDataPool { get; set; }
 58         public ConcurrentQueue<ReceiveEntity> ReceiveMailBox { get; set; }
 59         public ConcurrentQueue<ReceiveEntity> SendMailBox { get; set; }
 60 
 61         public static SocketTool InstanceSocketTool { get; set; }
 62 
 63         public void Listen(EndPoint e)
 64         {
 65             try
 66             {
 67                 _buffers = new StringBuilder();
 68                 _listenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 69                 _listenerSocket.Bind(e);
 70                 _listenerSocket.Listen(MaxConnect);
 71                 _args = new SocketAsyncEventArgs();
 72                 _args.Completed += new EventHandler<SocketAsyncEventArgs>(ProcessAccept);
 73                 BeginAccept(_args);
 74                 Console.Read();
 75             }
 76             catch (Exception ex)
 77             {
 78                 
 79             }
 80         }
 81 
 82         private void BeginAccept(SocketAsyncEventArgs e)
 83         {
 84             e.AcceptSocket = null;
 85             //异步操作完成,返回false
 86             if (!_listenerSocket.AcceptAsync(e))
 87                 ProcessAccept(_listenerSocket, e);
 88 
 89         }
 90         
 91         //异步操作完成时调用此方法
 92         private void ProcessAccept(object sender, SocketAsyncEventArgs e)
 93         {
 94             var s = e.AcceptSocket;
 95             e.AcceptSocket = null;
 96             var args = new SocketAsyncEventArgs();
 97             //继续向下回掉函数
 98             args.Completed += new EventHandler<SocketAsyncEventArgs>(OnIoCompleted);
 99             //设定异步套接字方法的数据缓冲区
100             args.SetBuffer(new byte[BufferSize], 0, BufferSize);
101             args.AcceptSocket = s;
102             //IO操作同步完成,返回false,且不引发e参数的socketasynceventargs事件
103             //IO操作若被挂起,将返回true,且引发e参数的...
104             if (!s.ReceiveAsync(args))
105                 ProcessReceive(args);
106             BeginAccept(e);
107         }
108         void OnIoCompleted(object sender, SocketAsyncEventArgs e)
109         {
110             //获取最近使用此上下文对象执行的套接字操作类型
111             switch (e.LastOperation)
112             {
113                 case SocketAsyncOperation.Receive:
114                     this.ProcessReceive(e);
115                     break;
116                 case SocketAsyncOperation.Send:
117                     this.ProcessSend(e);
118                     break;
119                 default:
120                     throw new ArgumentException("The last operation completed on the socket was not a receive or send");
121             }
122         }
123 
124         private void ProcessSend(SocketAsyncEventArgs e)
125         {
126             if (e.SocketError == SocketError.Success)
127             {
128                 //接收完毕开始下次接收
129                 if (!e.AcceptSocket.ReceiveAsync(e))
130                 {
131                     this.ProcessReceive(e);
132                 }
133             }
134             else
135             {
136 
137             }
138 
139         }
140      
141 
142         /// <summary>
143         /// 维护wcs端点列表
144         /// </summary>
145         /// <param name="clientId"></param>
146         /// <param name="socket"></param>
147         private static void AddDictWcs(int clientId, Socket socket)
148         {
149             lock (LockwcsList)
150             {
151                 if (WcsList.Keys.Contains(clientId))
152                 {
153                     var endPoint = WcsList[clientId];
154                     endPoint.EndPoint = socket;
155                     endPoint.RecentTimeOld = endPoint.RecentTime;
156                     endPoint.RecentTime = DateTime.Now;
157                     InstanceSocketTool.UpdateWcsDisplay(endPoint, UpdateUi.Post);
158                 }
159                 else
160                 {
161                     var newEndPoit = new WcsEndpoint<Socket>
162                     {
163                         RecentTime = DateTime.Now,
164                         RecentTimeOld = null,
165                         EndPoint = socket
166                     };
167                     WcsList.Add(clientId, newEndPoit);
168                 }
169 
170                 foreach (var wcs in WcsList.Where(wcs => (wcs.Value.RecentTime - DateTime.Now).Minutes > 2))
171                 {
172                     WcsList.Remove(wcs.Key);
173                 }
174                 InstanceSocketTool.UpdateWcsDisplay(WcsList, UpdateUi.Post);
175             }
176         }
177         private string ReplyAckWcs(Socket socket, string oriSerial)
178         {
179             var socketTool = InstanceSocketTool;
180             var ackMessage = new MessageData<ContentReply>
181             {
182                 infoType = 1,
183                 content = new ContentReply(),
184                 destination = DataFlowDirection.wcs.ToString(),
185                 source = DataFlowDirection.wms.ToString(),
186                 infoDesc = "反馈报文",
187                 serial = Guid.NewGuid().ToString("N")
188             };
189             ackMessage.content.oriSerial = oriSerial;
190             var sendStr = $"?{JsonConvert.SerializeObject(ackMessage)}$";
191            
192             var infoDisplay = socketTool.CreateInfoDisplay(socket);
193             infoDisplay.Message = sendStr;
194             infoDisplay.CustomColor = Color.DodgerBlue;
195             socketTool.PrintInfoConsole($"{sendStr}", ConsoleColor.Green, infoDisplay, UpdateUi.PostMessageInfo);
196             return sendStr;
197         }
198         private void ProcessReceive(SocketAsyncEventArgs e)
199         {
200             if (e.BytesTransferred > 0)
201             {
202                 if (e.SocketError == SocketError.Success)
203                 {
204                     var data = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
205                     _buffers.Append(data);
206 
207                     if (e.AcceptSocket.Available == 0)
208                     {
209                         var receStr=_buffers.ToString();
210                         if (InstanceSocketTool.ValidateMessageJson(ref receStr) == false)
211                             return;
212                         var info = InstanceSocketTool.CreateInfoDisplay(e.AcceptSocket);
213                         info.Message = receStr;
214                         info.CustomColor = Color.Green;
215                         InstanceSocketTool.PrintInfoConsole($"远处:{e.AcceptSocket} 发来消息:{receStr}", Console.ForegroundColor,
216                             info, UpdateUi.PostMessageInfo);
217                         var receiveContent = InstanceSocketTool.UnPackMessage(receStr);
218                         dynamic obj = JsonConvert.DeserializeObject(receiveContent);
219                         InstanceSocketTool.ValidateMessageObj(obj, receStr);
220                         AddDictWcs((int)obj.clientID.Value, e.AcceptSocket);
221                         if (obj.infoType.Value != 0)
222                         {
223                             ReceiveMailBox.Enqueue(new ReceiveEntity
224                             {
225                                 Client = e.AcceptSocket,
226                                 Message = obj
227                             });
228                         }
229                         //重置
230                         _buffers = new StringBuilder();
231                         //发送反馈
232                         var sendStr = ReplyAckWcs(e.AcceptSocket, Convert.ToString(obj.serial.Value));
233                         var sendBuffer = (byte[])Encoding.UTF8.GetBytes(sendStr);
234                         e.SetBuffer(sendBuffer, 0, sendBuffer.Length);
235                         if (!e.AcceptSocket.SendAsync(e))
236                         {
237                             SendRequest(obj, e.AcceptSocket, e);
238                             ProcessSend(e);
239                         }
240                     }
241                     else if (!e.AcceptSocket.ReceiveAsync(e))
242                     {
243                         ProcessReceive(e);
244                     }
245                 }
246                 else
247                 {
248                     //this.ProcessError(e);
249                     AppLogger.Error(e.SocketError.ToString());
250                 }
251             }
252             else
253             {
254                 //this.CloseClientSocket(e);
255             }
256 
257         }
258         static void Callback(IAsyncResult result)
259         {
260 
261 
262         }
263       
264         private void SendRequest(dynamic Message,Socket Client, SocketAsyncEventArgs e)
265         {
266             var wcsReceiver = new WcsReceiver(TaskStockIn)
267             {
268                 CacheSyncPool = SPoolyncDataPool,
269                 ClientId = Convert.ToString(Message.serial.Value)
270             };
271 
272             if (Message.infoType == 40 || Message.infoType == 42)
273             {
274                 wcsReceiver.ReplyBrowser(Message);
275                 return;
276             }
277             wcsReceiver.RecieiveRequest(Message, new Action(delegate
278             {
279 
280 
281             }));
282             
283             var packageList = new FrameHandlerTool().GetPackage(Message);
284             var sleepTime = 2000;
285             if (packageList.Count == 1)
286                 sleepTime = 1;
287             foreach (var message in packageList)
288             {
289                 Thread.Sleep(sleepTime);
290                 var sendStr = $"?{JsonConvert.SerializeObject(message)}$";
291                 var sendBuffer = (byte[])Encoding.UTF8.GetBytes(sendStr);
292                 e.SetBuffer(sendBuffer, 0, sendBuffer.Length);
293                 while (e.AcceptSocket.SendAsync(e))
294                 {
295                 }
296                 var infoDisplay = InstanceSocketTool.CreateInfoDisplay(e.AcceptSocket);
297                 infoDisplay.Message = sendStr;
298                 infoDisplay.CustomColor = Color.DodgerBlue;
299                 InstanceSocketTool.PrintInfoConsole($"{sendStr}", ConsoleColor.Green, infoDisplay, UpdateUi.PostMessageInfo);
300             }
301         }
302         public void HandReceiveMailbox()
303         {
304             while (true)
305             {
306                 if (ReceiveMailBox.IsEmpty)
307                 {
308                     Thread.Sleep(100);
309                     continue;
310                 }
311                 try
312                 {
313                     ReceiveEntity messageEntity;
314                     ReceiveMailBox.TryDequeue(out messageEntity);
315 
316                     Action a = () =>
317                     {
318                         var wcsReceiver = new WcsReceiver(TaskStockIn)
319                         {
320                             CacheSyncPool = SPoolyncDataPool,
321                             ClientId = Convert.ToString(messageEntity.Message.serial.Value)
322                         };
323 
324                         if (messageEntity.Message.infoType == 40 || messageEntity.Message.infoType == 42)
325                         {
326                             wcsReceiver.ReplyBrowser(messageEntity.Message);
327                             return;
328                         }
329                         wcsReceiver.RecieiveRequest(messageEntity.Message, new Action(delegate
330                         {
331                             }));
332                         SendMailBox.Enqueue(new ReceiveEntity
333                         {
334                             Client = messageEntity.Client,
335                             Message = wcsReceiver.Message
336                         });
337 
338                     };
339                     a.BeginInvoke(new AsyncCallback(Callback), null);
340                     if (ReceiveMailBox.IsEmpty)
341                     {
342                         ReceiveMailBox = new ConcurrentQueue<ReceiveEntity>();
343                         ClearMemory();
344                     }
345                 }
346                 catch (Exception ex)
347                 {
348                     AppLogger.Error($"{ex.Message} {ex.StackTrace}", ex);
349                 }
350             }
351         }
352 
353         public void HandleSendMailbox()
354         {
355             while (true)
356             {
357                 try
358                 {
359                     if (SendMailBox.IsEmpty)
360                     {
361                         Thread.Sleep(100);
362                         continue;
363                     }
364                     ReceiveEntity messageEntity;
365                     SendMailBox.TryDequeue(out messageEntity);
366                     var wcsReceiver = new WcsReceiver(TaskStockIn)
367                     {
368                         Message = messageEntity.Message
369                     };
370                     wcsReceiver.ReplyResponseToWcs(messageEntity.Client);
371                     if (SendMailBox.IsEmpty)
372                     {
373                         ClearMemory();
374                     }
375                 }
376                 catch (Exception ex)
377                 {
378                     AppLogger.Error($"HandleSendMailbox:{ex.StackTrace}【-】{ex.Message}", ex);
379                 }
380             }
381         }
382         #region 内存回收
383         [DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")]
384         public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize);
385         /// <summary>
386         /// 释放内存
387         /// </summary>
388         public static void ClearMemory()
389         {
390             GC.Collect();
391             GC.WaitForPendingFinalizers();
392             if (Environment.OSVersion.Platform == PlatformID.Win32NT)
393             {
394                 SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1);
395             }
396         }
397         #endregion
398     }
399 }
View Code

    备注:把原来的收件与发件模型(采用同步的方式)给重构了,采用SendRequest一个函数搞定,程序测试下,慢在回发消息(即从第三方接口拉数据,需要一定时间),收到的消息,确认帧可以立发,性能貌似还可。

    备注2:前期需求不明确,都没想到会有那么多代码,所以前期思考不足,程序书写没考虑那么多,直接采用同步方式,来一个客户端开启一个进程,弊端是大大的,还好业务场景相对简单,经过一番折腾,采用2个队列来解耦,程序变的好看起来了。软件嘛,就是兼具美感的艺术品,如果自己看着都不舒服,那真是到重构的时间了(哈哈哪本书好像说过)。随着抽象出报文工厂、工具处理类、消息转发路由,程序确实清晰很多,修改业务功能、重构(之前学习过模式设计以及重构,模式容易生搬硬套,要化有形于无形,重构对我帮助最大,在写代码时就考虑到重构,逼迫我写的代码不能一团浆糊,否则将来如何重构啊,能独立出函数就函数,能抽象出类则用独立类)socket处理方式时,基本不费吹灰之力,有时间就好好改代码吧。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM