您的位置:首页 > 娱乐 > 八卦 > 互联网站产品开发的流程_厦门seo关键词优化代运营_网站排名推广工具_指数函数图像

互联网站产品开发的流程_厦门seo关键词优化代运营_网站排名推广工具_指数函数图像

2025/3/6 11:21:29 来源:https://blog.csdn.net/FHKHH/article/details/145889767  浏览:    关键词:互联网站产品开发的流程_厦门seo关键词优化代运营_网站排名推广工具_指数函数图像
互联网站产品开发的流程_厦门seo关键词优化代运营_网站排名推广工具_指数函数图像

多进程网络服务端详细说明文档

一、概述

本项目实现了一个基于多进程的 TCP 网络服务端,主要用于处理多个客户端的连接请求。为了提高代码的可维护性和可复用性,分成了头文件(.h)和多个源文件(.cpp)。具体包含 ctcpserver.hctcpserver.cppmulti_process_network_server.cpp 三个文件。

二、文件结构及功能
1. ctcpserver.h
  • 功能:该头文件主要用于声明 ctcpserver 类,定义了类的成员变量和成员函数接口,起到了对类的功能进行抽象和声明的作用,方便其他源文件引用该类。
  • 关键内容
    • 成员变量
      • m_listenfd:监听的 socket 描述符,-1 表示未初始化。
      • m_clientfd:客户端连接的 socket 描述符,-1 表示客户端未连接。
      • m_clientip:客户端的 IP 地址,以字符串形式存储。
      • m_port:服务端用于通信的端口号。
    • 成员函数声明:包含了构造函数、析构函数以及一系列用于初始化服务端、接受客户端连接、发送和接收数据、关闭 socket 等操作的函数声明。
class ctcpserver {
private:int m_listenfd;int m_clientfd;std::string m_clientip;unsigned short m_port;
public:ctcpserver();~ctcpserver();bool initserver(const unsigned short in_port);bool accept();const std::string & clientip() const;bool send(const std::string &buffer);bool recv(std::string &buffer, const size_t maxlen);bool closelisten();bool closeclient();
};
2. ctcpserver.cpp
  • 功能:该源文件实现了 ctcpserver.h 中声明的 ctcpserver 类的所有成员函数,是类的具体实现部分。
  • 关键函数及实现细节
    • 构造函数 ctcpserver::ctcpserver():初始化 m_listenfdm_clientfd-1,表示初始状态下监听 socket 和客户端 socket 未初始化。
    • 析构函数 ctcpserver::~ctcpserver():调用 closelisten()closeclient() 函数,确保在对象销毁时关闭监听 socket 和客户端 socket,释放资源。
    • initserver 函数
      • 使用 socket() 函数创建一个 TCP 套接字,使用 IPv4 协议(AF_INET)和面向连接的 TCP 协议(SOCK_STREAM)。
      • 填充 sockaddr_in 结构体,设置协议族、端口号(使用 htons() 进行字节序转换)和 IP 地址(INADDR_ANY 表示监听所有可用 IP)。
      • 使用 bind() 函数将套接字绑定到指定的 IP 和端口,若绑定失败则关闭套接字并返回 false
      • 使用 listen() 函数将套接字设置为监听状态,允许最多 5 个客户端连接请求进入队列,若设置失败则关闭套接字并返回 false
    • accept 函数
      • 使用 accept() 函数从监听队列中取出一个客户端连接请求,返回一个新的套接字描述符用于与该客户端通信。
      • 使用 inet_ntoa() 函数将客户端的 IP 地址从大端序转换为字符串格式并存储在 m_clientip 中。
    • send 函数:检查客户端 socket 是否有效,若有效则使用 send() 函数向客户端发送数据。
    • recv 函数
      • 清空接收缓冲区并调整其大小为指定的最大长度。
      • 使用 recv() 函数接收客户端发送的数据,若接收失败则清空缓冲区并返回 false,否则根据实际接收的字节数调整缓冲区大小并返回 true
    • closelisten 函数:关闭监听 socket 并将 m_listenfd 设置为 -1
    • closeclient 函数:关闭客户端 socket 并将 m_clientfd 设置为 -1
3. multi_process_network_server.cpp
  • 功能:该文件是整个服务端程序的入口,包含 main 函数和信号处理函数,负责初始化服务端、处理客户端连接和信号处理。
  • 关键流程及函数说明
    • main 函数
      • 参数检查:检查命令行参数是否正确,若不正确则输出使用说明并退出程序。
      • 信号处理设置
        • 使用 for 循环忽略所有信号,避免程序被不必要的信号干扰,同时解决僵尸进程问题。
        • 设置 SIGTERMSIGINT 信号的处理函数为 FathEXIT,允许通过 kill 命令或 Ctrl + C 正常终止程序。
      • 服务端初始化:调用 tcpserver.initserver() 函数初始化服务端监听 socket,若初始化失败则输出错误信息并退出。
      • 客户端连接处理
        • 使用 while 循环不断接受客户端连接请求,若接受失败则输出错误信息并退出。
        • 调用 fork() 函数创建子进程,父进程关闭客户端 socket 并继续等待下一个客户端连接;子进程关闭监听 socket,重新设置信号处理函数,并与客户端进行通信。
      • 子进程通信处理
        • 循环接收客户端发送的数据,若接收失败则退出循环。
        • 接收到数据后,向客户端发送 “ok” 作为响应,若发送失败则退出循环。
    • 信号处理函数
      • FathEXIT 函数:处理父进程接收到的 SIGTERMSIGINT 信号。首先屏蔽这两个信号,防止信号处理函数被中断;输出父进程退出信息,使用 kill(0, SIGTERM) 向所有子进程发送终止信号;关闭监听 socket 并退出父进程。
      • ChldEXIT 函数:处理子进程接收到的 SIGTERM 信号。屏蔽 SIGINTSIGTERM 信号,输出子进程退出信息,关闭客户端 socket 并退出子进程。
