首页>>文章资讯>>主机推荐

Linux内核编程,高并发服务器的处理指标及指标

2024-03-23 04:00:00 196

前言

本文主要学习Linux内核编程,结合 2019进行跨平台编程,内容包括高并发服务器的介绍、服务器代码封装(+epoll)、服务器/客户端测试

一、高并发服务器

器服务收藏_服务器_服务器

什么是高并发?

高并发 是一种系统运行过程中遇到的一种 “短时间内遇到大量操作请求” 的情况

【主要发生在web系统集中大量访问收到大量请求】

举个例子:12306的抢票情况;天猫双十一活动【突然下单一万张票,上万人下单购物】

该情况发生会导致系统在这段时间内执行大量操作,例如,对资源的请求,数据库的操作等

高并发的处理指标?

高并发相关常用的一些指标有:

1️响应时间( Time)

含义:系统对请求做出响应的时间

举个例子:系统处理一个HTTP请求需要200ms,这个200ms就是系统的响应时间

2️吞吐量()

含义:单位时间内处理的请求数量

3️每秒查询率QPS(Query Per )

含义 :每秒响应请求数

在互联网领域,这个指标和吞吐量区分的没有这么明显

4️并发用户数

含义:同时承载正常使用系统功能的用户数量

举个例子:例如一个即时通讯系统,同时在线量一定程度上代表了系统的并发用户数

高并发和多线程的关系和区别?

“高并发和多线程” 总是被一起提起,给人感觉两者好像相等,实则 【高并发 ≠ 多线程】

1️多线程

2️高并发

需要C/C++ Linux服务器架构师学习资料加获取(资料包括C/C++,Linux,技术,Nginx,,MySQL,Redis,,,ZK,流媒体,CDN,P2P,K8S,,TCP/IP,协程,DPDK,等),免费分享

器服务收藏_服务器_服务器

二、搭建服务器/客户端

服务器代码(4种类的封装)

地址类【】

.h

.cpp

#include "CHostAddress.h"

CHostAddress::CHostAddress(char* ip, unsigned short port)
{
memset(this->ip, 0, sizeof(this->ip));
strcpy(this->ip, ip);

this->port = port;

this->s_addr.sin_family = AF_INET;
this->s_addr.sin_port = htons(this->port);
this->s_addr.sin_addr.s_addr = inet_addr(this->ip);

this->length = sizeof(this->s_addr);
}

CHostAddress::~CHostAddress()
{

}

char* CHostAddress::getIp()
{
return this->ip;
}

void CHostAddress::setIp(char* ip)
{
strcpy(this->ip, ip);
}

unsigned short CHostAddress::getPort()
{
return this->port;
}

void CHostAddress::setPort(unsigned short port)
{
this->port = port;
}

sockaddr_in CHostAddress::getAddr_in()
{
return this->s_addr;
}

sockaddr* CHostAddress::getAddr()
{
// bind函数需要用到struct sockaddr *,因此return类型转换之后数据
return (struct sockaddr*)&(this->s_addr);
}

int CHostAddress::getLength()
{
return this->length;
}

类【】

.h

#pragma once

#include //socket头文件
#include //socket头文件
#include
#include

class CBaseSocket
{
public:
CBaseSocket(char* ip, unsigned short port);
~CBaseSocket();
void Start();
int getSocketFd();
virtual void Run() = 0;//写成纯虚函数,子类来实现
virtual void Stop() = 0;//写成纯虚函数,子类来实现

protected:
int socketFd;//写到受保护区,子类可以用到

};

.cpp

#include "CBaseSocket.h"

CBaseSocket::CBaseSocket(char* ip, unsigned short port)
{
this->socketFd = 0;
}

CBaseSocket::~CBaseSocket()
{
}

void CBaseSocket::Start()
{
//打通网络通道
this->socketFd = socket(AF_INET, SOCK_STREAM, 0);//IPPROTO_TCP用0替换也行

if (this->socketFd < 0)//大于0成功,小于0失败
{
perror("socket error");//socket创建失败
}

this->Run();//子类实现的run函数
}

int CBaseSocket::getSocketFd()
{
return this->socketFd;
}

TCP类【】

.h

#pragma once

#include
#include "CBaseSocket.h"
#include "CHostAddress.h"
#include
#include
#include
#include /* See NOTES */
#include

using namespace std;

#define LISTEN_MAX_NUM 10

class CTcpServer :
public CBaseSocket
{
public:
CTcpServer(char* ip, unsigned short port);
~CTcpServer();
void Run();
void Stop();
CHostAddress* getAddress();
void setAddress(CHostAddress* address);

private:
CHostAddress* address;//地址类
};

.cpp

#include "CTcpSever.h"

CTcpServer::CTcpServer(char* ip, unsigned short port)
:CBaseSocket(ip, port)
{
this->address = new CHostAddress(ip, port);
}

CTcpServer::~CTcpServer()
{
}

void CTcpServer::Run()
{
int opt_val = 1;
int res = 0;

//端口复用 解决出现 adress already use的问题
res = setsockopt(this->socketFd, SOL_SOCKET, SO_REUSEADDR, (const void*)&opt_val, sizeof(opt_val));
if (res == -1)
{
perror("setsockopt error");
}
//绑定端口号和地址 协议族
res = bind(this->socketFd, this->address->getAddr(), this->address->getLength());
if (res == -1)
{
perror("bind error");
}
//监听这个地址和端口有没有客户端来连接
res = listen(this->socketFd, LISTEN_MAX_NUM);
if (res == -1)
{
perror("listen error");
}
cout << "Server start success socketFd = " << this->socketFd << endl;
}

