您的位置:首页 > 房产 > 建筑 > 福州到泉州_江苏卫健委疫情最新消息_每日新闻播报_谷歌怎么投放广告

福州到泉州_江苏卫健委疫情最新消息_每日新闻播报_谷歌怎么投放广告

2024/12/23 12:19:19 来源:https://blog.csdn.net/dghbs/article/details/142148718  浏览:    关键词:福州到泉州_江苏卫健委疫情最新消息_每日新闻播报_谷歌怎么投放广告
福州到泉州_江苏卫健委疫情最新消息_每日新闻播报_谷歌怎么投放广告

目录

服务器模型

 1》循环服务器

 2》并发服务器

1> 多进程:每有一个客户端连接创建一个进程进行通信

2>  多线程:每有一个客户端连接创建一个线程进行通信

 3> IO多路复用

4> 总结


服务器模型

在网络通信中,通常一个服务器要连接多个客户端

为了处理多个客户端的请求,通常有多种表现形式:循环服务器、并发服务器

 1》循环服务器

循环服务器:一个服务器在同一时间只能处理一个客户端的请求

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>int main(int argc, char const *argv[])
{if (argc != 2){perror("argc err\n");return -1;}char buf[128] = {0};// 1.创建套接字(socket)---------------》有手机int socketfd = socket(AF_INET, SOCK_STREAM, 0);if (socketfd < 0){perror("socket err\n");return -1;}printf("socketfd: %d\n", socketfd);// 2.指定(自己)网络信息--------------------》有号码// struct sockaddr_in// {//     sa_family_t sin_family;  /* address family: AF_INET *///     in_port_t sin_port;      /* port in network byte order *///     struct in_addr sin_addr; /* internet address */// };// /* Internet address. */// struct in_addr// {//     uint32_t s_addr; /* address in network byte order */// };struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); // 全局地址,自动获取本机地址// saddr.sin_addr.s_addr = INADDR_ANY;// 3.绑定套接字(bind)------------------》绑定手机(插卡)if (bind(socketfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind err\n");return -1;}printf("bind ok\n");// 4.监听套接字(listen)-----------------》待机if (listen(socketfd, 6) < 0){perror("listen err\n");return -1;}printf("listen ok\n");// 5.接收客户端连接请求(accept)--》接电话while (1) // 循环等待客户端链接,实现循环服务器功能{int len = sizeof(caddr);int acceptfd = accept(socketfd, (struct sockaddr *)&caddr, &len);if (acceptfd < 0){perror("accept err\n");return -1;}printf("acceptfd: %d\n", acceptfd);printf("port: %d  ip: %s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr)); // 来电显示,显示客户端信息// 6.接收、发送数据(recv send)---》通话int ret;/*设置套接字属性:超时重传*/struct timeval tm = {2, 0};setsockopt(acceptfd, SOL_SOCKET, SO_REUSEADDR, &tm, sizeof(tm));while (1){ret = recv(acceptfd, buf, sizeof(buf), 0);if (ret < 0){ perror("recv error\n");}else if (ret == 0){printf("client exit\n");break;}else{printf("buf: %s\n", buf);memset(buf, 0, sizeof(buf));}}// 7.关闭套接字(close)-----------------》挂电话close(acceptfd);}close(socketfd);return 0;
}

 2》并发服务器

并发服务器:一个服务器在同一时间内可以处理多个客户端的请求,有三种实现方法

