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

网络编程:服务器模型-并发服务器-多进程

并发服务器概念:

并发服务器同一时刻可以处理多个客户机的请求

设计思路:

并发服务器是在循环服务器基础上优化过来的

(1)每连接一个客户机,服务器立马创建子进程或者子线程来跟新的客户机通信 (accept之后的),服务器不会与客户端进行通信!!!

(2)IO多路复用技术

1、多进程实现并发服务器

思想:

主进程专门用于连接多个客户端的请求,若有一路客户端连接进来,主进程就创建一个子进程,用该子进程来处理该客户端的业务数据。

回顾:创建进程

 #include <sys/types.h>

 #include <unistd.h>

    pid_t fork(void);
    功能:创建一个子进程
    参数:无
    返回值:pid_t就是int类型的别名
        返回值大于0,代表此时是父进程,该值的含义为创建成功的子进程的ID号
        返回值等于0,代表此时是子进程
        返回值小于0,创建失败可以perror

源代码:

tcp_server.c 

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>//sockaddr_in
#include <unistd.h>
#include <arpa/inet.h>   // 包含 inet_addr 函数的声明 
#include <sys/select.h>
#include <sys/time.h>
#define BUF_SIZE 20int main(int argc, const char *argv[])
{//1.socketint iServer = socket(AF_INET, SOCK_STREAM, 0);if(-1 == iServer){puts("----------1、create socket error!");return -1;}printf("----------1、create socket ok! iServer:%d", iServer);//0,1,2标准输入输出出错,iServer为3//2.bindstruct sockaddr_in stServer;stServer.sin_family = AF_INET;//第一个成员stServer.sin_port = htons(8888);//第二个成员stServer.sin_addr.s_addr = inet_addr("127.0.0.1");//将点分十进制ip地址转换为32位无符号整数int ret = bind(iServer, (struct sockaddr *)&stServer, sizeof(struct sockaddr));if(-1 == ret){puts("----------2、bind error!");return -1;}puts("----------2、bind ok!");//3.listenret = listen(iServer, 5);if(-1 == ret){puts("----------3、listen error!");return -1;}puts("----------3、listen ok!");//4.acceptstruct sockaddr_in stClient;//存放对方的主机信息socklen_t len = sizeof(struct sockaddr_in);char buf[BUF_SIZE] = {0};fd_set stFdr;//文件描述符集合表,大小1024FD_ZERO(&stFdr);//将文件描述符集合表中所有内容清零while(1){FD_SET(iServer, &stFdr);FD_SET(0, &stFdr);//selectret = select(iServer + 1, &stFdr, NULL, NULL, NULL);if(ret <= 0){continue;}printf("select ok, ret = %d\r\n", ret);//FD_ISSETif(FD_ISSET(0, &stFdr)){memset(buf, 0, BUF_SIZE);fgets(buf, BUF_SIZE, stdin);printf("fgets ok, data = %s\r\n", buf);}if(FD_ISSET(iServer, &stFdr)){int iClient = accept(iServer, (struct sockaddr *)&stClient, &len);if(-1 == iClient){continue;//当前客户端出错转向下一个客户端	}printf("----------4、accept ok! iClient = %d\r\n",iClient );//标准输入输出出错,所以下一个打开的文件一定是3//5.recv/sendret = recv(iClient, buf, BUF_SIZE, 0);if(ret <= 0){close(iClient);continue;}printf("----------recv data ok! buf = %s\r\n",buf);//sendret = send(iClient, buf, BUF_SIZE, 0);if(ret <= 0){close(iClient);continue;}printf("----------send data ok! %s\r\n",buf);//close(iClient);//断开当前客户端}}return 0;
}