void CTcpServer::Stop()
{
if (this->socketFd != 0)
{
close(this->socketFd);
this->socketFd = 0;
}
}

CHostAddress* CTcpServer::getAddress()
{
return this->address;
}

void CTcpServer::setAddress(CHostAddress* address)
{
this->address = address;
}

epoll类 【】

.h

#pragma once
#include
#include
#include "CTcpSever.h"

#define EPOLL_SIZE 5

using namespace std;

class CEpollServer
{
public:
CEpollServer(char* ip, unsigned short port);
~CEpollServer();
void Start();

private:
int epollfd;
int epollwaitefd;
int acceptFd;
char buf[1024]; //存放客户端发来的消息
struct epoll_event epollEvent;
struct epoll_event epollEventArray[5];
CTcpServer* tcp;//TCP类

};

.cpp

#include "CEpollServer.h"

CEpollServer::CEpollServer(char* ip, unsigned short port)
{
//初始化 TcpServer类
this->tcp = new CTcpServer(ip, port);
this->tcp->Start();
cout << "socketFd = " << this->tcp->getSocketFd() << endl;

//初始化数据成员
this->epollfd = 0;
this->epollwaitefd = 0;
this->acceptFd = 0;
bzero(this->buf, sizeof(this, buf));

//事件结构体初始化
bzero(&(this->epollEvent), sizeof(this->epollEvent));
//绑定当前准备好的sockedfd(可用网络对象)
this->epollEvent.data.fd = this->tcp->getSocketFd();
//绑定事件为客户端接入事件
this->epollEvent.events = EPOLLIN;
//创建epoll
this->epollfd = epoll_create(EPOLL_SIZE);
//将已经准备好的网络描述符添加到epoll事件队列中
epoll_ctl(this->epollfd, EPOLL_CTL_ADD, this->tcp->getSocketFd(), &(this->epollEvent));
}

CEpollServer::~CEpollServer()
{
}

void CEpollServer::Start()
{
while (1)
{
cout << "epoll wait client" << endl;
this->epollwaitefd = epoll_wait(this->epollfd, epollEventArray, EPOLL_SIZE, -1);
if (this->epollwaitefd < 0)
{
perror("epoll wait error");
}
for (int i = 0; i < this->epollwaitefd; i++)
{
//判断是否有客户端上线
if (epollEventArray[i].data.fd == this->tcp->getSocketFd())
{
cout << "网络_开始工作_等待客户端_上线" << endl;
this->acceptFd = accept(this->tcp->getSocketFd(), NULL, NULL);
cout << "acceptfd = " << this->acceptFd << endl;

//上线的客户端描述符是acceptfd 绑定事件添加到epoll
epollEvent.data.fd = this->acceptFd;
epollEvent.events = EPOLLIN; //EPOLLIN表示对应的文件描述符可以读
epoll_ctl(this->epollfd, EPOLL_CTL_ADD, this->acceptFd, &epollEvent);
}
else if (epollEventArray[i].events & EPOLLIN)
{
bzero(this->buf, sizeof(this->buf));
int res = read(epollEventArray[i].data.fd, this->buf, sizeof(this->buf));
if (res > 0)
{
cout << "服务器_收到 fd = " << epollEventArray[i].data.fd << " 送达数据: buf = " << this->buf << endl;
}
else if (res <= 0)
{
cout << "客户端 fd = " << epollEventArray[i].data.fd << " _掉线_" << endl;
close(epollEventArray[i].data.fd);

//从epoll中删除客户端描述符
epollEvent.data.fd = epollEvent.data.fd;
epollEvent.events = EPOLLIN;
epoll_ctl(this->epollfd, EPOLL_CTL_DEL, epollEventArray[i].data.fd, &epollEvent);
}
}
}
}
}

客户端代码

#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;

int main()
{
int socketfd = 0;
int acceptfd = 0;
int len = 0;
int res = 0;
char buf[255] = { 0 };//初始化

//初始化网络
socketfd = socket(AF_INET, SOCK_STREAM, 0);
if (socketfd == -1)
{
perror("socket error");
}
else
{
struct sockaddr_in s_addr;
//确定使用哪个协议族 ipv4
s_addr.sin_family = AF_INET;

//填入服务器的ip地址 也可以是 127.0.0.1 (回环地址)
s_addr.sin_addr.s_addr = inet_addr("192.168.48.129");

//端口一个计算机有65535个 10000以下是操作系统自己使用的,自己定义的端口号为10000以后
s_addr.sin_port = htons(12345); //自定义端口号为12345

len = sizeof(s_addr);

//绑定ip地址和端口号
int res = connect(socketfd, (struct sockaddr*)&s_addr, len);
if (res == -1)
{
perror("connect error");
}
else
{
while (1)
{
cout << "请输入内容:" << endl;
cin >> buf;
write(socketfd, buf, sizeof(buf));
bzero(buf, sizeof(buf));
}
}
}
return 0;
}

案例测试

main.cpp

#include
#include "CEpollServer.h"

using namespace std;

int main()
{
CEpollServer* epoll = new CEpollServer("192.168.48.129", 12345);
epoll->Start();
return 0;
}

测试效果

通过Linux连接VS进行跨平台编程,上为本文设计的服务器,下为两个与之相连的客户端,在客户端1和客户端2中输入内容,服务器上能接收到相应的信息,即表示测试成功!如下图所示:

服务器_器服务收藏_服务器