------------------

前言

------------------ 
发了第一篇文章后,有不少同志留言,看来socket编程仍然是软件系统里面一个比较难的部分。

第一篇文章主要介绍了传输协议的设计,这个是整个socket框架最底层基础的部分,接下来整个socket服务器大楼都将在这个协议设计基础上不断搭建出来。

这篇文章我主要接上文提出的服务器各个性能参数给出解决思路。

-------------------

socket服务端接收模块设计 

-------------------

当服务器Accept一个新的socket之后,会对这个socket进行封装,成为一个connection(当然是自定义了) 。之后的处理都会交给这个connection负责。

由于socket发送的数据存在分包黏包问题,connection接收模块注定了要使用接收队列。当然这个所谓的接受队列并没有大家想象的这么深奥,大致的代码结构如下:

public class SocketReceiveQueue
{
    private Queue<ISocketReceivePackage> queue = null;
    private MemoryStream receiveBuffer = null;
}

即一个queue的接收队列+一个stream。处理逻辑:

a. 接收到数据,压入receiveBuffer

b. 从receiveBuffer读取数据、获取协议包ISocketReceivePackage,这里可能会有多个,也可能一个也没有。

c. 当接收完毕后,协议包再从queue出队列,交给注册的协议处理handler处理。

到目前为止,整块接收逻辑并没有涉及具体的业务、也绝对不应该涉及具体的业务。唯一要额外注意的就是接收包的长度问题,即协议包声明的length是否过大。

这里要注意,由于整个接收模块没有涉及到具体的业务逻辑,也就不应该在这里对任何的输入进行检测(非法攻击、频率等),代码上就是以最快速度解析完协议包,然后丢给上层handler分析。

-------------------

客户端请求性能分析

-------------------

当协议包来到了与业务相关的Handler之后,我们开始进行性能检测。首先是请求频率,使用如下公式:

requestInterval = (requestInterval * requestTimes + interval) / (requestTimes + 1)

计算得到的requestInterval就是客户端的请求频率。 数学上也很简单,就是一个类似f(x) = af(x)+b的迭代算法。这个算法的特定当然就是性能高,我只需要记录用户当前请求时间、请求累计次数之后,就能完全监控了用户的请求性能。

此外还需要记录顾客的错误次数。从设计理论上来说,客户传输过来的数据不应该有错,除非代码出错。当然,如果在值类型之间转换出现的问题算是错误的话(用户正常输入了错误的数值),这个不算入错误。这个错误值是需要记录在数据库里面;一旦发现错误值过大,则直接封这个IP了。

还需要说明的是,在服务器一定有一个触发器,每x秒会遍历一次所有的connection,一旦发现有长时间无请求的空连接,要主动踢出。

-------------------

socket服务端发送模块设计

-------------------

当服务器处理完数据后,需要将处理结果回复给客户端。如果使用简单的设计思路,即直接压入socket发送,性能是非常的低的;因此socket的发送必定需要使用发送队列。使用发送队列的优势在于:

a. 当服务器内部需要发送的数据激增的时候,通过压入发送队列,能够减轻IO的处理压力。很多时候我们会发现整个服务器的性能瓶颈就在IO的处理上(收发) ,而不是服务器的数据库操作等。因此设计上就要以减轻IO处理为目标。

b. 使用发送队列,能够把多个回复数据包合并一次发送,极大减轻了IO的压力。

发送队列的结构也就是一个Queue,大致的设计如下:

代码

public class SendMessageQueueController
{
   Queue<SocketConnection> queue;
} public class SocketConnection
{
    private SocketReceiveQueue receiveQueue;//接收队列
    private SocketSendQueue sendQueue;//发送队列
} public class SocketSendQueue 
{
    private Queue<ISocketSendPackage> queue;//发送协议集合
}

代码逻辑如下:

a. 把需要发送的协议包压入当前SocketSendQueue.

b. 判断SocketConnection是否已经存在在SendMessageQueueController,如果不存在,则入列;如果存在,则返回。

c. SendMessageQueueController每隔x毫秒检查一次发送队列,如果发现有数据,则进入while循环,直到所有SocketConnection出列并发送所有的数据。之后再进入等待。

d. 所谓包合并发送,就是把多个协议包一次写入发送的Stream里面,然后让socket发送。

这块的设计问题主要集中在线程冲突,需要在关键地方加几把锁,否则就容易出现线程冲突了。

------------------

后记

------------------

本文介绍的方法并不是最好的方法,我也相信业界有更加成熟的思路。不过我文中列举的一些设计思路至少我用起来还是能够满足现有需求的。

如果各位同志有更好的思路,希望多多留言指教。

在下一篇文章中将结合具体的传输协议开始设计服务端的通用逻辑模块,例如重发、数据缓存、登录登出等。

