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

2024.12.29(进程线程实现并发服务器)

作业

多进程多线程并发服务器实现一遍提交。

服务器

#include <myhead.h>
#define PORT 12345
#define IP "192.168.124.123"void *fun(void *fd)
{int newfd = *(int *)fd;char buff[1024];while(1){int res = recv(newfd,buff,sizeof(buff),0);if(res == 0){printf("当前客户端已经退出\n");break;}printf("%s\n",buff);strcat(buff,"作业写完了");send(newfd,buff,sizeof(buff),0);}pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{//创建套接字int oldfd = socket(AF_INET,SOCK_STREAM,0);if(oldfd == -1){perror("socket");return -1;}//绑定struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP)};if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("bind");return -1;}//监听if(listen(oldfd,22)==-1){perror("listen");return -1;}struct sockaddr_in client;int client_len = sizeof(client);int newfd;pthread_t tid;while(1){//接收新客户端连入请求newfd = accept(oldfd,(struct sockaddr *)&client,&client_len);if(newfd == -1){perror("accept");return -1;}//创建子线程与客户端通话if(pthread_create(&tid,NULL,fun,&newfd)==-1){perror("pthread_create");return -1;}}pthread_join(tid,NULL);close(newfd);close(oldfd);return 0;
}

客户端

#include <myhead.h>
#define PORT 12345
#define IP "192.168.124.123"
int main(int argc, const char *argv[])
{//创建套接字int oldfd = socket(AF_INET,SOCK_STREAM,0);if(oldfd == -1){perror("socket");return -1;}//连接服务器struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP)};if(connect(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("connect");return -1;}//收发消息char buff[1024];while(1){fgets(buff,sizeof(buff),stdin);buff[strlen(buff)-1] = '\0';send(oldfd,buff,strlen(buff),0);if(strcmp(buff,"quit")==0){break;}bzero(buff,sizeof(buff));recv(oldfd,buff,sizeof(buff),0);printf("服务器发来消息:%s\n",buff);}return 0;
}

学习笔记

端口号快速复用函数

        #include <sys/types.h> /* See NOTES */

        #include <sys/socket.h>

                int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);

        功能: 获取套接字或者其他层级的属性

        参数1:套接字描述符

        参数2:要获取的层级

        参数3:操作名称每一层级名称都不一样,具体见下表。

        参数4:变量的地址(表中的数据类型定义的变量)

        参数5:参数4 的大小。

        返回值:成功返回0,失败返回-1,并置位错误码

                int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

        功能:设置套接字或者其他层级的属性

        参数1:套接字描述符

        参数2:要获取的层级

        参数3:操作名称每一层级名称都不一样,具体见下表。

        参数4:变量的地址(表中的数据类型定义的变量)

        参数5:参数4 的大小。

        返回值:成功返回0,失败返回-1,并置位错误码

函数使用:

#include <myhead.h>int main(int argc, const char *argv[])
{int oldfd= socket(AF_INET,SOCK_STREAM,0);if(oldfd==-1){return -1;}//获取端口号快速复用的属性(默认关闭)int k;int k_len = sizeof(int);if(getsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&k,&k_len)==-1){perror("getsockopt");return -1;}printf("k = %d\n",k);//k=0//设置开启端口号快速复用属性(k!=0即可)k = 2;if(setsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&k,sizeof(k))==-1){perror("setsockopt");return -1;}printf("开启端口号快速复用功能\n");return 0;
}

1、循环服务器模型

创建套接字

绑定

监听

循环:

        创建新的用于通讯的套接字

        收消息

        发消息

        关闭新的套接字

        关闭旧的套接字

代码:

缺点:新客户端要通信,必须使旧的客户端先退出。

#include <myhead.h>
#define IP "192.168.124.34"
#define PORT 6666
int main(int argc, const char *argv[])
{//1、创建套接字//2、绑定//3、监听//4、循环连接不同客户端//5、循环收发信息//1创建套接字    int oldfd = socket(AF_INET,SOCK_STREAM,0);if(-1==oldfd){perror("socket");return -1;}//2、绑定struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP)};if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("bind");return -1;}//3、监听if(listen(oldfd,20)==-1){perror("listen");return -1;}//4、连接int newfd;struct sockaddr_in client;int client_len = sizeof(client);while(1){if((newfd = accept(oldfd,(struct sockaddr *)&client,&client_len))==-1){perror("accept");return -1;}//5、信息收发char buff[1024];while(1){int res = recv(newfd,buff,sizeof(buff),0);if(res==0){printf("您的客户端已经下线\n");break;}printf("%s\n",buff);strcat(buff,"中午吃啥");send(newfd,buff,sizeof(buff),0);}}close(oldfd);close(newfd);return 0;
}

2、基于TCP并发服务器,目前有多进程和多线程并发。

