C++ 数据结构算法 学习笔记(2)-顺序表 企业级应用案例

C++ 数据结构算法 学习笔记(2) - 顺序表的企业级应用案例

高并发 WEB 服务器中顺序表的应用

高性能的 web 服务器 Squid 每秒可处理上万并发的请求,从网络连接到服务器的客 户端与服务器端在交互时会保持一种会话(和电话通话的场景类似)。服务器端为了管 理好所有的客户端连接,给每个连接都编了一个唯一的整数编号,叫做文件句柄,简称 fd

在这里插入图片描述

为了防止某些恶意连接消耗系统资源,当某个客户端连接超时(在设定的一定时 间内没有发送数据)时,服务器就需要关闭这些客户端的连接

具体实现方案

当有新的请求连到服务器时,如果经过服务器频率限制模块判断,貌似恶意连 接,则使用顺序表来保存此连接的超时数据,超时值使用时间戳来表示,时间戳是指格林 威治时间 1970 年 01 月 01 日 00 时 00 分 00 秒(相当于北京时间 1970 年 01 月 01 日 08 时 00 分 00 秒)起至现在的总秒数,其结构体定义如下:

typedef struct
{
	int fd;
	time_t timeout;

}ConnTimeout;

服务器程序每隔一秒钟扫描一次所有的连接,检查是否超时,如果存在超时的 连接,就关闭连接,结束服务,同时将顺序表中的记录清除!

模拟实战 - 完整代码

10-Web_Server.cpp

#include <iostream>
#include <time.h>
#include <Windows.h>
#include "10-Web_Server.h"

using namespace std;

static void checkTimeouts(TimeoutSqList& list, time_t now);

int main()
{
	time_t now, end;
	time_t last_timeout;
	time(&now);
	TimeoutSqList list;
	end = now + 30;   //exit the while loop while time =30s
	last_timeout = now;

	initList(list);
	// 1. Simulate a frequency limiting module by analyzing and adding malicious links to the sequential list
	for (int i = 0; i < 10; i++)
	{
		ConnTimeout e;
		e.fd = i;
		e.timeout = now + 5 + 2*i;
		listAppend(list, e);
	}

	printList(list);

	do 
	{
		if (last_timeout + 0.999 < now)
		{
			checkTimeouts(list, now);    //Check whether timeout reach
			last_timeout = now;
		}
		Sleep(10);
		time(&now);

	} while (now<end);

	//time_t now;
	//time(&now);
	//cout << "Current time stamp: " << now << endl;
	//Sleep(2000);
	//time(&now);
	//cout << "Current time stamp after 2 seconds : " << now << endl;


	system("pause");
	return 0;
}

void checkTimeouts(TimeoutSqList& list, time_t now)
{
	int fd, i;
	cout << "Checking timeout..." << endl;
	for (i = 0; i < list.length; i++)
	{
		if (list.elems[i].timeout > now)
		{
			continue;
		}
		else
		{
			//Timeout already! Clean the connection
			fd = list.elems[i].fd;
			cout << "The current fd is :" << fd << " Disconnect the fd!" << endl;
			listDelete(list, i);
			i--; //Very important! because the previous i already remove, is dont have i-- then the next i will be skipped! so need to return the previous i!!!
		}

	}
}

10-TimeoutSqlList.cpp

#include <iostream>
#include <string>
#include "10-Web_Server.h"

using namespace std;

bool initList(TimeoutSqList& list)
{
	list.elems = new ConnTimeout[MAX_SIZE];
	if (!list.elems)
	{
		return false;
	}

	list.length = 0;
	list.size = MAX_SIZE;
	return true;
}

bool printList(TimeoutSqList& list)
{
	cout << "The length of the STAR list is " << list.length << " and its size is: " << list.size << endl;
	for (int i = 0; i < list.length; i++)
	{
		cout << "The " << list.elems[i].fd << " timestamp is :" << list.elems[i].timeout << endl;
	}
	return true;
}