经过一年时间的沉淀 再次回首 TCP Socket服务器编程 (二)的更多相关文章

  1. 经过一年时间的沉淀 再次回首 TCP Socket服务器编程--转

    ------------------ 前言 ------------------ 开发了这么多年,发现最困难的程序开发就是通讯系统. 其他大部分系统,例如CRM/CMS/权限框架/MIS之类的,无论怎 ...

  2. 再次回首 TCP Socket服务器编程

    转载:http://www.cnblogs.com/zc22/archive/2010/06/27/1766007.html ------------------ 前言 --------------- ...

  3. C# 的tcp Socket设置自定义超时时间

    简单的c# TCP通讯(TcpListener) C# 的TCP Socket (同步方式) C# 的TCP Socket (异步方式) C# 的tcp Socket设置自定义超时时间 C# TCP ...

  4. 计算机网络再次整理————tcp周边[八]

    前言 tcp的包的格式可以看我以前的计算机网络整理,下面这些周边只是为了开发时候我们能用到一些理论知识. 正文 首先要介绍的就是域名,为啥有域名这东西呢?单纯站在网络的角度上讲这属于应用层的东西了. ...

  5. [译]再次对比TCP与UDP

    免责声明:和往常一样,此文章的观点都属于‘No Bugs’Hare(译注:一个网站) ,也许不一定和翻译者或者Overload编辑的意见一致.同时,翻译者从Lapine翻译到英语也具有一定的难度.除此 ...

  6. 再回首,Java温故知新(二):Java基本数据类型

    Java作为一种强类型语言,意味着每一个变量都会有特定的类型,Java共有8种基本类型,其中有4种整型(byte.short.int.long).两种浮点型(float.double).1种字符型(c ...

  7. 计算机网络再次整理————tcp例子[五]

    前言 本文介绍一些tcp的例子,然后不断完善一下. 正文 服务端: // See https://aka.ms/new-console-template for more information us ...

  8. 再回首数据结构—AVL树(二)

    前面主要介绍了AVL的基本概念与结构,下面开始详细介绍AVL的实现细节: AVL树实现的关键点 AVL树与二叉搜索树结构类似,但又有些细微的区别,从上面AVL树的介绍我们知道它需要维护其左右节点平衡, ...

  9. 计算机网络再次整理————tcp[二]

    前言 本文不会去介绍tcp的具体协议,因为这个tcp 应该不能说是单纯的连接和传输数据这么简单,里面还有很多机制. 正文 首先介绍一下什么是协议族(protocal Family),举个例子PF_IN ...

随机推荐

  1. 【BZOJ3333】排队计划 树状数组+线段树

    [BZOJ3333]排队计划 Description Input Output Sample Input 6 2 160 163 164 161 167 160 2 3 Sample Output 6 ...

  2. 页游手游服务器(三)lua网络层

    在(一)中,介绍了c对lua层的拓展,这边主要写lua层的网络部分,目标是实现RPC调用,实现主要是三部分: 1通信协议(消息的序列化和反序列化) rpc调用主要信息包括方法名,方法参数,设计的通信协 ...

  3. Spring mvc 与 strust

    1. 机制:spring mvc的入口是servlet,而struts2是filter 2. 性能:spring会稍微比struts快.spring mvc是基于方法,单例(servlet也是单例): ...

  4. Symfony 没有找到数据库驱动An exception occured in driver: could not find driver

    如果一直报这个错误, 第一,你本地没有相关的数据库驱动(mysql:-->pdo_myql,postgresql-->pdo_pgsql等); 需要执行 php -m|grep -i pd ...

  5. KVM虚拟化技术实战全过程

    今天准备开始.................... centos安装-kvm 教程: http://www.linuxidc.com/Linux/2017-01/140007.htm http:// ...

  6. vmware 下的三种网络模式

    VMWare提供三种工作模式桥接(bridge).NAT(网络地址转换)和host-only(主机模式). 桥接模式 在桥接模式下,VMWare虚拟出来的操作系统就像是局域网中的一台独立的主机(主机和 ...

  7. Docker的前世今生

    核心知识点: 1.Docker的构想:对应用的封装.分发.部署.运行的生命周期的管理,一次封装到处运行 2.Docker的优点:一站式解决方案 3.Docker由LXC演变而来,迟迟没有集成到Linu ...

  8. 【六】MongoDB管理之副本集

    一.复制介绍 所谓的复制就是在多个主机之间同步数据的过程. 1.数据冗余及可用性 复制技术提供数据冗余及可用性,在不同的数据库服务器上使用多个数据副本,复制技术防止单个数据库服务器出现数据故障而出现数 ...

  9. P2455 [SDOI2006]线性方程组

    P2455 [SDOI2006]线性方程组 真\(\cdot\)高斯消元模板题 由于各种hack数据被造出来~码量突增~,其实也就多了二三十行 将每行系数消到最多有一个非0数 特殊情况: 在过程同时 ...

  10. tornado源码分析

    初识tornado 首先从经典的helloword案例入手 import tornado.ioloop import tornado.web class MainHandler(tornado.web ...