1> 多进程:每有一个客户端连接创建一个进程进行通信

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>void handler(int sig)
{wait(NULL);//回收子进程资源
}int main(int argc, char const *argv[])
{pid_t pid;char buf[128] = {0};int ret, acceptfd;// 1.创建套接字(socket)---------------》有手机int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket err");return -1;}printf("sockfd:%d\n", sockfd); // 3// 2.指定网络信息---------------------------》有号码struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;            // IPV4saddr.sin_port = htons(atoi(argv[1])); // 端口号// saddr.sin_addr.s_addr = inet_addr("192.168.50.13"); // 虚拟机IP// saddr.sin_addr.s_addr = inet_addr("0.0.0.0");saddr.sin_addr.s_addr = INADDR_ANY;int len = sizeof(caddr);// 3.绑定套接字(bind)------------------》绑定手机(插卡)if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind err");return -1;}printf("bind ok\n");// 4.监听套接字(listen)-----------------》待机if (listen(sockfd, 6) < 0){perror("listen err");return -1;}printf("listen ok\n");// 5.接收客户端连接连接请求(accept)--》接电话// tcp服务器一共有两类文件描述符,一类用于连接,一类用于通信// socket函数返回值:用于连接的文件描述符// accept函数返回值:用于通信的文件描述符signal(SIGCHLD, handler);//设置信号处理方式while (1){acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);if (acceptfd < 0){perror("accept err");return -1;}printf("port:%d ip:%s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));printf("acceptfd:%d\n", acceptfd);pid = fork();//创建子进程if (pid < 0){perror("fork err");return -1;}else if (pid == 0)//子进程用于通信{// 6.接收、发送数据(recv send)---》通话while (1){// read/write()ret = recv(acceptfd, buf, sizeof(buf), 0);if (ret < 0){perror("recv err");break;}else if (ret == 0)//客户端退出{printf("client exit\n");break;}else{printf("buf:%s\n", buf);memset(buf, 0, sizeof(buf));}}// 7.关闭套接字(close)-----------------》挂电话close(acceptfd);//关闭子进程通信描述符exit(0);//退出子进程}else//父进程用于循环链接客户端close(acceptfd);//关闭父进程通信文件描述符}close(sockfd);return 0;
}

2>  多线程:每有一个客户端连接创建一个线程进行通信

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>void *handler(void *arg)//从线程函数
{int ret, acceptfd;acceptfd = *((int *)arg);//强转为 int 类型char buf[128] = {0};// 6.接收、发送数据(recv send)---》通话while (1){// read/write()ret = recv(acceptfd, buf, sizeof(buf), 0);if (ret < 0){perror("recv err");return NULL;}else if (ret == 0){printf("client exit\n");break;}else{printf("buf:%s\n", buf);memset(buf, 0, sizeof(buf));}// 7.关闭套接字(close)-----------------》挂电话}close(acceptfd);pthread_exit(NULL);//退出线程
}int main(int argc, char const *argv[])
{int acceptfd;pthread_t tid;// 1.创建套接字(socket)---------------》有手机int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket err");return -1;}printf("sockfd:%d\n", sockfd); // 3// 2.指定网络信息---------------------------》有号码struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;            // IPV4saddr.sin_port = htons(atoi(argv[1])); // 端口号// saddr.sin_addr.s_addr = inet_addr("192.168.50.13"); // 虚拟机IP// saddr.sin_addr.s_addr = inet_addr("0.0.0.0");saddr.sin_addr.s_addr = INADDR_ANY;int len = sizeof(caddr);// 3.绑定套接字(bind)------------------》绑定手机(插卡)if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind err");return -1;}printf("bind ok\n");// 4.监听套接字(listen)-----------------》待机if (listen(sockfd, 6) < 0){perror("listen err");return -1;}printf("listen ok\n");// 5.接收客户端连接连接请求(accept)--》接电话// tcp服务器一共有两类文件描述符,一类用于连接,一类用于通信// socket函数返回值:用于连接的文件描述符// accept函数返回值:用于通信的文件描述符while (1){acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);if (acceptfd < 0){perror("accept err");return -1;}printf("port:%d ip:%s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));printf("acceptfd:%d\n", acceptfd);pthread_create(&tid, NULL, handler, &acceptfd);//创建从线程pthread_detach(tid);//不阻塞回收从线程资源}close(sockfd);return 0;
}

 3> IO多路复用

select  poll  epoll 见上篇博客 IO多路复用

4> 总结

多进程:

优点:服务器更稳定,父子进程资源独立,安全性高

缺点:需要开辟多个进程,资源消耗大,系统开销大

多线程:

优点:相对于多进程,资源开销小,多个线程共享同一个进程的资源

缺点:需要开辟多个线程,安全性差

IO多路复用:

优点:节省资源,系统开销小,性能高

缺点:代码复杂度高


今天的分享就到这里结束啦,如果有哪里写的不好的地方,请指正。
如果觉得不错并且对你有帮助的话点个关注支持一下吧!

版权声明:

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

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