Socket理解。

 

其他大部分系统,例如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在传输过成功,不同时间发送的数据包有可能被合并,同时到达了客户端,这个就是黏包问题。例如发送方发送了hello+world,而接收方可能一次就接受了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, 传输过程中分割成为hello+world,那么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服务器的设计要点。如果您有其他见解,欢迎留言与讨论。


我们的最新动态 (Bamboo@pixysoft.net)

  • 1.解决comet在多页面中冲突的问题.[2011-1-23]

  • 2.优化部分后台逻辑代码.提升首页访问性能[2011-1-20]

  • 3.网络Comet平台再次成功对接上线.[2011-1-9]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/254156.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多元化时代敏捷软件开发的崛起与传统软件工程的延续

多元化时代敏捷软件开发的崛起与传统软件工程的延续 1.传统软件开发模式 1.1瀑布模型 1.1.1概念 瀑布模型&#xff0c;顾名思义&#xff0c;软件开发的过程如同瀑布飞流一般&#xff0c;自上而下&#xff0c;逐级下落。瀑布模型的核心思想是将问题按照工序进行简化&#xff0c;…

Linux中的cron计划任务配置详解

cron来源于希腊单词chronos&#xff08;意为“时间”&#xff09;&#xff0c;指Linux系统下一个自动执行指定任务的程序&#xff08;计划任务&#xff09; ####1. crontab命令选项代码如下: #crontab -u <-l, -r, -e> -u指定一个用户 -l列出某个用户的任务计划 -r删除某…

new和delete

和 sizeof 类似&#xff0c;sizeof不是函数&#xff0c;它是一个操作符&#xff0c;它在编译期就完成了计算&#xff0c;在函数运行期间它已经是一个常数值了。 int a;sizeof(int) 4;sizeof(a) 4;sizeof a ——也是4 不需要括号&#xff01;此时要注意&#xff1a;sizeof in…

char a[]和char *a的比较,数组名,数组首地址,a,a,a[0]

char a[]和char *a的比较 指针和数组存在着一些本质的区别。当然&#xff0c;在某种情况下&#xff0c;比如数组作为函数的参数进行传递时&#xff0c;由于该数组自动退化为同类型的指针&#xff0c;所以在函数内部&#xff0c;作为函数参数传递进来的指针与数组确实具有一定的…

Java中继承thread类与实现Runnable接口的区别

Java中线程的创建有两种方式&#xff1a; 1&#xff0e; 通过继承Thread类&#xff0c;重写Thread的run()方法&#xff0c;将线程运行的逻辑放在其中 2&#xff0e; 通过实现Runnable接口&#xff0c;实例化Thread类 在实际应用中&#xff0c;我们经常用到多线程&#xff0c;…

【VMware vSAN 6.6】6.2.启用性能服务:vSAN硬件服务器解决方案

目录 1. 简介 1.1.适用于HCI的企业级存储2. 体系结构 2.1.带有本地存储的服务器2.2.存储控制器虚拟系统套装的缺点2.3.vSAN在vSphere Hypervisor中自带2.4.集群类型2.5.硬件部署选项3. 启用vSAN 3.1.启用vSAN3.2.轻松安装3.3.主动测试4. 可用性 4.1.对象和组件安置4.2.重新构建…

Android eclipse导入项目后出现Unable to resolve target #39;android-17#39;解决方法

eclipse导入项目后出现Unable to resolve target android-17解决方法。在最后附带还有一种编译逻辑不成功情况解决方法。 一、问题情况 二、解决的方法 1、改动项目的目标版本号与当前Android sdk相相应的版本号 2、自己主动修复一下项目 三、这个问题不是上面的。是另外情况&a…

多个圆点,鼠标选取两个,求两个点的距离,用于计算像素尺寸(halcon实现)