四、注意事项
  • 资源管理:每个子进程在处理完与客户端的通信后必须退出,否则会继续进入 accept 函数,导致错误。同时,要确保在程序结束时正确关闭所有 socket 资源,避免资源泄漏。
  • 信号处理:信号处理函数中屏蔽了 SIGINTSIGTERM 信号,防止信号处理函数被中断,确保程序的稳定性。
  • 高并发场景:该程序使用多进程处理客户端连接,每个客户端连接会创建一个新的子进程,可能会消耗较多的系统资源。在高并发场景下,可以考虑使用多线程或异步 I/O 技术进行优化。

1. ctcpserver.h 头文件

#ifndef CTCP_SERVER_H
#define CTCP_SERVER_H#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>class ctcpserver {
private:int m_listenfd;        // 监听的socket,-1表示未初始化int m_clientfd;        // 客户端连上来的socket,-1表示客户端未连接std::string m_clientip;     // 客户端字符串格式的IPunsigned short m_port; // 服务端用于通讯的端口
public:ctcpserver();~ctcpserver();// 初始化服务端用于监听的socketbool initserver(const unsigned short in_port);// 受理客户端的连接bool accept();// 获取客户端的IP(字符串格式)const std::string & clientip() const;// 向对端发送报文bool send(const std::string &buffer);// 接收对端的报文bool recv(std::string &buffer, const size_t maxlen);// 关闭监听的socketbool closelisten();// 关闭客户端连上来的socketbool closeclient();
};#endif

2. ctcpserver.cpp 源文件

#include "ctcpserver.h"// 构造函数,初始化监听socket和客户端socket为未初始化状态
ctcpserver::ctcpserver() : m_listenfd(-1), m_clientfd(-1) {}// 析构函数,关闭监听socket和客户端socket
ctcpserver::~ctcpserver() {closelisten();closeclient();
}// 初始化服务端用于监听的socket
bool ctcpserver::initserver(const unsigned short in_port) {// 第1步:创建服务端的socket// AF_INET表示使用IPv4协议,SOCK_STREAM表示使用面向连接的TCP协议if ((m_listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) return false;m_port = in_port;// 第2步:把服务端用于通信的IP和端口绑定到socket上struct sockaddr_in servaddr;  // 用于存放协议、端口和IP地址的结构体memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;  // 协议族,固定填AF_INETservaddr.sin_port = htons(m_port);  // 指定服务端的通信端口,htons用于将主机字节序转换为网络字节序servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  // 如果操作系统有多个IP,全部的IP都可以用于通讯// 绑定服务端的IP和端口(为socket分配IP和端口)if (bind(m_listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {close(m_listenfd);m_listenfd = -1;return false;}// 第3步:把socket设置为可连接(监听)的状态// 5表示监听队列的最大长度if (listen(m_listenfd, 5) == -1) {close(m_listenfd);m_listenfd = -1;return false;}return true;
}// 受理客户端的连接(从已连接的客户端中取出一个客户端)
// 如果没有已连接的客户端,accept()函数将阻塞等待
bool ctcpserver::accept() {struct sockaddr_in caddr;  // 客户端的地址信息socklen_t addrlen = sizeof(caddr);  // struct sockaddr_in的大小// 从监听队列中取出一个客户端连接if ((m_clientfd = ::accept(m_listenfd, (struct sockaddr *)&caddr, &addrlen)) == -1) return false;m_clientip = inet_ntoa(caddr.sin_addr);  // 把客户端的地址从大端序转换成字符串return true;
}// 获取客户端的IP(字符串格式)
const std::string & ctcpserver::clientip() const {return m_clientip;
}// 向对端发送报文,成功返回true,失败返回false
bool ctcpserver::send(const std::string &buffer) {if (m_clientfd == -1) return false;// 发送报文if ((::send(m_clientfd, buffer.data(), buffer.size(), 0)) <= 0) return false;return true;
}// 接收对端的报文,成功返回true,失败返回false
// buffer - 存放接收到的报文的内容,maxlen - 本次接收报文的最大长度
bool ctcpserver::recv(std::string &buffer, const size_t maxlen) {buffer.clear();  // 清空容器buffer.resize(maxlen);  // 设置容器的大小为maxlen// 直接操作buffer的内存接收数据int readn = ::recv(m_clientfd, &buffer[0], buffer.size(), 0);if (readn <= 0) {buffer.clear();return false;}buffer.resize(readn);  // 重置buffer的实际大小return true;
}// 关闭监听的socket
bool ctcpserver::closelisten() {if (m_listenfd == -1) return false;::close(m_listenfd);m_listenfd = -1;return true;
}// 关闭客户端连上来的socket
bool ctcpserver::closeclient() {if (m_clientfd == -1) return false;::close(m_clientfd);m_clientfd = -1;return true;
}

