当前位置: 首页 > wzjs >正文

宿主选择 网站建设企业网站推广优化公司

宿主选择 网站建设,企业网站推广优化公司,南县网站开发,设计一个网站的价格多进程Socket服务端编程详解 一、代码概述 本程序实现了一个多进程的TCP服务端&#xff0c;主进程负责监听客户端连接&#xff0c;子进程处理具体通信。核心类ctcpserver封装了Socket操作&#xff0c;通过信号处理确保资源释放。 代码示例 #include <iostream> #inclu…

多进程Socket服务端编程详解


一、代码概述

本程序实现了一个多进程的TCP服务端,主进程负责监听客户端连接,子进程处理具体通信。核心类ctcpserver封装了Socket操作,通过信号处理确保资源释放。

代码示例

#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>
using namespace std;// 核心类定义
class ctcpserver         // TCP通讯的服务端类。
{// 类的成员变量和方法
};ctcpserver tcpserver;void FathEXIT(int sig);  // 父进程的信号处理函数。
void ChldEXIT(int sig);  // 子进程的信号处理函数。int main(int argc,char *argv[])
{// 主函数流程return 0;
}// 父进程的信号处理函数实现
void FathEXIT(int sig)
{// 函数实现
}// 子进程的信号处理函数实现
void ChldEXIT(int sig)
{// 函数实现
}

二、关键类与函数解析

1. ctcpserver

成员变量
  • m_listenfd:监听Socket(类似总机电话),初始值为 -1 表示未初始化。
  • m_clientfd:客户端Socket(类似分机电话),初始值为 -1 表示客户端未连接。
  • m_clientip:客户端IP(字符串格式)。
  • m_port:服务端端口。
核心方法
  • initserver():初始化服务端Socket(创建→绑定→监听)。
bool initserver(const unsigned short in_port)
{// 第1步:创建服务端的socket。 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_INET。servaddr.sin_port=htons(m_port);            // ②指定服务端的通信端口。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设置为可连接(监听)的状态。if (listen(m_listenfd,5) == -1 ) { close(m_listenfd); m_listenfd=-1; return false;}return true;
}
  • accept():接受客户端连接,保存其IP。
bool 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;
}
  • send()/recv():与客户端通信。
