浅谈Socket长连+多线程[原创,欢迎指点]
前戏
【PS:原文手打,转载说明出处】
【PS:博主自认为适用于云平台设备管控,且适用于IM主控】
好久没来了,13年时还想着多写一些博客,这都17年过年,年前也写一写Scoket+多线程,不足之处,见谅。(算是个人小总结)
缘由
不知各位同仁有没有发现,用简单,无外乎就是都是一个流程
1)监听链接
2)校验链接是否是正常链接
3)保存链接至全局静态字典
4)开启新线程监听监听到的线程报文
5)执行对应命令或者发送对应命令
然后内部各种心跳,各种断线重连后释放缘由链接对象,重新保存对象;
其实使用过程当中,貌似也就这些来着,不管再大的系统,也都是这个主流程,但是一些细节处理如何把握;
下面我简单的说说我的处理
(1)分布式
(2)线程在无命令状态下自我销毁
(3)线程状态监听
正文
1 分布式
背景:其实吧,也算是我公司穷,用不起高大上的硬件负载,其次能由于个人学历水平有限,搞不定各种开源导致的以下产物
1.1 流程图
1.2 流程说明
分布式服务器中植入程序
1)负责监听前置机发送当前连接数以及服务资源使用情况
2)监听客户机连接并返回当前最优前置至客户机
3)客户机连接前置
2 前置机处理
当客户机连接至前置机时,前置机实时监听后,开始监听到底是哪一台客户机连接上来,在用Dictionary<key,Socket>存储连接Socket,开启新线程监听客户机心跳等
关键:连接初始化指令时,带入客户机唯一标识,这时字典中存储的key为唯一标识,value为Socket
2.1 前置与分布式服务器
1:根据字典中保存的Socket,监听Socket最后链接时间,以及判断Socket链接状态,统计连接成功数据,释放连接中断Socket,发送连接数至分布式服务器
2:发送当前前置程序内存、CPU占用百分比等相关服务器硬件使用情况信息至分布式服务器
2.2 前置与客户机
因为连接上了,不止只收取命令,还需要发送对应命令至客户机,顾沿用字典中存储的长连接Socket对象,发送命令至客户机
因为是命令,遵循应答原则,必须命令到达后,返回明确指令收到,在发送下一条命令,这里我使用了线程阻塞(当然,如果是IM发送聊天记录就没关系了,集体丢到客户机就可以了,返回成功后在更新发送时间以及发送状态就好)
3 编码
流程大家应该其实都懂,然后就是代码,因为马上年终尾牙,要去粗饭了,代码贴上来,各自领悟领悟哈
为什么要重写堆栈,以为我的是设备,设备命令必须保证命令一定送达,而且有顺序的执行,则重新写了个规则
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace KR.DevFx.SocketTools { public class DevQueue:IDisposable { #region 属性 ; /// <summary> /// 序号 /// </summary> public int No { get { return no; } set { no = value; } } ; /// <summary> /// 当前执行序号 /// </summary> public int CurrentCommandNo { get { return currentCommandNo; } set { currentCommandNo = value; } } private bool queueSwitch = false; /// <summary> /// 堆栈开关 /// </summary> public bool QueueSwitch { get { return queueSwitch; } set { queueSwitch = value; } } private bool isMeanwhileExec = false; /// <summary> /// 是否同时执行 /// </summary> public bool IsMeanwhileExec { get { return isMeanwhileExec; } set { isMeanwhileExec = value; } } private Dictionary<int, object> dicCommand = new Dictionary<int, object>(); /// <summary> /// 堆栈内容 /// </summary> public Dictionary<int, object> DicCommand { get { return dicCommand; } set { dicCommand = value; } } #endregion #region 方法 /// <summary> /// 参数入栈 /// </summary> /// <param name="_param"></param> public void AddQueue(object _param) { if (No == int.MaxValue) { No = ; } No = No + ; DicCommand.Add(No, _param); } /// <summary> /// 取堆栈数据 /// </summary> /// <returns></returns> public object GetQueue() { ; if (QueueSwitch) { throw new Exception("当前堆未执行结束,无法再次获取堆内内容!如需获取必须执行RemoveQueue方法"); } if (DicCommand.ContainsKey(nextCommandNo)) { if (!IsMeanwhileExec) QueueSwitch = true; object returnVlaue = DicCommand[nextCommandNo]; CurrentCommandNo = CurrentCommandNo + ; return returnVlaue; } return null; } /// <summary> /// 移出当前堆栈内容 /// </summary> /// <returns></returns> public bool RemoveQueue() { ) { if (DicCommand.ContainsKey(CurrentCommandNo)) { QueueSwitch = false; return DicCommand.Remove(CurrentCommandNo); } else throw new Exception(string.Format("根据当前命令号未找到堆栈内容!命令号:{0}", CurrentCommandNo)); } else { QueueSwitch = false; return true; } } /// <summary> /// 移出当前堆栈内容,且获取下一堆栈内容对象 /// </summary> /// <returns></returns> public object RemoveQueueAndGet() { if (RemoveQueue()) { return GetQueue(); } else { return null; } } #endregion public void Dispose() { No = ; CurrentCommandNo = ; DicCommand.Clear(); } } }
DevQueue 栈堆
线程管控,为什么要重写,因为发现了在多线程过程中,使用线程状态来判断线程是否在执行还是死睡状态不准导致,具体原因没有刨根。
//***************************************************************** // Description: 线程助手 // Author: HUAQIAN ZHOU // Created Date: 2016/12/26 //***************************************************************** // Modified By: // Modification Date: // Purpose of Modification: //***************************************************************** using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace KR.DevFx.SocketTools { public class DevThreadPool { public static Dictionary<string, DevThread> DicLastingThread = new Dictionary<string, DevThread>(); } /// <summary> /// 线程助手 /// </summary> public class DevThread : IDisposable { #region 构造函数 public DevThread() { ThreadStatus = DevThreadStatus.JustInit; } /// <summary> /// 初始化线程 /// </summary> /// <param name="_threadPara"></param> /// <param name="_threadName"></param> /// <param name="isLasting"></param> public DevThread(string _threadName) { ThreadStatus = DevThreadStatus.JustInit; if (!DevThreadPool.DicLastingThread.ContainsKey(_threadName)) { DevThreadPool.DicLastingThread.Add(_threadName, this); } ThreadName = _threadName; } #endregion #region 线程堆栈命令 private DevQueue _threadQueue = new DevQueue(); public DevQueue ThreadQueue { get { return _threadQueue; } set { _threadQueue = value; } } #endregion #region 属性 /// <summary> /// 执行内容适用委托 /// </summary> /// <param name="work"></param> public delegate void ThreadFunByPara(Object work); /// <summary> /// 线程工作的内容委托 /// </summary> public ThreadFunByPara FunByPara { get; set; } /// <summary> /// 执行内容适用委托 /// </summary> /// <param name="work"></param> public delegate void ThreadFun(); /// <summary> /// 线程工作的内容委托 /// </summary> public ThreadFun Fun { get; set; } private Thread _currentThread; /// <summary> /// 当前线程 /// </summary> public Thread CurrentThread { get { return _currentThread; } set { _currentThread = value; } } private string threadStatus; /// <summary> /// 当前线程状态 /// </summary> public string ThreadStatus { get { return threadStatus; } set { threadStatus = value; } } private object _threadPara; /// <summary> /// 线程参数 /// </summary> public object ThreadPara { get { return _threadPara; } set { _threadPara = value; } } private string _threadName; /// <summary> /// 线程名称 /// </summary> public string ThreadName { get { return _threadName; } set { _threadName = value; } } #endregion #region 方法 /// <summary> /// 开启线程 /// </summary> public void ThreadStart() { if (ThreadStatus == DevThreadStatus.JustInit) { CurrentThread = new Thread(new ThreadStart(Process)); CurrentThread.IsBackground = true; ThreadStatus = KR.DevFx.SocketTools.DevThreadStatus.Running; CurrentThread.Start(); } else if (ThreadStatus == DevThreadStatus.SleepWait) { CurrentThread.IsBackground = true; ThreadStatus = KR.DevFx.SocketTools.DevThreadStatus.Running; CurrentThread.Interrupt(); } else if (CurrentThread.ThreadState == ThreadState.Stopped || ThreadStatus == DevThreadStatus.Abort) { CurrentThread = new Thread(new ThreadStart(Process)); CurrentThread.IsBackground = true; ThreadStatus = KR.DevFx.SocketTools.DevThreadStatus.Running; CurrentThread.Start(); } } /// <summary> /// 开启线程 /// </summary> /// <param name="_objPara">线程参数</param> public void ThreadStart(object _objPara) { if (_objPara == null) { throw new Exception("exception:DevThread ThreadStart para is null"); } if (ThreadStatus == DevThreadStatus.JustInit) { CurrentThread = new Thread(new ThreadStart(Process)); CurrentThread.IsBackground = true; ThreadPara = _objPara; ThreadStatus = KR.DevFx.SocketTools.DevThreadStatus.Running; CurrentThread.Start(); } else if (ThreadStatus == DevThreadStatus.SleepWait) { ThreadStatus = KR.DevFx.SocketTools.DevThreadStatus.Running; CurrentThread.IsBackground = true; ThreadPara = _objPara; CurrentThread.Interrupt(); } else if (CurrentThread.ThreadState == ThreadState.Stopped || ThreadStatus == DevThreadStatus.Abort) { CurrentThread = new Thread(new ThreadStart(Process)); CurrentThread.IsBackground = true; ThreadStatus = KR.DevFx.SocketTools.DevThreadStatus.Running; CurrentThread.Start(); } } /// <summary> /// 线程处理方法(调用委托执行) /// </summary> private void Process() { try { if (ThreadPara == null) { Fun(); } else { FunByPara(ThreadPara); } } catch { } } /// <summary> /// 睡眠时间 /// </summary> /// <param name="timeout"></param> public void ThreadSleep(int timeout) { try { Thread.Sleep(timeout); } catch { } } /// <summary> /// 无限睡眠 /// </summary> public void ThreadSleep() { try { ThreadStatus = DevThreadStatus.SleepWait; Thread.Sleep(Timeout.Infinite); } catch { } } /// <summary> /// 线程释放 /// </summary> public void Dispose() { ThreadQueue.Dispose(); try { if (CurrentThread != null) { CurrentThread.Abort(); CurrentThread = null; } } catch { } } #endregion } /// <summary> /// 线程状态 /// </summary> public class DevThreadStatus { /// <summary> /// 正在执行 /// </summary> public const string Running = "Running"; /// <summary> /// 线程等待 /// </summary> public const string SleepWait = "SleepWait"; /// <summary> /// 刚初始化 /// </summary> public const string JustInit = "JustInit"; /// <summary> /// 线程终止 /// </summary> public const string Abort = "Abort"; } }
SocketTools
线程阻塞
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace KR.ClientServices.Service { public class WaitResponseMessage { private string _WaitResopnseData = ""; /// <summary> /// 要等待的响应报文数据 /// </summary> public string WaitResopnseData { get { return _WaitResopnseData; } set { _WaitResopnseData = value; } } private string _WaitResponseDataCode = ""; /// <summary> /// 要等待的响应报文标识码 /// </summary> public string WaitResponseDataCode { get { return _WaitResponseDataCode; } set { _WaitResponseDataCode = value; } } ; /// <summary> /// 发送数据超时等待时间 秒 /// </summary> public int SendDataWaitTimeOut { get { return _SendDataWaitTimeOut; } set { _SendDataWaitTimeOut = value; } } /// <summary> /// 发送数据超时阻塞器 /// </summary> private ManualResetEvent SendDataObturator = new ManualResetEvent(false); /// <summary> /// 开始等待 /// </summary> /// <returns></returns> public bool BeginWait() { SendDataObturator.Reset(); return SendDataObturator.WaitOne(SendDataWaitTimeOut, false); } /// <summary> /// 结束等待 /// </summary> public void EndWait() { SendDataObturator.Set(); } } }
WaitResponseMessage
发送时处理
public SystemResult SendWait(byte[] sendData, string code) { lock (ApSocket) { SystemResult sr = new SystemResult(); try { WaitSendResponseMessage.WaitResponseDataCode = code; this.ApSocket.Send(sendData); if (WaitSendResponseMessage.BeginWait()) { sr.ResultMsg = WaitSendResponseMessage.WaitResopnseData; //应答 1 0x06:成功 } else { sr.ResultMsg = "发送数据失败:响应超时"; sr.Status = ; } } catch (Exception ex) { sr.Status = ; sr.ResultMsg = ex.Message; } return sr; } }
SendWait
结尾
就这样了哈,过个好年,我们公司是小公司,现在目前也没有说几十万几百万台设备在对外使用,所以也没测试贫瘠来着,希望那天我能面对几十万台,几百万台设备同时上来让我耍耍,那时候工资花啦啦的,哈哈哈
浅谈Socket长连+多线程[原创,欢迎指点]的更多相关文章
- 浅谈Socket长连+多线程
缘由 不知各位同仁有没有发现,用简单,无外乎就是都是一个流程 1)监听链接 2)校验链接是否是正常链接 3)保存链接至全局静态字典 4)开启新线程监听监听到的线程报文 5)执行对应命令或者发送对应命令 ...
- 【转】浅谈多核CPU、多线程、多进程
浅谈多核CPU.多线程.多进程 1.CPU发展趋势 核心数目依旧会越来越多,依据摩尔定律,由于单个核心性能提升有着严重的瓶颈问题,普通的桌面PC有望在2017年末2018年初达到24核心(或者16核3 ...
- 浅谈Session的使用(原创)
目录 浅谈Session的使用(原创) 1.引言 2.Session域的生命周期 2.1 Session的创建 2.2 Session的销毁 3.那么,session被销毁后,其中存放的属性不就都访问 ...
- 浅谈Socket编程
浅谈Socket编程 说到Socket,想必大家会觉得陌生又熟悉.许多同学听说过Socket,但仅仅知道它翻译成中文叫做套接字,除此之外似乎并没有太多的了解了.那么今天我就来抛砖引玉地聊一聊Socke ...
- (转)浅谈.NET下的多线程和并行计算(一)前言
转载——原文地址:http://www.cnblogs.com/lovecindywang/archive/2009/12/25/1632014.html 作为一个ASP.NET开发人员,在之前的开发 ...
- 浅谈多核CPU、多线程、多进程
1.CPU发展趋势 核心数目依旧会越来越多,依据摩尔定律,由于单个核心性能提升有着严重的瓶颈问题,普通的桌面PC有望在2017年末2018年初达到24核心(或者16核32线程),我们如何来面对这突如其 ...
- 浅谈 Boost.Asio 的多线程模型
Boost.Asio 有两种支持多线程的方式,第一种方式比较简单:在多线程的场景下,每个线程都持有一个io_service,并且每个线程都调用各自的io_service的run()方法. 另一种支持多 ...
- <转>浅谈 Boost.Asio 的多线程模型
本文转自:http://senlinzhan.github.io/2017/09/17/boost-asio/ Boost.Asio 有两种支持多线程的方式,第一种方式比较简单:在多线程的场景下,每个 ...
- 浅谈最长上升子序列(LIS)
一.瞎扯的内容 给一个长度为n的序列,求它的最长上升子序列(LIS) 简单的dp n=read(); ;i<=n;i++) a[i]=read(); ;i<=n;i++) ;j<i; ...
随机推荐
- 未在本地计算机上注册“Microsoft.Ace.OleDB.12.0”
这是异常 我的电脑室x86的所以选择x86.
- PAT (Advanced Level) 1020. Tree Traversals (25)
递归建树,然后BFS一下 #include<iostream> #include<cstring> #include<cmath> #include<algo ...
- flex中form表单中子元素之间的距离控制
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.ado ...
- Quick Cocos2dx 与 EnterFrame事件
利用EnterFrame做出行走的效果,效果图如下: 具体操作: 1 给self多加一个bg1用作与bg无限循环换位 2 在AnotherScene:onEnter方法里面新增onEnterFrame ...
- openstack controller ha测试环境搭建记录(十)——配置neutron(控制节点)
创建neutron用户:mysql -u root -p CREATE DATABASE neutron;GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@ ...
- CodeForces 660B Seating On Bus
模拟. #include<cstdio> #include<cstring> #include<cmath> #include<vector> #inc ...
- Spring自学教程-声明式事务处理(六)
Spring事务处理分两种: 一.编程式事务:在程序中控制事务开始,执行和提交: 1.1 使用TransactionTemplate, 使用回调函数执行事务,不需要显示开始事务,不需要显示提交事务,但 ...
- tableView滑动到底部
- (void)scrollToBottom { NSInteger sectionCount = [self.dataSource numberOfSectionsInTableView:self] ...
- iOS开发——代理与block传值
一.代理传值的方法 1.Hehe1ViewController.h中 #import <UIKit/UIKit.h> @protocol Hehe1ViewControllerDelega ...
- _foreach
从JDK1.5之后增加的foreach循环取消索引 for(类型 变量 : 数组 | 集合){ 每一次循环会自动将数组内容设置给变量 } 范例: ,,,} ; for(int x : i){ Syst ...