tcp_client.c

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#define BUF_SIZE 20
//main函数参数,如果需要键入IP则给定即可
int main(int argc, const char *argv[])
{//1、socketint iClient = 	socket(AF_INET, SOCK_STREAM, 0);if(-1 == iClient){puts("----------1、create socket error!");return -1;}puts("----------1、create socket ok!");//2、connectstruct sockaddr_in stServer;stServer.sin_family = AF_INET;stServer.sin_port = htons(8888);//stServer.sin_addr.s_addr = inet_addr("192.168.15.71");stServer.sin_addr.s_addr = inet_addr("127.0.0.1");int ret = connect(iClient, (struct sockaddr *)&stServer, sizeof(struct sockaddr_in));if(-1 == ret){puts("----------2、connect error!");return -1;}puts("----------2、connect ok!");char buf[BUF_SIZE] = {0};while(1){//gets();//char *fgets(char *s, int size, FILE *stream);fgets(buf, BUF_SIZE, stdin);//更安全,边界检查//3、send recvret = send(iClient, buf, BUF_SIZE, 0);if(-1 == ret){puts("----------3、send data error!");}printf("----------3、send data ok! buf = %s\r\n",buf);//recv//函数原型:ssize_t recv(int sockfd, void *buf, size_t len, int flags);memset(buf, 0, BUF_SIZE);ret = recv(iClient, buf, BUF_SIZE, 0);if(-1 == ret){puts("----------4、recv error!");return -1;}printf("----------4、recv data ok! buf = %s\r\n",buf);}close(iClient);return 0;
}

思考:

多进程并发服务器的缺点:每连接一个客户端,就为其创建子进程,客户端数量比较大时,服务器的运 行效率就会变低。

注:

以上代码只能实现:

        ①客户端连接到服务器端,只能发送一条数据,之后发送不成功

        ②服务器端可以检测标准输入给自己

测试结果如下图:

2、多进程实现并发服务器-优化版本

tcp_server.c 

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>//sockaddr_in
#include <unistd.h>
#include <arpa/inet.h>   // 包含 inet_addr 函数的声明 
#include <sys/select.h>
#include <sys/time.h>
#define BUF_SIZE 20int main(int argc, const char *argv[])
{//1.socketint iServer = socket(AF_INET, SOCK_STREAM, 0);if(-1 == iServer){puts("----------1、create socket error!");return -1;}printf("----------1、create socket ok! iServer:%d", iServer);//0,1,2标准输入输出出错,iServer为3//2.bindstruct sockaddr_in stServer;stServer.sin_family = AF_INET;//第一个成员stServer.sin_port = htons(9999);//第二个成员stServer.sin_addr.s_addr = inet_addr("127.0.0.1");//将点分十进制ip地址转换为32位无符号整数int ret = bind(iServer, (struct sockaddr *)&stServer, sizeof(struct sockaddr));if(-1 == ret){puts("----------2、bind error!");return -1;}puts("----------2、bind ok!");//3.listenret = listen(iServer, 5);if(-1 == ret){puts("----------3、listen error!");return -1;}puts("----------3、listen ok!");//4.acceptstruct sockaddr_in stClient;//存放对方的主机信息socklen_t len = sizeof(struct sockaddr_in);char buf[BUF_SIZE] = {0};fd_set stFdr;//文件描述符集合表,大小1024FD_ZERO(&stFdr);//将文件描述符集合表中所有内容清零FD_SET(iServer, &stFdr);//iServer添加到原文件描述符集合表中int max = iServer;while(1){//selectfd_set stFdrTmp = stFdr;	//定义临时文件描述符集合表ret = select(max + 1, &stFdrTmp, NULL, NULL, NULL);if(ret <= 0){printf("select error!\r\n");continue;}printf("select ok, ret = %d\r\n", ret);int i = 0;for(i = 0; i < max + 1; i++){if(FD_ISSET(i, &stFdrTmp)){	 // 循环判断哪个文件描述符被置位//操作if(i == iServer){ // i == 3, 操作int iClient = accept(iServer, (struct sockaddr *)&stClient, &len);if(-1 == iClient){continue;//当前客户端出错转向下一个客户端	}printf("----------4、accept ok! iClient = %d\r\n",iClient );//标准输入输出出错,所以下一个打开的文件一定是4FD_SET(iClient, &stFdr);//更新maxif(max < iClient){max = iClient;}}else{ // 与多个客户端保持连接//recv/sendret = recv(i, buf, BUF_SIZE, 0);if(ret > 0){printf("recv:%s\r\n", buf);send(i, buf, BUF_SIZE, 0);}else{close(i);FD_CLR(i, &stFdr);}}}}}return 0;
}