// 向对端发送报文,成功返回true,失败返回false。
bool send(const 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 recv(string &buffer,const size_t maxlen)
{ buffer.clear();         // 清空容器。buffer.resize(maxlen);  // 设置容器的大小为maxlen。int readn=::recv(m_clientfd,&buffer[0],buffer.size(),0);  // 直接操作buffer的内存。if (readn<=0) { buffer.clear(); return false; }buffer.resize(readn);   // 重置buffer的实际大小。return true;
}
  • closelisten()/closeclient():关闭监听/客户端Socket。
// 关闭监听的socket。
bool closelisten()
{if (m_listenfd==-1) return false; ::close(m_listenfd);m_listenfd=-1;return true;
}// 关闭客户端连上来的socket。
bool closeclient()
{if (m_clientfd==-1) return false;::close(m_clientfd);m_clientfd=-1;return true;
}

2. 主函数流程

int main(int argc,char *argv[])
{if (argc!=2){cout << "Using:./demo10 通讯端口\nExample:./demo10 5005\n\n";cout << "注意:运行服务端程序的Linux系统的防火墙必须要开通5005端口。\n";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 2if (tcpserver.initserver(atoi(argv[1]))==false) // 初始化服务端用于监听的socket。{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();  // 父进程关闭客户端连接的socket。continue;                 // 父进程返回到循环开始的位置,继续受理客户端的连接。}tcpserver.closelisten();    // 子进程关闭监听的socket。// 子进程需要重新设置信号。signal(SIGTERM,ChldEXIT);   // 子进程的退出函数与父进程不一样。signal(SIGINT ,SIG_IGN);    // 子进程不需要捕获SIGINT信号。// 子进程负责与客户端进行通讯。cout << "客户端已连接(" << tcpserver.clientip() << ")。\n";string buffer;while (true){// 接收对端的报文,如果对端没有发送报文,recv()函数将阻塞等待。if (tcpserver.recv(buffer,1024)==false){perror("recv()"); break;}cout << "接收:" << buffer << endl;buffer="ok";  if (tcpserver.send(buffer)==false)  // 向对端发送报文。{perror("send"); break;}cout << "发送:" << buffer << endl;}return 0;  // 子进程一定要退出,否则又会回到accept()函数的位置。}
}

三、多进程模型

1. 父进程职责

  • 监听新连接(通过m_listenfd)。
  • 创建子进程处理客户端请求。
  • 关闭客户端Socket:避免资源泄漏。在代码中,父进程在fork()之后调用tcpserver.closeclient()关闭客户端连接的socket。
if (pid>  0) 
{ // 父进程。tcpserver.closeclient();  // 父进程关闭客户端连接的socket。continue;                 // 父进程返回到循环开始的位置,继续受理客户端的连接。
}

2. 子进程职责

  • 处理客户端通信(通过m_clientfd)。
  • 关闭监听Socket:专注当前任务,避免干扰。子进程在fork()之后调用tcpserver.closelisten()关闭监听的socket。
if (pid == 0) 
{tcpserver.closelisten();    // 子进程关闭监听的socket。// 子进程负责与客户端进行通讯
}

3. fork()的底层行为

  • 文件描述符复制:子进程复制父进程的所有Socket。
  • 引用计数机制:每个Socket的引用计数随fork()增加,需父子进程协作关闭。

四、信号处理机制

1. 信号处理逻辑

// 忽略所有信号(防止僵尸进程)
for (int ii = 1; ii <= 64; ii++) signal(ii, SIG_IGN);
// 设置父进程退出信号
signal(SIGTERM, FathEXIT);
signal(SIGINT, FathEXIT);

2. 关键信号

  • SIGTERMkill命令触发的终止信号。
  • SIGINT:Ctrl+C中断信号。
  • SIGCHLD:子进程退出信号(通过忽略自动回收资源)。

3. 信号处理函数

  • FathEXIT:父进程退出前关闭监听Socket,终止所有子进程。
void FathEXIT(int sig)
{// 以下代码是为了防止信号处理函数在执行的过程中再次被信号中断。signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN);cout << "父进程退出,sig=" << sig << endl;kill(0,SIGTERM);     // 向全部的子进程发送15的信号,通知它们退出。// 在这里增加释放资源的代码(全局的资源)。tcpserver.closelisten();       // 父进程关闭监听的socket。exit(0);
}
  • ChldEXIT:子进程退出前关闭客户端Socket。
void ChldEXIT(int sig)
{// 以下代码是为了防止信号处理函数在执行的过程中再次被信号中断。signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN);cout << "子进程" << getpid() << "退出,sig=" << sig << endl;// 在这里增加释放资源的代码(只释放子进程的资源)。tcpserver.closeclient();       // 子进程关闭客户端连上来的socket。exit(0);
}

五、Socket编程细节

1. struct sockaddr_in

  • 作用:存储IPv4地址信息(IP + 端口)。
  • 成员
struct sockaddr_in {sa_family_t    sin_family; // 地址族(AF_INET)in_port_t      sin_port;   // 端口(网络字节序)struct in_addr sin_addr;   // IP地址(二进制形式)char           sin_zero[8];// 填充字段
};

initserver()函数中使用该结构体来绑定服务端的IP和端口。

struct sockaddr_in servaddr;
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(m_port);
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
if (bind(m_listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))==-1)
{ close(m_listenfd); m_listenfd=-1; return false; 
}

2. 初始化Socket的三步曲

  1. 创建Socketsocket(AF_INET, SOCK_STREAM, 0)
if ( (m_listenfd=socket(AF_INET,SOCK_STREAM,0))==-1) return false;
  1. 绑定地址bind()指定IP和端口。
if (bind(m_listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))==-1)
{ close(m_listenfd); m_listenfd=-1; return false; 
}
  1. 开始监听listen()设置队列长度。