3. multi_process_network_server.cpp 主源文件

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <netdb.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "ctcpserver.h"ctcpserver tcpserver;// 父进程的信号处理函数
void FathEXIT(int sig);
// 子进程的信号处理函数
void ChldEXIT(int sig);int main(int argc, char *argv[]) {// 检查命令行参数if (argc != 2) {std::cout << "Using:./multi_process_network_server 通讯端口\nExample:./multi_process_network_server 5005\n\n";std::cout << "注意:运行服务端程序的Linux系统的防火墙必须要开通5005端口。\n";std::cout << "      如果是云服务器,还要开通云平台的访问策略。\n\n";return -1;}// 忽略全部的信号,不希望被打扰。顺便解决了僵尸进程的问题for (int ii = 1; ii <= 64; ii++) signal(ii, SIG_IGN);// 设置信号,在shell状态下可用 "kill 进程号" 或 "Ctrl+c" 正常终止些进程// 但请不要用 "kill -9 +进程号" 强行终止signal(SIGTERM, FathEXIT);signal(SIGINT, FathEXIT);  // SIGTERM 15 SIGINT 2// 初始化服务端用于监听的socketif (tcpserver.initserver(atoi(argv[1])) == false) {perror("initserver()");return -1;}while (true) {// 受理客户端的连接(从已连接的客户端中取出一个客户端)// 如果没有已连接的客户端,accept()函数将阻塞等待if (tcpserver.accept() == false) {perror("accept()");return -1;}// 创建子进程处理客户端连接int pid = fork();if (pid == -1) {perror("fork()");return -1;}  // 系统资源不足if (pid > 0) {// 父进程tcpserver.closeclient();  // 父进程关闭客户端连接的socketcontinue;  // 父进程返回到循环开始的位置,继续受理客户端的连接}// 子进程tcpserver.closelisten();  // 子进程关闭监听的socket// 子进程需要重新设置信号signal(SIGTERM, ChldEXIT);  // 子进程的退出函数与父进程不一样signal(SIGINT, SIG_IGN);  // 子进程不需要捕获SIGINT信号// 子进程负责与客户端进行通讯std::cout << "客户端已连接(" << tcpserver.clientip() << ")。\n";std::string buffer;while (true) {// 接收对端的报文,如果对端没有发送报文,recv()函数将阻塞等待if (tcpserver.recv(buffer, 1024) == false) {perror("recv()");break;}std::cout << "接收:" << buffer << std::endl;buffer = "ok";if (tcpserver.send(buffer) == false) {  // 向对端发送报文perror("send");break;}std::cout << "发送:" << buffer << std::endl;}return 0;  // 子进程一定要退出,否则又会回到accept()函数的位置}
}// 父进程的信号处理函数
void FathEXIT(int sig) {// 以下代码是为了防止信号处理函数在执行的过程中再次被信号中断signal(SIGINT, SIG_IGN);signal(SIGTERM, SIG_IGN);std::cout << "父进程退出,sig=" << sig << std::endl;kill(0, SIGTERM);  // 向全部的子进程发送15的信号,通知它们退出// 在这里增加释放资源的代码(全局的资源)tcpserver.closelisten();  // 父进程关闭监听的socketexit(0);
}// 子进程的信号处理函数
void ChldEXIT(int sig) {// 以下代码是为了防止信号处理函数在执行的过程中再次被信号中断signal(SIGINT, SIG_IGN);signal(SIGTERM, SIG_IGN);std::cout << "子进程" << getpid() << "退出,sig=" << sig << std::endl;// 在这里增加释放资源的代码(只释放子进程的资源)tcpserver.closeclient();  // 子进程关闭客户端连上来的socketexit(0);
}

代码说明

  • ctcpserver.h:定义了 ctcpserver 类的接口,包括类的成员变量和成员函数的声明。使用了预处理器指令 #ifndef#define#endif 来防止头文件被重复包含。
  • ctcpserver.cpp:实现了 ctcpserver 类的成员函数,包括构造函数、析构函数以及各种操作函数。
  • multi_process_network_server.cpp:主源文件,包含了 main 函数和信号处理函数,使用 ctcpserver 类来实现多进程的网络服务端。

编译和运行

将上述三个文件放在同一目录下,使用以下命令进行编译:

g++ -o multi_process_network_server ctcpserver.cpp multi_process_network_server.cpp

编译成功后,使用以下命令运行服务端程序:

./multi_process_network_server 5005

其中 5005 是服务端监听的端口号,你可以根据需要修改。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com