动画视频模板网站,企业网站开发制作费入那里,公司注册网上核名业务如何终止,家在深圳歌曲其他大部分系统#xff0c;例如CRM/CMS/权限框架/MIS之类的#xff0c;无论怎么复杂#xff0c;基本上都能够本地代码本地调试#xff0c;性能也不太重要。#xff08;也许这个就是.net的企业级开发的战略吧#xff09; 可是来到通讯系统#xff0c;一切变得困难复杂。原…
其他大部分系统例如CRM/CMS/权限框架/MIS之类的无论怎么复杂基本上都能够本地代码本地调试性能也不太重要。也许这个就是.net的企业级开发的战略吧 可是来到通讯系统一切变得困难复杂。原因实在太多了如
性能永远是第一位有时候一个if判断都要考虑性能毕竟要损耗一个CPU指令而在通讯系统服务器每秒钟都产生上百万级别的通讯量这样一个if就浪费了1个毫秒了。 系统环境极其恶劣所有我们可以想象的恶意攻击、异常输入等都要考虑 网络说断就断在socket环境下客户端可以以各种理由断开链接而且服务器根本不会知道连一个流水作业的业务逻辑都无法保证正常执行因此需要设计各种辅助的协议、架构去监督。 各种网络链接问题例如代理、防火墙等等。。。
经过了1年的跌跌撞撞我总算收获了点有用的经验本文先从设计角度介绍一些我在Socket编程中的经验下一篇在放出源代码。 ------------------
现有的Socket编程资源
------------------
1. 首选推荐开源的XMPP框架也就是Google的Gtalk的开源版本。里面的架构写的非常漂亮。特点就是简洁、清晰。 2. 其次推荐LumaQQ.net这套框架本身写的一般般但是腾讯的服务器非常的猛这样必然导致客户端也要比较猛。通过学习这套框架能够了解腾讯的IM传输协议设计而且他们的协议是TCP/UDP结合一举两得。 3. 最后就是DotMsn。这个写的实在很一般般而且也主要针对了MSN的协议特点。是能够学习到一点点的框架知识的不过要有所鉴别。 ------------------
Socket的选择
------------------
在Java到了Java5终于出现了异步编程NIO于是各种所谓的框架冒了出来例如MINA, xsocket等等而在.NET微软一早就为我们准备好了完善的Socket模型。主要包括同步Socket、异步Socket我还听说了.net 3.x之后异步的Socket内置了完成端口。综合各种模型的性能我总结如下 1. 如果是短链接使用同步socket。例如http服务器、转接服务器等等。 2. 如果是长链接使用异步socket。例如通讯系统QQ / Fetion、webgame等。 3. .net的异步socket的连接数性能在 7500/s每秒并发7500个socket链接。而听说完成端口在1.5w所有。但是我到目前还没有正式见过所谓的完成端口不知道到底有多牛逼。 4. 我听说了java的NIO性能在5000/s所有我们项目内部也进行了链接测试在4000~5000比较稳定当然如果代码调优之后能提高一点点。 ------------------
TCP Socket协议定义
------------------
本文从这里开始主要介绍TCP的socket编程。
新手们例如当初的我第一次写socket总是以为在发送方压入一个Helloworld,接收方收到了这个字符串就“精通”了Socket编程了。而实际上这种编程根本不可能用在现实项目因为 1. socket在传输过程中helloworld有可能被拆分了分段到达客户端例如 hello world一个分段就是一个包Package这个就是分包问题。 2. socket在传输过成功不同时间发送的数据包有可能被合并同时到达了客户端这个就是黏包问题。例如发送方发送了helloworld,而接收方可能一次就接受了helloworld. 3. socket会自动在每个包后面补n个 0x0 byte分割包。具体怎么去补这个我就没有深入了解。 4. 不同的数据类型转化为byte的长度是不同的例如int转为byte是4位int32这样我们在制作socket协议的时候要特别小心了。具体可以使用以下代码去测试
代码 !-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -- public void test() { int myInt 1; byte[] bytes new byte[1024]; BinaryWriter writer new BinaryWriter(new MemoryStream(bytes)); writer.Write(myInt); writer.Write(j); writer.Close(); } 尽管socket环境如此恶劣但是TCP的链接也至少保证了
包发送顺序在传输过程中是不会改变的例如发送方发送 H E L L那么接收方一定也是顺序收到H E L L这个是TCP协议承诺的因此这点成为我们解决分包、黏包问题的关键。 如果发送方发送的是helloworld, 传输过程中分割成为helloworld那么TCP保证了在hello与world之间没有其他的byte。但是不能保证helloworld和下一个命令之间没有其他的byte。 因此如果我们要使用socket编程就一定要编写自己的协议。目前业界主要采取的协议定义方式是包头包体长度包体。具体如下 1. 一般包头使用一个int定义例如int 173173173作用是区分每一个有效的数据包因此我们的服务器可以通过这个int去切割、合并包组装出完整的传输协议。有人使用回车字符去分割包体例如常见的SMTP/POP协议这种做法在特定的协议是没有问题的可是如果我们传输的信息内容自带了回车字符串那么就糟糕了。所以在设计协议的时候要特别小心。 2. 包体长度使用一个int定义这个长度表示包体所占的比特流长度用于服务器正确读取并分割出包。 3. 包体就是自定义的一些协议内容例如是对像序列化的内容现有的系统已经很常见了使用对象序列化、反序列化能够极大简化开发流程等版本稳定后再转入手工压入byte操作。 一个实际编写的例子比如我要传输2个整型 int 1, int 2那么实际传输的数据包如下 173173173 8 1 2
|------包头------|----包体长度----|--------包体--------|
这个数据包就是4个整型总长度 4*4 16。 说说我走的弯路
我曾经偷懒使用特殊结束符去分割包体这样传输的数据包就不需要指名长度了。可是后来高人告诉我如果使用特殊结束符去判断包性能会损失很大因为我们每次读取一个byte都要做一次if判断这个性能损失是非常严重的。所以最终还是走主流使用以上的结构体。 ------------------
Socket接收的逻辑概述
------------------
针对了我们的数据包设计socket的传输特点我们的接收逻辑主要是
1. 寻找包头。这个包头就是一个int整型。但是写代码的时候要非常注意一个int实际上占据了4个byte而可悲的是这4个byte在传输过程中也可能被socket 分割了因此读取判断的逻辑是
判断剩余长度是否大于4 读取一个int判断是否包头如果是就跳出循环。 如果不是包头则倒退3个byte回到第一点。 如果读取完毕也没有找到则有可能包头被分割了因此当前已读信息压入接收缓存等待下一个包到达后合并判断。
2. 读取包体长度。由于长度也是一个int因此判断的时候也要小心同上。
3. 读取包体由于已知包体长度因此读取包体就变得非常简单了只要一直读取到长度未知剩余的又回到第一条寻找包头。 这个逻辑不要小看就这点东西忙了我1天时间。而非常奇怪的是我发现c#写的socket似乎没有我说的这么复杂逻辑。大家可以看看LumaQQ.net / DotMsn等他们的socket接收代码都非常简单。我猜想要么是.net的socket进行了优化不会对int之类的进行分割传输要么就是作者偷懒随便写点代码开源糊弄一下。 ------------------
Socket服务器参数概述
------------------
我在开篇也说了Socket服务器的环境是非常糟糕了最糟糕的就是客户端断线之后服务器没有收到通知。 因为socket断线这个也是个信息也要从客户端传递到我们socket服务器。有可能网络阻塞了导致服务器连断开的通知都没有收到。
因此我们写socket服务器就要面对2个环境
1. 服务器在处理业务逻辑中的任何时候都会收到Exception, 任何时候都会因为链接中断而断开。
2. 服务器接收到的客户端请求可以是任意字符串因此在处理业务逻辑的时候必须对各种可能的输入都判断防止恶意攻击。 针对以上几点我们的服务器设计必须包含以下参数
1. 客户端链接时间记录主要判断客户端空连接情况防止连接数被恶意占用。
2. 客户端请求频率记录要防止客户端频繁发送请求导致服务器负荷过重。
3. 客户端错误记录一次错误可能导致服务器产生一次exception而这个性能损耗是非常严重的因此要严格监控客户端的发送协议错误情况。
4. 客户端发送信息长度记录有可能客户端恶意发送非常长的信息导致服务器处理内存爆满直接导致宕机。 5. 客户端短时间暴涨有可能在短时间内客户端突然发送海量数据直接导致服务器宕机。因此我们必须有对服务器负荷进行监控一旦发现负荷过重直接对请求的socket返回处理失败例如我们常见的“404”。 6. 服务器短时间发送信息激增有可能在服务器内部处理逻辑中突然产生了海量的数据需要发送例如游戏中的“群发”因此必须对发送进行队列缓存然后进行合并发送减轻socket的负荷。 ------------------
后记
------------------
本文从架构设计分析了一个socket服务器的设计要点。如果您有其他见解欢迎留言与讨论。 我们的最新动态 (Bamboopixysoft.net) 1.解决comet在多页面中冲突的问题.[2011-1-23] 2.优化部分后台逻辑代码.提升首页访问性能[2011-1-20] 3.网络Comet平台再次成功对接上线.[2011-1-9]