if (listen(m_listenfd,5) == -1 ) 
{ close(m_listenfd); m_listenfd=-1; return false;
}

3. accept()的工作原理

  • 阻塞等待:直到有客户端连接。
  • 返回新Socketm_clientfd专用于当前客户端。
if ((m_clientfd=::accept(m_listenfd,(struct sockaddr *)&caddr,&addrlen))==-1) return false;

六、引用计数与资源管理

1. 引用计数机制

  • 定义:内核为每个Socket维护全局引用计数,表示使用该Socket的进程数。
  • 关闭操作close()减少引用计数,归零时内核释放资源。

2. 父子进程的关闭逻辑

进程关闭的Socket目的
父进程m_clientfd避免资源泄漏,专注监听新连接
子进程m_listenfd避免干扰父进程,专注通信任务

3. 资源泄漏的后果

  • 未关闭Socket:端口占用、内存泄漏、僵尸进程。

七、常见问题解答

1. 为何父进程关闭客户端Socket?

  • 资源管理:父进程不处理通信,关闭避免泄漏。
  • 引用计数:父进程关闭后,子进程是唯一持有者,确保最终释放。

2. 为何子进程关闭监听Socket?

  • 职责分离:子进程无需监听新连接,关闭避免干扰父进程。

3. 如何避免僵尸进程?

  • 忽略SIGCHLD信号:内核自动回收子进程资源。在代码中,通过for (int ii = 1; ii <= 64; ii++) signal(ii, SIG_IGN);忽略所有信号,包括SIGCHLD

4. close()shutdown()的区别

  • close():减少引用计数,归零时释放资源。
  • shutdown():强制关闭读写通道,不依赖引用计数。

八、总结与最佳实践

1. 关键知识点

  • 多进程分工:父进程监听,子进程通信。
  • 信号处理:优雅退出和资源回收。
  • Socket引用计数:父子进程协作关闭Socket。

2. 最佳实践

  • 明确关闭职责:父子进程及时关闭不需要的Socket。
  • 使用shutdown():在需要强制关闭时使用。
  • 监控工具netstat检查Socket状态,ps查看进程状态。

http://www.dtcms.com/wzjs/197677.html

相关文章:

  • 太原网站建设谁家好网络服务包括
  • 做那种事的网站厦门网站推广优化哪家好
  • 北京市住房城乡建设部网站首页广告公司的业务范围
  • app制作网站口碑营销的缺点
  • 做司法考试题目的网站韶关seo
  • 2网站建设公司广州seo搜索
  • 张家口网站建设价格软文广告成功案例
  • 如何把网站做跳转浏览器链接地址成都网站优化排名推广
  • 青岛网页制作案例北京做的好的seo公司
  • 国家卫生计生委和能力建设继续网站免费加客源
  • 图片类网站如何做优化百度做个人简介多少钱
  • 动态网站开发实训心得800网络推广的主要内容
  • 企业网站视频栏目建设方案谷歌搜索引擎入口363
  • 网站首页翻转效果什么模块百度一下app下载安装
  • 下载源代码的网站大一html网页制作作业
  • 零基础计算机培训班成人的广州百度推广优化排名
  • wordpress仪表盘定制seo值怎么提高
  • 四川招投标网windows优化大师卸载
  • 互联网创业项目概述百度seo培训班
  • 网站建设推广哪家专业网络广告策划
  • 做网站怎么这么贵荆门刚刚发布的
  • 网站建设背景潍坊新闻头条最新消息
  • 建设企业银行网站多少钱网站建设公司大型
  • 人才网站的seo怎么做app推广怎么联系一手代理
  • 济南建设网站制作天津百度推广排名优化
  • 车票制作图片的软件江门seo推广公司
  • 如何管理个人网站营销型企业网站有哪些平台
  • 深圳比较好的网站设计公司如何做网站推广及优化
  • php做网站项目的流程好网站
  • dede自定义网站地图营销软文范例