read_image (Image, C:/Users/22967/Desktop/晶圆找位置/0.bmp) dev_close_window () dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle) dev_display (Image)binary_threshold (Image, Region1, max_separability, dark, UsedThreshold) connection (Region1, C…

修改UBOOT和LINUX调试串口(TI达芬奇芯片--DM6467)

Posted on 2011-10-31 10:53 jamiedu 阅读(889) 评论(0) 编辑 收藏 1.1 概述 TI针对DM6467提供的UBOOT和内核默认都是串口0作为调试串口输出的&#xff0c;但现在我需要使用DM6467的UART0的modem功能&#xff0c;所以修改代码&#xff0c;改变调试串口为串口2。 需要修改的主要…

Java List与数组之间的转换

http://blog.csdn.net/kingzone_2008/article/details/8444678转载于:https://www.cnblogs.com/longshiyVip/p/5985981.html

受欢迎的五个开源可视化工具——你的选择是?

摘要&#xff1a;大数据时代&#xff0c;数据为王&#xff0c;还在对一堆数据而发愁吗&#xff1f;试试可视化工具吧&#xff0c;相信本文提到的五款工具有一款能够帮助到你。人工智能时代&#xff0c;数据和算法以及硬件资源是非常重要的&#xff0c;相关行业的大公司也越来越…

halcon车刀崩边检测

list_files (新建文件夹, files, Files) read_image (Image, Files[0]) dev_close_window () get_image_size (Image, Width, Height) dev_open_window (0, 0, Width/1.5, Height/1.5, black, WindowHandle) dev_set_draw (margin) dev_set_colored (12) for Index:0 to |Files…

FFMPEG解码264文件步骤

本文以H264视频流为例&#xff0c;讲解解码流数据的步骤。 为突出重点&#xff0c;本文只专注于讨论解码视频流数据&#xff0c;不涉及其它&#xff08;如开发环境的配置等&#xff09;。如果您需要这方面的信息&#xff0c;请和我联系。 准备变量 定义AVCodecContext。如果…

Storm概念学习系列之storm的特性

不多说&#xff0c;直接上干货&#xff01; storm的特性 Storm 是一个开源的分布式实时计算系统&#xff0c;可以简单、可靠地处理大量的数据流。 Storm支持水平扩展&#xff0c;具有高容错性&#xff0c;保证每个消息都会得到处理&#xff0c;而且处理速度很快&#xff08;在一…

Confluence 6 配置服务器基础地址示例

2019独角兽企业重金招聘Python工程师标准>>> 如果 Confluence 的安装是没有安装在非根目录路径&#xff08;这个是上下文路径&#xff09;&#xff0c;然后服务器基础 URL 地址应该包括上下文地址。例如&#xff0c;你的 Confluence 正在运行在下面的地址&#xff1…

BootstrapValidator验证

bootstrap&#xff1a;能够增加兼容性的强大框架. 因为项目需要数据验证&#xff0c;看bootstrapValidator 还不错&#xff0c;就上手一直&#xff0c;完美兼容&#xff0c;话不多说。 需要引用css&#xff1a; bootstrap.min.css bootstrapValidator.min.css js: jquery-1.10.…

基于ARM9的视频采集传输系统

http://www.ic37.com/htm_tech/2007-11/77189_618093.htm

halcon找矩形顶点的一种方法

主程序&#xff1a; read_image (Image11, 11)*画仿射矩形 dev_set_color (green) draw_rectangle2 (3600, Row, Column, Phi, Length1, Length2)*生成仿射矩形xld gen_rectangle2_contour_xld (Rectangle, Row, Column, Phi, Length1, Length2) *找顶点工具&#xff08;基于卡…

老男孩linux运维50期

一、自我介绍&#xff1a;我是来自老男孩Linux运维脱产50期的杨国峰&#xff0c;我以前是学软件编码的&#xff0c;但在大学里基本没怎么学&#xff0c;每一门课都一知半解的&#xff0c;甚至有些连软件都不会装&#xff0c;在校期间&#xff0c;我对JAVA、网页设计等都不感兴趣…

博客收藏

http://www.dreamfairy.cn/blog/category/unity3d/转载于:https://www.cnblogs.com/wantnon/p/5989843.html