tcp_client.c

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#define BUF_SIZE 20
//main函数参数,如果需要键入IP则给定即可
int main(int argc, const char *argv[])
{//1、socketint iClient = 	socket(AF_INET, SOCK_STREAM, 0);if(-1 == iClient){puts("----------1、create socket error!");return -1;}puts("----------1、create socket ok!");//2、connectstruct sockaddr_in stServer;stServer.sin_family = AF_INET;stServer.sin_port = htons(9999);//stServer.sin_addr.s_addr = inet_addr("192.168.15.71");//stServer.sin_addr.s_addr = inet_addr("192.168.15.71");stServer.sin_addr.s_addr = inet_addr("127.0.0.1");int ret = connect(iClient, (struct sockaddr *)&stServer, sizeof(struct sockaddr_in));if(-1 == ret){puts("----------2、connect error!");return -1;}puts("----------2、connect ok!");char buf[BUF_SIZE] = {0};while(1){//gets();//char *fgets(char *s, int size, FILE *stream);fgets(buf, BUF_SIZE, stdin);//更安全,边界检查//3、send recvret = send(iClient, buf, BUF_SIZE, 0);if(-1 == ret){puts("----------3、send data error!");}printf("----------3、send data ok! buf = %s\r\n",buf);//recv//函数原型:ssize_t recv(int sockfd, void *buf, size_t len, int flags);memset(buf, 0, BUF_SIZE);ret = recv(iClient, buf, BUF_SIZE, 0);if(-1 == ret){puts("----------4、recv error!");return -1;}printf("----------4、recv data ok! buf = %s\r\n",buf);}close(iClient);return 0;
}

注:

以上代码可以实现:

        ①多个客户端与服务器连接 并 发送&回显数据

测试结果如下图:

http://www.lryc.cn/news/348373.html

相关文章:

  • React 基础案例
  • 【Python探索之旅】选择结构(条件语句)
  • Recommender ~ Collaborative filtering
  • 我觉得POC应该贴近实际
  • AI 情感聊天机器人工作之旅 —— 与复读机问题的相遇与别离
  • 如何使用ArcGIS Pro进行选房分析
  • android图标底色问题,debug与release不一致
  • 如何提高自己的全局视野?
  • element ui的确认提示框文字样式修改
  • Typescript 哲学 - ts模块使用最佳实践
  • 自动驾驶决策规划——坐标转换
  • 信创应用软件之邮箱
  • TriCore: Architecture
  • 16进制与不同进制之间计算加减乘除的比较快的方法
  • 责任链模式:原理与实现解析,及其应用场景代入
  • 从心理学角度看,GPT 对人有什么影响?
  • 【C语言/数据结构】栈:从概念到两种存储结构的实现
  • 47. UE5 RPG 实现角色死亡效果
  • C语言/数据结构——每日一题(环形链表)
  • vue:网页icon无法显示
  • 电脑设置在哪里打开?Window与Mac双系统操作指南
  • 【linux】海量小文件的存储方案
  • 【SpringBoot整合系列】SpringBoot整合RabbitMQ-基本使用
  • MySQL————创建存储过程函数
  • 数据赋能(86)——数据要素:管理核心框架
  • 测试的基本概念
  • Python多线程加速-休眠部分线程
  • B+树(B+ Tree)
  • 【Linux】了解信号产生的五种方式
  • 【nuxt3国际化i18n】vue3+nuxt3+vite+TS国际化的正确做法