bool listAppend(TimeoutSqList& list, ConnTimeout timeout)
{
	if (list.length < list.size)
	{

		list.elems[list.length] = timeout;
		list.length++;
		return true;
	}
	else
	{
		cout << "The element is not able to append inside the list" << endl;
		return false;
	}
}


bool listDelete(TimeoutSqList& l, int num)
{
	if (num<0 || num>l.length)
	{
		return false;
	}
	if (num == l.length - 1)
	{
		l.length--;
		return true;
	}
	else
	{
		for (int i = num; i < l.length - 1; i++)
		{
			l.elems[i] = l.elems[i + 1];

		}
		l.length--;
		return true;

	}
}

void listRemove(TimeoutSqList& l)
{
	if (l.elems)
	{
		delete l.elems;
		l.length = 0;
		l.size = 0;
	}
}

10-Web_Server.h

#ifndef _WEB_SERVER_H_
#define _WEB_SERVER_H_

#include <time.h>
#define MAX_SIZE	1000

typedef struct
{
	int fd;
	time_t timeout;

}ConnTimeout;

typedef struct
{
	ConnTimeout* elems;
	int length;
	int size;

}TimeoutSqList;

bool initList(TimeoutSqList& L);
bool listAppend(TimeoutSqList& L, ConnTimeout e);
bool listDelete(TimeoutSqList& L, int i);
void listRemove(TimeoutSqList& L);
bool printList(TimeoutSqList& L);

#endif

上面的代码需要注意的事项如下:

  1. 在 "10-Web_Server.cpp"的代码里 (第31行),

    	do 
    	{
    		if (last_timeout + 0.999 < now)
    		{
    			checkTimeouts(list, now);    //Check whether timeout reach
    			last_timeout = now;
    		}
    		Sleep(10);
    		time(&now);
    
    	} while (now<end);
    
    

这里面的 last_timeout 可以直接+0.999,以便让程序知道每1秒运行此程序,而不是直接使用Sleep(1000).

为什么不使用Sleep(1000)反而直接用 last-timeout +0.999 呢?

答: Sleep(1000)会让程序什么都不做,等1秒. 这样的话会造成程序运行效率极低!!! 所以使用last-timeout +0.999取代. 这个方式尤其在单片机上有很大的用途(比如单片机每一秒需要执行某种实现)

  1. 在 "10-Web_Server.cpp"的代码里 (第64行)

    else
    {
    	//Timeout already! Clean the connection
    	fd = list.elems[i].fd;
    	cout << "The current fd is :" << fd << " Disconnect the fd!" << endl;
    	listDelete(list, i);
    	i--; //Very important! because the previous i already remove, is dont have i-- then the next i will be skipped! so need to return the previous i!!!
    }
    

    这里面的代码 i–尤为重要!!! 原因是当我们找到了一个超时的fd时,我们的判断会清除那个连接.

    比如说i=3fd超时了,程序会清除它. 当清除后,我们的listDelete()函数会将顺序表里的元素往上移, 这样的话当 elems[3]被清除的时候 i++ 会直接让程序判断 elems[5]而不是 elems[4] (原因是虽然 i=4, 但elems[4]已被往上移一个位置).

    这个的后果会导致 elems[4]被跳过了! 所以为了避免这个原因 需要在程序上加上 i–!!!

  2. 在 "10-Web_Server.cpp"的代码里 (第8行)里为什么这个函数要加上 static?

    static void checkTimeouts(TimeoutSqList& list, time_t now);
    

    在C++中,关键字static 有多种用法,具体取决于它的使用上下文。在函数声明前使用static时,它指的是该函数有文件作用域。这意味着这个函数只能在定义它的文件内部被访问,即它是局部于文件的

    这个的用意是:

    • 可以在不同的文件中有相同名字的函数,而这些函数互不干扰
    • 在大型项目中,特别是当多个开发者工作在相同的代码库上时,限制函数的作用域到单个文件可以减少命名冲突的可能性
    • 编译器可以对静态函数进行更好的优化。因为编译器知道函数只在一个文件内使用,它可以更容易地分析和优化这个函数的调用,有时甚至可以内联这个函数,以去除函数调用的开销
  • 27
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值