2.1、多进程并发服务器,父进程只负责处理不同客户端的链接请求,每一个客户端请求连接就创建出一个子进程进行处理通话。

多进程服务器模型

1、子进程在哪创建

2、子进程怎么回收

3、终端打开的文件描述符有限。

1、多进程并发执行

模型:

定义信号处理函数,非阻塞回收僵尸进程。

绑定子进程退出时的信号。

1、创建套接字

2、绑定

3、监听

4、循环接收客户端信息

5、让父进程接收客户端请求并关闭新文件描述符,子进程关闭旧的描述符只负责数据收发。

服务器代码:

#include <myhead.h>
#define IP "192.168.124.34"
#define PORT 8888
void handle(int sss)
{if(sss==SIGCHLD){while(waitpid(-1,NULL,WNOHANG)>0);//非阻塞循环回收子进程资源}printf("回收成功\n");
}
int main(int argc, const char *argv[])
{//1、创建套接字//2、绑定//3、监听//4、父进程连接新的客户端//5、创建子父进程,父进程关闭新的描述符//5、子进程负责数据收发并关闭旧的描述符//1、创建套接字int oldfd = socket(AF_INET,SOCK_STREAM,0);if(-1==oldfd){perror("socket");return -1;}//端口号快速复用int k = 999;if(setsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&k,sizeof(k))==-1){perror("setsockopt");return -1;}printf("端口号快速复用成功\n");//2、绑定struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP)};if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("bind");return -1;}//3、监听if(listen(oldfd,88)==-1){perror("listen");return -1;}//4、父进程连接新的客户端 struct sockaddr_in client;int client_len = sizeof(client);while(1){int newfd = accept(oldfd,(struct sockaddr *)&client,&client_len);if(newfd==-1){perror("accept");return -1;}printf("%s:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));//输出新客户端信息//5、创建子父进程pid_t pid = fork();char buff[1024];if(pid>0){if(signal(SIGCHLD,handle)==SIG_ERR)//绑定子进程退出时的信号{perror("signal");return -1;}//父进程关闭新的描述符close(newfd);}else if(pid==0){//子进程关闭旧的描述符信息收发close(oldfd);while(1){int res = recv(newfd,buff,sizeof(buff),0);if(res==0){printf("客户端已经退出\n");break;}printf("%s\n",buff);strcat(buff,"今天周日啊");send(newfd,buff,sizeof(buff),0);}exit(EXIT_SUCCESS);//子进程退出}else{perror("fork");return -1;}}close(oldfd);return 0;
}

TCP客户端:

#include <myhead.h>
#define IP "192.168.124.34"
#define SERPORT 8888
int main(int argc, const char *argv[])
{//1、创建套接字//2、绑定(不是必须绑定)//3、连接//4、收发消息int oldfd = socket(AF_INET,SOCK_STREAM,0);if(oldfd==-1){perror("socket");return -1;}
#if 0//绑定固定的IP和端口号(不是必须的)struct sockaddr_in client = {.sin_family  =AF_INET,.sin_port = htons(7899),//自定义端口号.sin_addr.s_addr = inet_addr("192.168.124.34")};if(bind(oldfd,(struct sockaddr *)&client,sizeof(client))==-1){perror("bind");return -1;}
#endif//连接服务器struct sockaddr_in server = {.sin_family  =AF_INET,.sin_port = htons(SERPORT),//注意端口号需要服务器端口.sin_addr.s_addr = inet_addr(IP)};if(connect(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("connect");return -1;}//收发消息char buff[1024];while(1){fgets(buff,sizeof(buff),stdin);buff[strlen(buff)-1] = '\0';send(oldfd,buff,strlen(buff),0);if(strcmp(buff,"quit")==0)//退出客户端{break;}bzero(buff,sizeof(buff));recv(oldfd,buff,sizeof(buff),0);//阻塞接收服务器消息printf("服务器发来消息:%s\n",buff);}return 0;
}

2、多线程并发服务器

1、大部分的多任务并发执行,我们都选择多线程,而不是多进程,因为多线程资源开销小,而且创建销毁比进程容易。

2、如果客户端过多,需要建立一个线程池,有客户端请求,就从线程池拿出一个线程分配给该客户端。

3、由于线程是提前创建好的,所以响应速度很快。

4、客户端退出后,线程会被销毁,由于线程占用资源较少,销毁也不会占用太多开销。

模型:

线程函数:

        收发消息

        关闭新描述符

        子线程退出

        建立原始套接字

        绑定主机IP 监听客户端

循环:

        accept:获取客户端请求

        建立子线程

        线程挂起

        关闭旧的文件描述符

多线程并发服务器

#include <myhead.h>
#define IP "192.168.124.34"
#define PORT 8888
void *fun(void *fd)
{int newfd = *(int *)fd; char buff[1024];while(1){int res = recv(newfd,buff,sizeof(buff),0);if(res==0){printf("客户端下线\n");break;}printf("%s\n",buff);strcat(buff,"5点放学");send(newfd,buff,sizeof(buff),0);}pthread_exit(NULL);//子线程退出
}int main(int argc, const char *argv[])
{//创建套接字int oldfd = socket(AF_INET,SOCK_STREAM,0);if(oldfd==-1){perror("socket");return -1;}//绑定struct sockaddr_in server = {.sin_family  =AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr  =inet_addr(IP)};if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("bind");return -1;}//监听if(listen(oldfd,20)==-1){perror("listen");return -1;}struct sockaddr_in client;int client_len = sizeof(client);int newfd;pthread_t tid;while(1){//接收新客户端连入请求newfd = accept(oldfd,(struct sockaddr *)&client,&client_len);if(newfd==-1){perror("accept");return -1;}//创建子线程与客户端通话if(pthread_create(&tid,NULL,fun,&newfd)==-1){perror("pthread_create");return -1;}}pthread_join(tid,NULL);//回收子线程资源close(newfd);close(oldfd);return 0;
}

多线程客户端:

#include <myhead.h>
#define IP "192.168.124.34"
#define SERPORT 8888
int main(int argc, const char *argv[])
{//1、创建套接字//2、绑定(不是必须绑定)//3、连接//4、收发消息int oldfd = socket(AF_INET,SOCK_STREAM,0);if(oldfd==-1){perror("socket");return -1;}
#if 0//绑定固定的IP和端口号(不是必须的)struct sockaddr_in client = {.sin_family  =AF_INET,.sin_port = htons(7899),//自定义端口号.sin_addr.s_addr = inet_addr("192.168.124.34")};if(bind(oldfd,(struct sockaddr *)&client,sizeof(client))==-1){perror("bind");return -1;}
#endif//连接服务器struct sockaddr_in server = {.sin_family  =AF_INET,.sin_port = htons(SERPORT),//注意端口号需要服务器端口.sin_addr.s_addr = inet_addr(IP)};if(connect(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("connect");return -1;}//收发消息char buff[1024];while(1){fgets(buff,sizeof(buff),stdin);buff[strlen(buff)-1] = '\0';send(oldfd,buff,strlen(buff),0);if(strcmp(buff,"quit")==0)//退出客户端{break;}bzero(buff,sizeof(buff));recv(oldfd,buff,sizeof(buff),0);//阻塞接收服务器消息printf("服务器发来消息:%s\n",buff);}return 0;
}作业:多进程多线程并发服务器实

思维导图

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

相关文章:

  • 如何在 Ubuntu 上安装 PyTorch
  • 8-Gin 中间件 --[Gin 框架入门精讲与实战案例] 【文末有测试代码】
  • 【潜意识Java】深入详细理解分析Java中的toString()方法重写完整笔记总结,超级详细。
  • 【论文笔记】Contrastive Learning for Sign Language Recognition and Translation
  • Gitlab17.7+Jenkins2.4.91实现Fastapi/Django项目持续发布版本详细操作(亲测可用)
  • 一起来看--红黑树
  • SpringBoot整合篇 05、Springboot整合Redission
  • 供应链系统设计-供应链中台系统设计(六)- 商品中心概念篇
  • 胡闹厨房练习(三)
  • 关于ESD(静电放电)等级的划分
  • 探究步进电机与输入脉冲的关系
  • 基于YOLOV5+Flask安全帽RTSP视频流实时目标检测
  • Windows内置的服务器IIS(Internet Information Services)托管网站
  • 虚幻引擎结构之UObject
  • js的Reflect对象
  • this指向了谁?
  • 基于Resnet、LSTM、Shufflenet及CNN网络的Daily_and_Sports_Activities数据集仿真
  • mac系统vsCode中使用Better Comments在.vue文件里失效
  • UE5.3 C++ Ceiusm中的POI 制作3DUI 结合坐标转化
  • 一起学Git【第六节:查看版本差异】
  • numpy np.newaxis介绍
  • 小程序配置文件 —— 16 项目配置文件和配置 sass
  • 【yolov5】实现FPS游戏人物检测,并定位到矩形框上中部分,实现自瞄
  • 概率统计与随机过程--作业5
  • “802.11g”,“802.11n”,“802.11ac”,“802.11ax”
  • Kubernetes 常用的网络插件
  • Retrofit和rxjava 实现窜行请求,并行请求,循环多次请求,递归请求,错误重试
  • 2025年度好用便签推荐,电脑桌面便签app分享
  • 【论文解读】Arbitrary-steps Image Super-resolution via Diffusion Inversion
  • kkFileView集成springboot:使用自定义预览接口(非minio预览接口),发现无法预览资源