服务器模型、网络超时检测

服务器模型

循环服务器

每次只能处理一个客户端,当前客户端退出后,才能处理下一个客户端。
(循环处理客户端,因此不能做耗时动作。)

练习: 实现 TCP 全双工

利用进程实现

利用线程实现

并发服务器

同一时刻可以响应多个客户端的请求。

多进程实现并发(不建议)

// ser_pro.c
// cli_pro.c

多线程实现并发

每来一个客户端连接,开一个子线程来专门处理客户端的数据,实现简单,资源占用少。

// ser_thr.c#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h>    
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>void *handler(void *arg){int fd = *(int *)arg;char buf[256] = {};while (1){int receriver = recv(fd, buf, sizeof(buf), 0);if (receriver < 0){perror("Failed to receive");return NULL;} else if (receriver == 0){printf("Client exited. \n");break;} else {printf("%s\n", buf);}}close(fd);pthread_exit(NULL);
}int main(int argc, char const *argv[])
{if (argc != 2){printf("Please input %s <port>. \n", argv[0]);return -1;}int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("Failed to create a socket");return -1;}printf("sockfd: %d\n", sockfd);struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr("0.0.0.0");if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("Failed to bind");return -1;}if (listen(sockfd, 6) < 0){perror("Failed to listen");return -1;}struct sockaddr_in caddr;socklen_t length = sizeof(caddr);char buf[256] = {};while (1){int accfd = accept(sockfd, (struct sockaddr *)&caddr, &length);if (accfd < 0){perror("Failed to accept");return -1;}printf("Client IPv4: %s\t\tport: %d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));pthread_t tid;pthread_create(&tid, NULL, handler, &accfd);pthread_detach(tid);}close(sockfd);    return 0;
}
// cli_thr.c#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>void *mythread_recv(void *arg)
{int fd = *((int *)arg);char buf[256] = {};while (1){int receriver = recv(fd, buf, sizeof(buf), 0);if (receriver < 0){perror("recv is err:");return NULL;} else {printf("%s\n", buf);}}pthread_exit(NULL);
}int main(int argc, const char *argv[])
{if (argc != 3){printf("Please input %s <ip> <port>. \n", argv[0]);return -1;}int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("Failed to create a socket");return -1;}struct sockaddr_in caddr;caddr.sin_family = AF_INET;caddr.sin_port = htons(atoi(argv[2]));caddr.sin_addr.s_addr = inet_addr(argv[1]);if (connect(sockfd, (struct sockaddr *)&caddr, sizeof(caddr)) < 0){perror("Failed to connect");return -1;}pthread_t tid;pthread_create(&tid, NULL, mythread_recv, &sockfd);pthread_detach(tid);char buf[128] = "";while (1){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf)-1] == '\n')buf[strlen(buf)-1] = '\0';if (!strcmp(buf, "quit")){printf("Client exited. \n");break;}send(sockfd, buf, sizeof(buf), 0);}close(sockfd);return 0;
}

实现效果如下:
在这里插入图片描述

