• 网络编程 lesson6 服务器模型和网络超时检测


    目录

    服务器模型介绍

    网络编程服务器模型

    循环服务器模型

    并发服务器模型

    1. 多线程服务器

    2. 多进程服务器

    3. 事件驱动服务器

    网络超时检测

    应用场景

    设置超时检测的方式

    1.利用函数参数设置

    代码示例

    2.利用socket属性设置

    3.利用alarm定时器设置

    代码示例


    服务器模型介绍

    在网络模型中,服务器模型是指在计算机网络中扮演服务器角色的计算机系统或软件。它用于接收和处理客户端的请求,并向客户端提供所需的服务或资源。以下是一些常见的服务器模型:

    1. 客户端/服务器模型:这是最常见的服务器模型之一。在这种模型中,客户端(例如个人计算机、智能手机或其他设备)发送请求到服务器,服务器处理这些请求并返回响应。这种模型适用于诸如网页浏览、电子邮件、文件传输等各种应用。
    2. 分布式服务器模型:在分布式服务器模型中,多台服务器协同工作,共同处理客户端的请求。这种模型可以提高系统的性能和可伸缩性,因为负载可以在多个服务器之间分配,而不是由单个服务器承担所有请求。
    3. 客户端/代理/服务器模型:在这种模型中,代理服务器充当位于客户端和目标服务器之间的中间人。客户端发送请求到代理服务器,代理服务器代表客户端与目标服务器通信,并将响应返回给客户端。这种模型常用于提供缓存、负载均衡和安全性增强等功能。
    4. 对等网络模型:在对等网络模型中,计算机之间没有明确的客户端和服务器角色。相反,所有计算机都可以充当客户端和服务器,并与其他计算机直接通信。这种模型适用于对等文件共享、即时通讯等应用。
    5. 云服务器模型:云服务器模型是指在云计算环境中提供的服务器服务。云服务器提供虚拟化的计算资源,客户可以根据需要动态分配和管理这些资源。这种模型具有弹性和可伸缩性,适用于处理大规模的网络流量和复杂的应用。

    这些服务器模型在不同的网络环境和应用场景中有不同的适用性。根据具体的需求和目标,可以选择适当的服务器模型来满足网络服务的要求。

    网络编程服务器模型

    在网络程序里面,通常都是一个服务器处理多个客户机。

    为了处理多个客户的请求,服务器的程序有不同的处理方式。

    循环服务器模型

    循环服务器模型(Round Robin Server Model)是一种负载均衡的服务器模型。在这种模型中,多台服务器按照轮询的方式依次接收和处理客户端的请求,以实现请求的均衡分配。循环服务器一次只能响应一个客户端的请求。伪代码如下

    1. socket() //创建套接字
    2. bind()//绑定套接字
    3. listen()//监听
    4. while(1)
    5. {
    6. accept();//创建新套接字和客户端连接
    7. while(1)
    8. {
    9. process();//对接收的数据进行处理
    10. }
    11. close();//关闭套接字
    12. }

    并发服务器模型

    并发服务器模型(Concurrent Server Model)是一种服务器模型,旨在处理同时到达的多个客户端请求。在这种模型中,服务器能够同时处理多个请求,而无需等待前一个请求的完成。

    以下是一种常见的并发服务器模型:

    1. 多线程服务器

    在多线程服务器模型中,服务器为每个客户端请求创建一个独立的线程来处理。当有新的请求到达时,服务器创建一个新的线程,并将该请求分配给该线程进行处理。这样,服务器可以同时处理多个请求,每个请求都在独立的线程中执行。这种模型具有较低的开销和快速的响应时间,但需要额外的线程管理和同步机制来确保线程安全性。伪代码如下

    1. socket();
    2. bind();
    3. listen();
    4. while(1)
    5. {
    6. accept();
    7. if(fork()==0)//子进程
    8. {
    9. while(1)
    10. {
    11. process();//进行处理
    12. }
    13. close();
    14. exit();
    15. }
    16. else
    17. {
    18. }
    19. }
    20. //注:收到客户端消息后,打印下是来自哪个客户端的数据(来电显示)
    21. //注:使用SIGCHLD来处理子进程结束的信号,信号函数中回收进程资源。
    22. //当子进程结束时,会向其父进程发送一个SIGCHLD信号。这个信号通知父进程子进程的状态发生了变化,
    23. //可以通过捕捉SIGCHLD信号来处理子进程的退出状态。

    2. 多进程服务器

    在多进程服务器模型中,服务器为每个客户端请求创建一个独立的进程来处理。每个进程具有自己的内存空间和执行环境,因此可以独立地执行请求。这种模型提供了更好的隔离性和稳定性,但进程间切换的开销较大。伪代码如下

    1. socket()
    2. bind();
    3. listen();
    4. while(1)
    5. {
    6. accept();
    7. pthread_create();//创建线程
    8. }

    3. 事件驱动服务器

    在事件驱动服务器模型中,服务器使用异步I/O和事件循环机制来处理多个客户端请求。服务器使用单个线程或进程来接收和处理所有的请求,并使用事件通知机制来处理客户端的I/O操作。这种模型具有高效的资源利用率和可扩展性,适用于处理大量并发请求。

    借助select、poll、epoll机制,将新连接的客户端描述符增加到描述符表中,只需要一个线程即可处理所有的客户端连接,在嵌入式开发中应用广泛,不过代码写起了稍显繁琐。、

    网络超时检测

    应用场景

    在网络通信中,很多操作会使得进程阻塞:

    TCP套接字中的recv/accept

    UDP套接字中的recvfrom

    超时检测的必要性

    避免进程在没有数据时无限制地阻塞

    实现某些特定协议要求,比如某些设备规定,发送请求数据后,如果多长时间后没有收到来自设备的回复,需要做出一些特殊处理

    设置超时检测的方式

    1.利用函数参数设置

    select,poll,epoll函数可以通过最后一个参数设置超时检测

    1. //1.select函数设置超时检测
    2. int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    3. struct timeval tm={2,0};//设置2s打算阻塞
    4. sret=select(maxfd+1,&tempfds,NULL,NULL,&tm);
    5. //通过设置第5个参数来进行超时检测
    6. struct timeval{
    7. long tv_sec;//秒
    8. long tv_usec;//微秒
    9. }
    10. //2.poll设置超时
    11. int poll(struct pollfd *fds, nfds_t nfds, int timeout);
    12. //第三个参数:时间单位是毫秒 -1阻塞, 2000=2s
    13. ret = poll(event, num, 2000);//超时检测时间为2s
    14. //3.epoll函数设置超时
    15. int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
    16. //第四个参数:时间单位是毫秒 -1阻塞, 2000=2s
    17. ret = epoll_wait(epfd, events, 20, 2000);
    18. 上面设置超时后的返回值:
    19. <0 error
    20. =0 超时
    21. >0 正常

    代码示例

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. void *mythread(void *arg)
    12. {
    13. int acceptfd = *((int *)arg); //4
    14. //循环收发消息
    15. char buf[128];
    16. int ret;
    17. while (1)
    18. {
    19. ret = recv(acceptfd, buf, sizeof(buf), 0);
    20. if (ret < 0)
    21. {
    22. perror("recv err.");
    23. return NULL;
    24. }
    25. else if (ret == 0)
    26. {
    27. printf("client exit\n");
    28. close(acceptfd);
    29. break;
    30. }
    31. else
    32. {
    33. printf("buf:%s\n", buf);
    34. }
    35. }
    36. pthread_exit(NULL); //return NULL;
    37. }
    38. int main(int argc, char const *argv[])
    39. {
    40. if (argc != 2)
    41. {
    42. printf("please input %s \n", argv[0]);
    43. return -1;
    44. }
    45. //1.创建流式套接字
    46. int sockfd = socket(AF_INET, SOCK_STREAM, 0); //链接
    47. if (sockfd < 0)
    48. {
    49. perror("socket err.");
    50. return -1;
    51. }
    52. printf("sockfd:%d\n", sockfd); //3
    53. //填充ipv4的通信结构体
    54. struct sockaddr_in saddr, caddr;
    55. saddr.sin_family = AF_INET;
    56. saddr.sin_port = htons(atoi(argv[1]));
    57. saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    58. socklen_t len = sizeof(caddr);
    59. //2.绑定套接字 ip和端口(自己)
    60. if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    61. {
    62. perror("bind err.");
    63. return -1;
    64. }
    65. printf("bind ok.\n");
    66. //3.监听
    67. if (listen(sockfd, 5) < 0)
    68. {
    69. perror("listen err.");
    70. return -1;
    71. }
    72. printf("listen ok.\n");
    73. //4.阻塞等待客户端链接
    74. while (1)
    75. {
    76. int acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
    77. if (acceptfd < 0)
    78. {
    79. perror("accept err.");
    80. return -1;
    81. }
    82. printf("acceptfd=%d\n", acceptfd); //通信
    83. printf("client:ip=%s port=%d\n",
    84. inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
    85. pthread_t tid;
    86. pthread_create(&tid, NULL, mythread, &acceptfd);
    87. pthread_detach(tid);
    88. }
    89. close(sockfd);
    90. return 0;
    91. }

    2.利用socket属性设置

    socket属性表

    选项名称        说明                  数据类型 
    ======================================================================== 
    	SOL_SOCKET  应用层
    ------------------------------------------------------------------------ 
    SO_BROADCAST	允许发送广播数据          int 
    SO_DEBUG		允许调试            		 int 
    SO_DONTROUTE    不查找路由                 int 
    SO_ERROR        获得套接字错误             int 
    SO_KEEPALIVE    保持连接                  int 
    SO_LINGER       延迟关闭连接               struct linger 
    SO_OOBINLINE    带外数据放入正常数据流           int 
    SO_RCVBUF       接收缓冲区大小               int 
    SO_SNDBUF       发送缓冲区大小               int 
    SO_RCVLOWAT     接收缓冲区下限               int 
    SO_SNDLOWAT     发送缓冲区下限              int 
    SO_RCVTIMEO     接收超时                 struct timeval 
    SO_SNDTIMEO     发送超时                 struct timeval 
    SO_REUSEADDR    允许重用本地地址和端口          int 
    SO_TYPE         获得套接字类型              int 
    SO_BSDCOMPAT    与BSD系统兼容               int 
    ==========================================================================             
             IPPROTO_IP  IP层/网络层
    ----------------------------------------------------------------------------
    IP_HDRINCL      在数据包中包含IP首部          int 
    IP_OPTINOS      IP首部选项               int 
    IP_TOS          服务类型 
    IP_TTL          生存时间                int 
    IP_ADD_MEMBERSHIP       将指定的IP加入多播组                    struct ip_mreq
    ==========================================================================            
    		IPPRO_TCP  传输层
    -----------------------------------------------------------------------------
    TCP_MAXSEG      TCP最大数据段的大小            int 
    TCP_NODELAY     不使用Nagle算法             int  
    

    API接口

    1. int getsockopt(int sockfd,int level,int optname,void *optval,socklen_t *optlen)
    2. int setsockopt(int sockfd,int level,int optname,void *optval,socklen_t optlen)
    3. 功能:获得/设置套接字属性
    4. 参数:
    5. sockfd:套接字描述符
    6. level: 协议层
    7. SOL_SOCKET(应用层)
    8. IPPROTO_TCP(传输层)
    9. IPPROTO_IP(网络层)
    10. optname:选项名
    11. SO_BROADCAST 允许发送广播数据 int
    12. SO_RCVBUF 接收缓冲区大小 int
    13. SO_SNDBUF 发送缓冲区大小 int
    14. SO_RCVTIMEO 接收超时 struct timeval
    15. SO_SNDTIMEO 发送超时 struct timeval
    16. optval:选项值
    17. optlen:选项值大小指针
    18. 设置超时检测
    19. struct timeval{
    20. long tv_sec; //秒
    21. long tv_usec; //微秒
    22. }
    23. //设置接收超时
    24. struct timeval tm ={2,0};
    25. setsockopt(acceptfd,SOL_SOCKET,SO_RCVTIMEO,&tm,sizeof(tm));
    26. //设置应用层的发送超时
    27. //设置端口和地址重用
    28. int optval=1;
    29. setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval));
    30. //设置应用层,允许重用端口和地址

    3.利用alarm定时器设置

    1. alarm(5) 闹钟 定时器
    2. //5秒之后,会有一个信号产生SIGALRM
    3. int sigaction(int signum,const struct *act,struct sigaction *oldact);
    4. 功能:对接收到的指定信号处理
    5. struct sigaction{
    6. void (*sa_handler)(int);
    7. };
    8. //设置信号属性
    9. struct sigaction act;
    10. sigaction(SIGALRM,NULL,&act);//获取原属性
    11. act.sa_handler=handler;//修改属性
    12. sigaction(SIGALRM,&act,NULL);//将修改的属性设置回去
    13. 注:
    14. 在recv前调用alarm函数
    15. alarm的 SIGALRM信号产生后会打断(终端)下面的系统调用recv;
    16. 打断后相当于recv执行完毕。

    代码示例

    1. #include
    2. #include
    3. #include
    4. void handler(int sig)
    5. {
    6. printf("timeout\n");
    7. }
    8. int main(int argc, char const *argv[])
    9. {
    10. char buf[32];
    11. //修改信号的属性
    12. struct sigaction act;
    13. sigaction(SIGALRM,NULL,&act);
    14. act.sa_handler=handler;
    15. sigaction(SIGALRM,&act,NULL);
    16. while(1)
    17. {
    18. alarm(5);
    19. printf("hello\n");
    20. if(fgets(buf,sizeof(buf),stdin) == NULL)
    21. {
    22. perror("fgets err.");
    23. continue;
    24. }
    25. printf("buf:%s\n",buf);
    26. }
    27. return 0;
    28. }

  • 相关阅读:
    学习...笔记08:Origin绘制常见图像的方法总结
    Google Earth Engine APP——打印点的坐标到控制台上和map上,设置样式并更新
    Maven进阶-多环境开发与跳过测试
    第27集丨为人格补钙,让心灵吸氧
    真相浮出水面
    andlua怎么判断软件是否运行
    Linux添加、删除用户和用户组
    【Computer Vision】基于ResNet-50实现CIFAR10数据集分类
    javaScript中Math内置对象基本方法入门
    virsh 保护命令 virtual protect rebase
  • 原文地址:https://blog.csdn.net/weixin_42352787/article/details/130912685