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

多进程并发服务器

文章目录

    • 思路
    • 问题
    • 多进程并发回环服务器代码
    • 客户端代码

思路

每当一个客户端连接服务器后,创建一个子进程负责与该客户端通信,客户端断开连接之后,服务器回收子进程资源。

问题

问题1:父进程阻塞在等待连接(accept())处,不能在父进程回收资源,可以使用信号SIGCHLD进行软中断回调处理,当子进程结束后会产生SIGCHLD信号,信号触发回调函数,进程子进程资源回收,父进程阻塞在accept()函数时遇到软中断就会产生EINTR错误信号,这就需要处理accept函数返回值为-1时的错误,判断错误号errno,若errno == EINTR则继续等待客户端连接进入accept阻塞。

问题2:由于多个进程同时退出时也仅有一个SIGCHLD信号,所以在有SIGCHLD信号时,回调函数内就要循环执行释放子进程资源,直到子进程资源释放完成。

信号的注册,以及回调函数的编写:

//子进程回收回调函数
void recvChild(int arg)
{while(1){int ret = waitpid(-1, NULL, WNOHANG);if(ret > 0){printf("recv child, the num is:%d\n", ret);}else if(ret == 0){//还有子进程}else if(ret == -1){//没有子进程了break;}}
}//注册信号,解决子进程的回收问题struct sigaction act;act.sa_flags = 0;sigemptyset(&act.sa_mask);act.sa_handler = recvChild;sigaction(SIGCHLD, &act, NULL);

问题3:在使用客户端发送数据的时候,是先使用write发送数据,然后通过read读取数据,该读取是阻塞的,所以一开始没有数据时是一直阻塞的,回环服务器接收到数据回传给客户端,这样客户端和服务器同时进行read时,就会出现都阻塞的状态。
可以设置客户端先发送,再读取,客户端发送后,数据经过服务器回传,客户端收到数据后,再进行下一次发送,若有一次数据丢失则无法进行数据发送,有一直阻塞在接收的风险。
也可以在客户端设置两个进程,一个发送进程,一个接收进程,这样就可以解决。

问题4:接收数据中没有结束符’\0’,在`printf %s时,会导致数据错误(数据先长后短,打印的会包含上次数据),注意结束符的位置,strlen计算到结束符之前。
通过在接收的字符串后补上结束符处理
或者接收数据前对接收数组进行清空处理

 ********////接收数据没有字符结束符,无法判断数据结束,//使用printf%s出现问题:可以将末尾增加字符结束符/0,也可以使用数据初始化(浪费资源)
//memset(recv, 0, 1024);
int len = read(cfd, recv, 1024);
recv[len] = 0;

多进程并发回环服务器代码

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <wait.h>
#include <errno.h>void recvChild(int arg)
{while(1){int ret = waitpid(-1, NULL, WNOHANG);if(ret > 0){printf("recv child, the num is:%d\n", ret);}else if(ret == 0){//还有子进程}else if(ret == -1){//没有子进程了break;}}
}int main()
{//注册信号,解决子进程的回收问题struct sigaction act;act.sa_flags = 0;sigemptyset(&act.sa_mask);act.sa_handler = recvChild;sigaction(SIGCHLD, &act, NULL);//socketint lfd = socket(PF_INET, SOCK_STREAM, 0);if(lfd == -1){perror("socket");exit(-1);}//bindstruct sockaddr_in saddr;saddr.sin_family = AF_INET;inet_pton(AF_INET, "192.168.1.108", &saddr.sin_addr.s_addr);//saddr.sin_addr.s_addr = INADDR_ANY;saddr.sin_port = htons(9999);int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));if(ret == -1){perror("bind");exit(-1);}//listenret = listen(lfd, 2);if(ret == -1){perror("listen");exit(-1);}int prosess_num = 0;while(1){//acceptstruct sockaddr_in caddr;socklen_t len = sizeof(caddr);int cfd = accept(lfd, (struct sockaddr *)&caddr, &len);if(cfd == -1){if(errno == EINTR) continue;perror("accept");exit(-1);}//childprosess_num++;pid_t fd = fork();if(fd == -1){perror("fork");exit(-1);}if(fd == 0){char cip[16];printf("the process %d link success!\n", prosess_num);inet_ntop(AF_INET, &caddr.sin_addr, cip, sizeof(cip));printf("client IP:%s, Port:%d\n\n", cip, ntohs(caddr.sin_port));char recv[1025];while(1){********//
//接收数据没有字符结束符,无法判断数据结束,
//使用printf%s出现问题:可以将末尾增加字符结束符/0,也可以使用数据初始化(浪费资源)//memset(recv, 0, 1024);int len = read(cfd, recv, 1024);recv[len] = 0;if(len == -1){perror("read");exit(-1);}else if(len > 0){if(strcmp(recv, "break\r\n") == 0) break;write(cfd, recv, len+1);printf("IP:%s Port:%d: %s", cip, ntohs(caddr.sin_port), recv);}else{printf("client is closed...\n");break;}}printf("the process %d, IP:%s, port:%d, will close!\n", prosess_num, cip, ntohs(caddr.sin_port));close(cfd);exit(0);}}close(lfd); return 0;
}

客户端代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <fcntl.h>int main()
{int lfd = socket(PF_INET, SOCK_STREAM, 0);if(lfd == -1){perror("socket");exit(-1);}struct sockaddr_in saddr;saddr.sin_family = AF_INET;int len = sizeof(saddr);inet_pton(AF_INET, "192.168.1.108", &saddr.sin_addr.s_addr);saddr.sin_port = htons(9999);int ret = connect(lfd, (struct sockaddr *)&saddr, sizeof(saddr));if(ret == -1){perror("connect");exit(-1);}printf("client link success!\n");//由于读操作会阻塞,客户端需要先发送数据,若先读取数据,一个进程就会阻塞住,一个进程就要先发数据,如果一次数据没有接收到,则会阻塞住。//可以采用两个进程,发、收互不影响pid_t pid = fork();if(pid==0){char rbuf[1024];while(1){//memset(rbuf, 0, 1024);int lent = read(lfd, rbuf, 1024);if(lent > 0){printf("send: %s", rbuf);}else if(lent == -1) perror("read");}}else if(pid > 0){int i = 0;char sbuf[1024];while(1){      i++;if(i > 255) i = 0;sprintf(sbuf, "the num is %d\n", i);write(lfd, sbuf,strlen(sbuf));sleep(1);}}close(lfd);return 0;
}
http://www.lryc.cn/news/116612.html

相关文章:

  • 2021秋招总结
  • Linux6.34 Kubernetes yaml文件详解
  • 防火墙笔记
  • 使用代码下载开源的大模型文件示例以及中文微调llama资源汇总:
  • Wav2vec2 论文阅读看到的一些问题
  • 爬虫学习记录(持续更新)
  • libevent源码学习1---创建event
  • Python类的设计
  • 微信小程序的项目解构
  • 【Archaius技术专题】「Netflix原生态」动态化配置服务之微服务配置组件变色龙
  • python条件分支和循环语句
  • 工具推荐:Wireshark网络协议分析工具(对比tcpdump)
  • [OnWork.Tools]系列 04-快捷启动
  • 如何将项目挂后台运行?【nohup和tmux】
  • 什么是进程、线程、协程
  • Python爬虫——selenium_访问元素信息
  • Linux 文件基本属性
  • CSS 盒模型是什么?它包含哪些属性?标准盒模型/怪异盒模型
  • VB+SQL光盘信息管理系统设计与实现
  • MySQL5.7数据库、Navicat Premium1.6可视化工具安装教程【详细教程】
  • JVM 调优实例
  • Python numpy中的correlate相关性详解
  • 用python实现xmind用例转换为excel/csv用例
  • 论文浅尝 | 面向多步推理任务专业化较小语言模型
  • 基于Java的新闻全文搜索引擎的设计与实现
  • golang 自定义exporter - 端口连接数 portConnCount_exporter
  • MoveTowards详解
  • Redis学习笔记Day01-Redis入门
  • C++ Lambda表达式的完整介绍
  • 【等保测评】云计算Linux服务器(一)