浅谈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; ...
随机推荐
- 前端UI
一个非常好的前端UI,值得研究下 http://semantic-ui.com/
- php 注意点
1.如果一个方法可静态化,就对它做静态声明.速率可提升至4倍. 2.echo 比 print 快. 3.使用echo的多重参数(译注:指用逗号而不是句点)代替字符串连接. 4.在执行for循环之前确定 ...
- Linux的iptables常用配置范例(2)
iptables -F #清除所有规则 iptables -X #清除所有自定义规则 iptables -Z #各项计数归零 iptables -P INPUT DROP #将input链 ...
- webapp之路--之query media
query media是css3中的模块,对于移动端的开发是非常重要的,是响应式web设计的中不可或缺的一部分.简单点说就是根据不同移动设备的屏幕参数来制定不同的css方案以实现web的响应式开发.目 ...
- Android Studio:Gradle DSL method not found: 'runProguard()'
Android Studio发布了新的1.0版,更新之后却发现原来在0.8下面正常的项目编译失败了,从报错上来看是卡在gradle上面. Gradle DSL method not found: 'r ...
- Sequence Classification
Natural Language Processing with Python Charpter 6.1 import nltk from nltk.corpus import brown def p ...
- PHPCMS v9 列表页实现文件下砸
{template "content","header"} <div class="list"> <div class=& ...
- 源码解析-knockout源码准备
准备包括心理和资源两方面. 心理 我看过一句话说,当你用一个框架时,不要忙着看一遍使用教程就开始写项目,先去看看框架原理. 这句话我深以为然.现今前端快速发展,很多前端攻城狮都很茫然:框架更新太快了, ...
- LPC1768的SPI通讯
SPI是一种全双工串行接口,可处理多个连接到指定总线上的主机和从机.在数据传输过程中总线上只能有一个主机和一个从机通信.在数据传输中,主机总是会向从机发送一帧8到16个位的数据,而从机也总会向主机发送 ...
- css3动画-transition
当css属性改变的时候,控制animation的速度,让属性的变化发生在一段时间之内,而不是立即生效. 语法 transition: <property> <duration> ...