首页 > 编程语言 > 详细

一个简单的TCP服务器(C++)的设计

时间:2021-02-17 15:33:56      阅读:31      评论:0      收藏:0      [点我收藏+]

一:

  C++服务器的开发中有比较多现成的框架,比如游戏服务器中常用的skynet。但是在为一些智能门锁,或者些小批量的电子产品(几千台到2万台左右)的话,我们可以自己设计个简单的服务器,不需要拿现成的框架来做(毕竟熟悉一个框架得要比较长的时间)。

二:

  下面是简易TCP服务器设计的要点:

  (1)网络报文的数据格式

    首先得协调好双方(客户端和接收端)的通信的数据结构,如果客户端是C/C++实现的类型,那么通信的双方(客户端和接收端)数据类型可以用结构体;但是现在多数的终端可能是java或者android实现的,一般的话可以定义这样的数据类型:

    [HNYY5G123456789,23,GPS...]

    // 一个数据的开头用上“ [ ” ,结尾用上“ ] ”   

    //HNYY5G是表示公司名称缩写

    //123456789,这个表示设备的ID,设备ID的获取可以是IMEI号或者别的

    //23表示这个当前数据后面对应的数据的长度

 

    这样的话,客户端发送的数据就按定义上面的定义;服务器(C++)要识别到一个完整的数据的话,就用 ” [ “  和   ” ]  “,这个可以用到函数split。

  (2)IO多路复用机制的选择

    select,poll,epoll这3个的选择。

    在linux下,毫无疑问就是使用epoll;(也要设置为非阻塞)

    在windows   server  2008上,可以使用select;(也要设置为非阻塞)

  (3)网络粘包的解决

    为啥会产生网络的粘包?

    在网络发送端socket有个发送缓冲区,这个缓冲区不是一有数据过来就调用send函数,并执行这个函数把数据发送到服务端的,而是等到缓冲区满或者到一定的时间点才发送的。接收端对应的socket也有个缓冲区,这个缓冲区也不大(好像是几十K吧),而我们接收端就使用recv函数来取出数据。而当服务器的数据发送太过频繁的话,但是接收端使用recv(这个接收数据是从缓冲区取出数据的)每次取出的数据量比较小的话,比如执行一次recv才取出几K的话,这时候就会出现缓冲区数据的溢出。这样的话也出现所说的粘包问题。

    解决的方法就是recv一次取出的数据差不多和接收缓冲区的大小一样。如下的代码:

    char     recvbuf[4096*10];

    int   ret=recv(fd, recvbuf,sizeof(recvbuf), 0) ;     

    //ret返回接收到数据的个数,fd表示socket

  (4)内存池

    为啥要有内存池?

    因为服务器是要长期的运行的,程序在运行的过程中难免产生内存碎片,久而久之,会使内存的可使用空间变小,因此为了服务器的高可用性。我们得设计个简单的内存池,程序变量所需要的空间都从这个设计的内存池里面分配,当变量的生命周期结束的时候,就将这块内存的数据清除,回收。

    而对于C++11,由于引入了智能指针,对于对象的管理,可以直接使用智能指针来管理,这时候设计服务器程序可以不使用内存池。

  (5)线程池

    引入线程池,是在服务器程序启动时,就有已经分配好的线程,当需要线程的时候,就从线程池里面分配取出线程,这样的话,就避免频繁创建和销毁线程带来系统的开销。

  (6)心跳检测

    为啥要引入心跳检测?

    服务器在管理多个客户端的时候,当某些客户端长期不发送数据,或者掉线了。服务还在维护这些客户端,这样的话会造成性能的下降(每执行一次select或者epoll都要检测这些已经掉线的客户端)和资源的浪费,这样的话就得引入心跳检测。心跳检测就是检测客户端在一定的时间内是否有数据到达,或者规定的心跳数据类型到达(比如 :[HNYY5G123456789,2,LK] ,LK就是规定的心跳数据标识),没有过的话就的把该客户端从服务器管理的网络IO(也就是客户端的socket)中删除。

  (7)日志系统

    服务器程序的运行,难免会产生些不可以避免的问题。而排查这些问题,得用到这些错误的信息,因此服务器得有个日志系统,也就是打印log的类。

    服务器并不只是单进程或者单线程的,打印log的类得要单例模式来实现,还得在程序跑起来之前这个类就要生成了。

  (8)性能的检测

    注:发送和接收这两个函数的性能是不对等的。如下是我机器上的数据:

    send这个函数发送的上线是每秒发送130W左右的数据包;

    recv这个函数接收的上线是每秒可以接收430W个数据包;

    如何模拟多个客户端的连接?因为服务器要在真实的场景跑起来的话,是有大量的客户端连接的。客户端这边的话,用一个类来实现,然后定义一个类的数组,这个数组的连接,发送,接收数据放在一个死循环体内while(1) {   .......  },这样就模拟了多个客户端数据的发送的测试。

    同样的,模拟大数据的发送,就把一个数据的内容变得比较大  (几十K),同样服务端的数据类型也更改,这样就能完成大额数据的发送,来测试服务器的性能。

一个简单的TCP服务器(C++)的设计

原文:https://www.cnblogs.com/Unclebigdata/p/14408875.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!