// server.c#include "mymacro.h"linklist_t ph;void *server_send(void *arg){linklist_t p;char buf[256] = {};while (1){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf)-1] == '\n')buf[strlen(buf)-1] = '\0';p = (linklist_t)arg;while (p->next){p = p->next;send(p->fd, buf, sizeof(buf), 0);}}// pthread_exit(NULL);			// 服务器端不需要退出此线程
}void *server_recv(void *arg){int fd = *(int *)arg;char buf[256] = {};while (1){int receriver = recv(fd, buf, sizeof(buf), 0);if (receriver < 0){perror("Failed to receive");return NULL;} else if (receriver == 0){DeleteFromLinkedList(ph, fd);printf("Client[%d] exited. \n", fd);break;} else {printf("Client[%d]: %s\n", fd, buf);}}close(fd);pthread_exit(NULL);
}int main(int argc, char const *argv[])
{if (argc != 2){printf("Please input %s <port>. \n", argv[0]);return -1;}int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("Failed to create a socket");return -1;}// printf("sockfd: %d\n", sockfd);ADDRIN 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");socklen_t length = sizeof(caddr);if (bind(sockfd, (ADDR *)&saddr, sizeof(saddr)) < 0){perror("Failed to bind");return -1;}if (listen(sockfd, 6) < 0){perror("Failed to listen");return -1;}ph = CreateLinkedList();head = ph;char buf[256] = {};pthread_t tid_recv, tid_send;pthread_create(&tid_send, NULL, server_send, head);pthread_detach(tid_send);while (1){int accfd = accept(sockfd, (ADDR *)&caddr, &length);if (accfd < 0){perror("Failed to accept");return -1;}// printf("Client IPv4: %s\t\tport: %d\n", //        inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));InsertIntoLinkedList(head, accfd);ShowLinkedList(head);pthread_create(&tid_recv, NULL, server_recv, &accfd);pthread_detach(tid_recv);}close(sockfd);    return 0;
}
```c
// client.c#include "mymacro.h"void *client_recv(void *arg)
{int fd = *((int *)arg);char buf[256] = {};while (1){int receriver = recv(fd, buf, sizeof(buf), 0);if (receriver < 0){perror("recv is err:");return NULL;} else {printf("Server: %s\n", buf);}}pthread_exit(NULL);
}int main(int argc, const char *argv[])
{if (argc != 3){printf("Please input %s <ip> <port>. \n", argv[0]);return -1;}int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("Failed to create a socket");return -1;}struct sockaddr_in caddr;caddr.sin_family = AF_INET;caddr.sin_port = htons(atoi(argv[2]));caddr.sin_addr.s_addr = inet_addr(argv[1]);if (connect(sockfd, (struct sockaddr *)&caddr, sizeof(caddr)) < 0){perror("Failed to connect");return -1;}pthread_t tid;pthread_create(&tid, NULL, client_recv, &sockfd);pthread_detach(tid);char buf[256] = {};while (1){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf)-1] == '\n')buf[strlen(buf)-1] = '\0';if (!strcmp(buf, "quit")){printf("Client exited. \n");break;}send(sockfd, buf, sizeof(buf), 0);}close(sockfd);return 0;
}
// mymacro.h#ifndef __MYMACRO_H_
#define __MYMACRO_H_#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h>    
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>#define N 32typedef struct sockaddr_in ADDRIN;
typedef struct sockaddr ADDR;typedef struct linkedlist{int fd;struct linkedlist *next;
} linknode_t, *linklist_t;linklist_t head;/* *************** 以下函数可写到一个新的.c文件中 **************** */linklist_t CreateLinkedList(){linklist_t p = (linklist_t)malloc(sizeof(linknode_t));if (!p){perror("Failed to create a linked list");return NULL;}p->next = NULL;return p;
}int InsertIntoLinkedList(linklist_t p, int data){linklist_t pnw = (linklist_t)malloc(sizeof(linknode_t));if (!pnw){perror("Failed to create a new node");return -1;}pnw->fd = data;while (p->next)p = p->next;p->next = pnw;pnw->next = NULL;return 0;
}int DeleteFromLinkedList(linklist_t p, int data){while (p->next){if (p->next->fd == data){linklist_t pdel = p->next;p->next = pdel->next;free(pdel);pdel = NULL;}else                // 必须加,否则段错误p = p->next;}return 0;
}void ShowLinkedList(linklist_t p){p = p->next;while (p){printf("%d ", p->fd);p = p->next;}putchar(10);
}#endif

实现效果如下:
在这里插入图片描述

IO 多路复用

借助 select, poll, epoll 机制,将新连接的客户端描述符添加到描述符表中,只需要一个线程即可处理所有的客户端连接,在嵌入式开发中应用广泛,但代码复杂度较高。
( 点此跳转到 IO 多路复用 )
在这里插入图片描述

总结
在这里插入图片描述

网络超时检测

使用原因
避免进程在没有数据时无休止阻塞,当到达设定的时间, 进程从原操作返回,继续执行下面的代码。

通过函数参数设置超时

select 超时检测

在这里插入图片描述
在这里插入图片描述

// 无超时fd_set readfds, tempfds;		// 1. 先构造一张有关文件描述符的表FD_ZERO(&readfds);				// 2. 清空表FD_SET(0, &readfds);			// 3. 将关心的文件描述符添加到表中FD_SET(fd, &readfds);int maxfd = fd;char buf[256] = {};while (1){tempfds = readfds;			// 4. 备份表select(maxfd+1, &tempfds, NULL, NULL, NULL);	// 5. 调用 select 函数if (FD_ISSET(0, &tempfds)){						// 6. 产生事件,进行相应处理fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf)-1] == '\n')buf[strlen(buf)-1] = '\0';printf("Keyboard: %s\n", buf);}if (FD_ISSET(fd, &tempfds)){int len = read(fd, buf, sizeof(buf));buf[len] = '\0';printf("Mouse: %s\n", buf);}}
// 添加超时fd_set readfds, tempfds;		// 1. 先构造一张有关文件描述符的表FD_ZERO(&readfds);				// 2. 清空表FD_SET(0, &readfds);			// 3. 将关心的文件描述符添加到表中FD_SET(fd, &readfds);int maxfd = fd;char buf[256] = {};while (1){tempfds = readfds;			// 4. 备份表struct timeval tiv = {3, 0};		// 3s + 0 msint ret = select(maxfd+1, &tempfds, NULL, NULL, &tiv);	// 5. 调用 select 函数if (ret < 0){perror("Failed to select");return -1;} else if (ret == 0){printf("Timeout! \n");continue;} else {}if (FD_ISSET(0, &tempfds)){						// 6. 产生事件,进行相应处理fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf)-1] == '\n')buf[strlen(buf)-1] = '\0';printf("Keyboard: %s\n", buf);}if (FD_ISSET(fd, &tempfds)){int len = read(fd, buf, sizeof(buf));buf[len] = '\0';printf("Mouse: %s\n", buf);}}

在这里插入图片描述

poll 超时检测

在这里插入图片描述

// 无超时struct pollfd fds[64];fds[0].fd = 0;fds[0].events = POLLIN;fds[1].fd = sockfd;fds[1].events = POLLIN;    int last = 1;char buf[256] = {};int accfd, receiver;struct sockaddr_in caddr;int length = sizeof(caddr);while (1){int getpoll = poll(fds, last+1, -1);if (getpoll < 0){perror("Failed to poll");return -1;}for (int i = 0; i < last+1; i++){if (fds[i].revents == POLLIN){if (fds[i].fd == 0){/* 相应处理 */}if (fds[i].fd == sockfd){/* 相应处理 */}}}}
// 添加超时struct pollfd fds[64];fds[0].fd = 0;fds[0].events = POLLIN;fds[1].fd = sockfd;fds[1].events = POLLIN;    int last = 1;char buf[256] = {};int accfd, receiver;struct sockaddr_in caddr;int length = sizeof(caddr);while (1){int getpoll = poll(fds, last+1, 3000);			// 设置 3000ms 超时检测if (getpoll < 0){perror("Failed to poll");return -1;} else if (getpoll == 0){printf("Timeout! \n");continue;} else {}for (int i = 0; i < last+1; i++){if (fds[i].revents == POLLIN){if (fds[i].fd == 0){/* 相应处理 */}if (fds[i].fd == sockfd){/* 相应处理 */}}}}

epoll 超时检测

在这里插入图片描述

// 无超时struct epoll_event ev, eves[16];ev.data.fd = 0;ev.events = EPOLLIN | EPOLLET;epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &ev);ev.data.fd = sockfd;ev.events = EPOLLIN | EPOLLET;epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);char buf[256] = {};while (1){ int num = epoll_wait(epfd, eves, 16, -1);if (num < 0){perror("Failed to epwait");return -1;}for (int i = 0; i < num; i++){if (eves[i].data.fd == 0){/* 相应处理 */}if (eves[i].data.fd == sockfd){/* 相应处理 */}}}
// 添加超时struct epoll_event ev, eves[16];ev.data.fd = 0;ev.events = EPOLLIN | EPOLLET;epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &ev);ev.data.fd = sockfd;ev.events = EPOLLIN | EPOLLET;epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);char buf[256] = {};while (1){ int num = epoll_wait(epfd, eves, 16, 3000);			// 设置 3000ms 超时检测if (num < 0){perror("Failed to epwait");return -1;} else if (num == 0){printf("Timeout! \n");continue;} else {}for (int i = 0; i < num; i++){if (eves[i].data.fd == 0){/* 相应处理 */}if (eves[i].data.fd == sockfd){/* 相应处理 */}}}

setsockopt() 设置套接字属性

#include<sys.socket.h>  
#include<sys/types.h> 
#include<sys/time.h>
int setsockopt(int sockfd, int level, int optname, void *optval, socklen_t optlen);功能:获得/设置套接字属性
参数:sockfd:	套接字描述符level:		协议层optname:	选项名optval:		选项值optlen:		选项值大小指针
返回值:		成功: 0                失败: -1

在这里插入图片描述
在这里插入图片描述

端口与地址复用

// 端口与地址复用 server.cint sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("Failed to create a socket");return -1;}printf("sockfd: %d\n", sockfd);struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr("0.0.0.0");int op = 1;			// 非 0 即可setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &op, sizeof(op));if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("Failed to bind");return -1;}

超时检测,打断接下来的阻塞

	while (1){struct timeval tiv = {2, 500};			// 设置 2.5s 超时检测setsockopt(acceptfd, SOL_SOCKET, SO_RCVTIMEO, &tiv, sizeof(tiv));int recvbyte = recv(acceptfd, buf, sizeof(buf), 0);        if (recvbyte < 0){perror("Failed to receive");// return -1;			// 注释掉,每2.5s接收不到客户端消息,打印一次上一行代码} else if (recvbyte == 0){printf("Client exited\n");break;} else {if (!strcmp(buf, "quit")){flag = 1;break;}printf("%s\n", buf);}}

在这里插入图片描述

alarm() 定时器 + sigaction() 修改信号的行为

alarm() 定时器

若 alarm(n); 则 n 秒后,会有 SGIALRM信号 产生,从而终止程序。

// 程序退出alarm(2);while (1){}
// 2s后,程序退出,打印 “闹钟” 或 "Alarm clock"

但将 alarm(n); 置于死循环中,则不会终止程序。

while (1){alarm(2);
}
// 死循环
// 程序退出char buf[64] = {};
while (1){alarm(2);fgets(buf, sizeof(buf), stdin);		// 有阻塞		// 不输入任何内容
}
// 2s后,程序退出,打印 “闹钟” 或 "Alarm clock"

sigaction 修改信号的行为

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);功能:对接收到的指定信号处理   
参数: 1. signum  信号	2. act:设置新行为   oldact:设置旧行为 
返回值: 成功: 0失败: -1结构体如下:  struct sigaction {     void (*sa_handler)(int); 			// 函数指针其他的结构体成员如mark(信号集),flag(对信号的标记)都不常用};==================== 需要定义一个函数 ====================void handler(int sig){printf("timeout .....\n");}   

在这里插入图片描述

超时检测

#include <stdio.h>
#include <signal.h>
#include <unistd.h>void handler(int sig){printf("Timeout! \n");
}int main(int argc, char const *argv[])
{struct sigaction act;sigaction(SIGALRM, NULL, &act);     // 获取 SIGALRM信号 原来的属性act.sa_handler = handler;           // 修改属性sigaction(SIGALRM, &act, NULL);     // 写回属性char buf[64] = {};while (1){alarm(2);						// 2s后,产生一个 SIGALRM信号if (fgets(buf, sizeof(buf), stdin) == NULL){perror("Failed to get buffer");continue;}printf("Nothing. \n");}return 0;
}

运行结果如下:
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/209663.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Airtest结合Poco对控件实施精准截图,学起来!

1.前言 最近在Q群内发现有个小伙伴提出了一个很有趣的脚本需求&#xff0c;想要实现“通过选择器获取到了控件&#xff0c;然后截图这个控件范围”&#xff0c;根据我们的Airtest的局部截图接口以及poco控件的属性查询接口是可以很快实现的~ 2.接口查找 首先我们需要知道我们…

电脑序列号查询

电脑序列号是厂商给每台电脑分配的一个产品识别码&#xff0c;也称为S/N&#xff08;Serial Number&#xff09;。主要用来查询电脑的出厂日期、保修状态、生产产地、产品配置等信息。电脑序列号查询有以下几种方法&#xff1a; 1、电脑机箱外壳&#xff1b; 2、系统信息/命令…

【QML】StackView上层页面半透明,显示下层页面

1、 应用场景 有时候需要模拟弹窗效果&#xff0c;需要下层的页面半透明显示。仅仅将上层页面背景设置为透明并不能实现这个效果&#xff0c;下层的页面依然被覆盖。Qt帮助文档中有如下代码&#xff0c;经测试有效果。 2、 代码 重点标记&#xff1a; 下层页面需要设置这个…

C#winfrom端屏幕截图功能的简单实现(修改了屏幕的缩放比例后,截图功能异常,慎用!!!)

文章目录 1 主要文件1.1 FrmScreenShot.cs1.2 FrmScreenShot.Designer.cs1.1 Utility.cs 在发现有一款播放软件禁止截图功能后&#xff0c;使用了其他的截图工具发现都会被播放软件禁用掉截图功能&#xff0c;想了下试着自己做一个截图工具&#xff0c;也可以方便将截图工具添加…

【VRTK】【VR开发】【Unity】8-可交互对象

【概述】 之前我们只是用了一个简单方块作为可交互对象。其实可交互对象可以有许多细节设置,包括具体抓握物体的哪个点,指定抓握的方向,指定Secondary Action允许两手互换抓握,双手抓握,用两手改变物体大小等。 【拾取物体】 要让一个物体能够被拾取,必须设置它为可互…

诊断0x27服务解密文件DLL制作与使用

DLL文件在CANoe的使用方法 DLL文件在诊断里面可以用在0x27秘钥服务里面&#xff0c;对解密有帮助&#xff0c;在下图位置加载。 DLL文件制作 vector公司本来就给了我们一个demo&#xff0c;先拷贝一份下来&#xff0c;别把原来的文件给改坏了。我这个是CANoe12&#xff0c;de…

35的程序员被辞了可以自己接外包啊?为什么都那么悲观呢?

35的年纪&#xff0c;上有老下有小&#xff0c;即将步入中年危机&#xff0c;在这个节骨眼上被辞&#xff0c;能不悲观吗&#xff1f; 在这个年纪人们往往追求的是稳定的工作和生活&#xff0c;而进入一个自己不熟悉的行业并不是一个好的选择。 况且&#xff0c;你认为的外包…

PHP众筹系统源码+支持报名众筹+商品众筹+无偿众筹+市面上所有的众筹模式 附带完整的搭建教程

大家好啊&#xff0c;罗峰今天来给大家分好用的源码系统了。今天要给大家分享的是一款PHP众筹系统源码。众筹作为一种新型的融资方式&#xff0c;逐渐在市场上占据了重要的地位。从公益众筹到商品众筹&#xff0c;再到股权众筹&#xff0c;各种众筹模式层出不穷。然而&#xff…

SELinux refpolicy详解(1)

本文部分内容参考&#xff1a; SELinux - ArchWiki SELinux_百度百科 一、SELinux介绍 1. SELinux简介 SELinux&#xff08;Security-Enhanced Linux&#xff0c;安全增强型Linux&#xff09;是美国国家安全局&#xff08;NSA&#xff09;对于强制访问控制的实现&#xff0…

element-plus的el-dropdown去除鼠标悬浮或点击时的黑边框

设置为outline:unset;或者outline:none;即可 :deep(.el-tooltip__trigger:focus-visible) {outline: unset;}

flutter 输入框组件 高度问题

使用的组件名字为 TestField 组件 TestField 配置 占位文字 设置 decoration 属性 InputDecoration 中hintText去掉输入到 输入框的间距 InputDecoration 中contentPadding EdgeInsets.zero去掉边框中的间距 InputDecoration 中 使用 isDense:true设置输入框内文字的颜色 …

【OpenCV+OCR】计算机视觉:识别图像验证码中指定颜色文字

文章目录 1. 写在前面2. 读取验证码图像3. 生成颜色掩码4. 生成黑白结果图5. OCR文字识别6. 测试结果 【作者主页】&#xff1a;吴秋霖 【作者介绍】&#xff1a;Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作&#xff01; 【…