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

TCP并发服务器

1.阻塞IO

        CPU占用率低,等待资源时将任务挂起,不占用CPU资源,等到拿到资源后继续向下执行
2.非阻塞IO 
       能够让任务不阻塞,效率低,因为没有数据时,CPU一直空转

fcntl(fd,F_GETFL);获得文件描述符的属性

flags |= O_NONBLOCK;在现有属性中加入非阻塞属性 

fcntl(fd,F_SETFL,flags);将新属性设置回文件描述符

#include "head.h"int main(void)
{int fd = 0;char tmpbuff[4096] = {0};int flags;ssize_t nsize = 0;char *pret = NULL;mkfifo("/tmp/myfifo", 0777);fd = open("/tmp/myfifo", O_RDONLY);if (-1 == fd){perror("fail to open");return -1;}/* 获得fd文件描述符的属性 */flags = fcntl(fd, F_GETFL);/* 在现有属性中加入非阻塞属性 */flags |= O_NONBLOCK;/* 将新属性设置回fd文件描述符 */fcntl(fd, F_SETFL, flags);flags = fcntl(0, F_GETFL);flags |= O_NONBLOCK;fcntl(0, F_SETFL, flags);while (1){memset(tmpbuff, 0, sizeof(tmpbuff));nsize = read(fd, tmpbuff, sizeof(tmpbuff));if (nsize > 0){printf("FIFO:%s\n", tmpbuff);}memset(tmpbuff, 0, sizeof(tmpbuff));pret = gets(tmpbuff);if (NULL != pret){printf("STDIN:%s\n", tmpbuff);}  }close(fd);return 0;
}

3.异步IO
    将一个文件描述符设定为异步IO,当IO有事件发生时,内核会向用户层发送SIGIO信号提醒用户层处理事件 
 signal(SIGIO,handle);

flag = fcntl(fd,F_GETFL);   

flag |= O_ASYNC;//将fd设置为异步IO(文件描述符,发生可读事件时,会发送信号通知)
fcntl(fd,F_SETFL,flag);//把异步IO事件的接收进程设置为当前进程,通知给当前进程
fcntl(fd,F_SETOWN,getpid());

#include "head.h"int fd = 0;void handler(int signo)
{char tmpbuff[4096] = {0};memset(tmpbuff, 0, sizeof(tmpbuff));read(fd, tmpbuff, sizeof(tmpbuff));printf("RECV:%s\n", tmpbuff);return;
}int main(void)
{char tmpbuff[4096] = {0};int flags;signal(SIGIO, handler);mkfifo("/tmp/myfifo", 0777);fd = open("/tmp/myfifo", O_RDONLY);if (-1 == fd){perror("fail to open");return -1;}flags = fcntl(fd, F_GETFL);flags |= O_ASYNC;//将fd设置为异步IO(文件描述符发生可以读的事件,会发送信号通知)fcntl(fd, F_SETFL, flags);//通知给当前进程fcntl(fd, F_SETOWN, getpid());while (1){memset(tmpbuff, 0, sizeof(tmpbuff));gets(tmpbuff);printf("STDIN:%s\n", tmpbuff);}close(fd);return 0;
}

4.多路复用IO
   select
        监听文件描述符集合,将所有要监听的事件加入集合中,使用select监听所有事件,当集合中有事件发生,  select不再阻塞,同时select会将产生事件的文件描述符留在集合中,而把没有产生事件的文件描述符从集合中踢出,所以留在集合中的文件描述即为产生事件的文件描述符,对其处理即可

注意:因为select会把没有产生事件的文件描述符从集合中踢出,所以要用一个临时变量tmp来使用select

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
          功能:
            监听文件描述符是否有事件发生
          参数:
            nfds:最大文件描述符的值 + 1 
            readfds:读文件描述符集合
            writefds:写文件描述符集合
            exceptfds:异常文件描述符集合
            timeout:超时时间
          返回值:
            成功返回产生事件的文件描述符个数
            失败返回-1 

void FD_CLR(int fd, fd_set *set);
        功能:将fd从集合中清除

int  FD_ISSET(int fd, fd_set *set);
        功能:判断fd是否仍在文件描述符集合中
void FD_SET(int fd, fd_set *set);
        功能:将fd加入文件描述符集合中
void FD_ZERO(fd_set *set);
        功能:将文件描述符集合清0  

练习:

#include "head.h"int CreateTcpConnection(const char *pip, int port)
{int sockfd = 0;int ret = 0;struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(port);seraddr.sin_addr.s_addr = inet_addr(SER_IP);sockfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == sockfd){return -1;}ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (-1 == ret){return -1;}return sockfd;
}int HandleConnection(int sockfd)
{char tmpbuff[4096] = {0};static int cnt = 0;ssize_t nsize = 0;sprintf(tmpbuff, "hello world --- %d", cnt);nsize = send(sockfd, tmpbuff, strlen(tmpbuff), 0);if (-1 == nsize){return -1;}cnt++;memset(tmpbuff, 0, sizeof(tmpbuff));nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);  if (-1 == nsize){return -1;}else if (0 == nsize){return 0;}printf("RECV:%s\n", tmpbuff);return nsize;
}int main(void)
{int sockfd = 0;int ret = 0;sockfd = CreateTcpConnection(SER_IP, SER_PORT);if (-1 == sockfd){printf("连接服务器异常\n");return -1;}while (1){ret = HandleConnection(sockfd);if (-1 == ret){printf("连接出错!\n");break;}else if (0 == ret){printf("连接关闭\n");break;}sleep(1);}close(sockfd);return 0;
}

 server.c

#include "head.h"int CreateListenSocket(const char *pip, int port)
{int sockfd = 0;int ret = 0;struct sockaddr_in seraddr;sockfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == sockfd){return -1;}seraddr.sin_family = AF_INET;seraddr.sin_port = htons(port);seraddr.sin_addr.s_addr = inet_addr(pip);ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (-1 == ret){return -1;}ret = listen(sockfd, 10);if (-1 == ret){return -1;}return sockfd;
}int HandleConnection(int confd)
{char tmpbuff[4096] = {0};ssize_t nsize = 0;nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);if (-1 == nsize){return -1;}else if (0 == nsize){return 0;}printf("RECV:%s\n", tmpbuff);sprintf(tmpbuff, "%s --- echo", tmpbuff);nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);if (-1 == nsize){return -1;}return nsize;
}int main(void)
{int sockfd = 0;int confd = 0;int ret = 0;fd_set rdfds;fd_set tmpfds;int nready = 0;int maxfd = 0;int i = 0;//创建监听套接字sockfd = CreateListenSocket(SER_IP, SER_PORT);if (-1 == sockfd){printf("创建监听套接字失败\n");return -1;}//将sockfd加入监听集合中FD_ZERO(&rdfds);FD_SET(sockfd, &rdfds);maxfd = sockfd;while (1){//开始监听tmpfds = rdfds;nready = select(maxfd+1, &tmpfds, NULL, NULL, NULL);if (-1 == nready){perror("fail to select");return -1;}//如果sockfd产生事件,处理新的连接请求,并将新的文件描述符加入集合,下次一起监听if (FD_ISSET(sockfd, &tmpfds)){confd = accept(sockfd, NULL, NULL);if (-1 == confd){FD_CLR(sockfd, &rdfds);close(sockfd);continue;}   maxfd = maxfd > confd ? maxfd : confd;FD_SET(confd, &rdfds);}//遍历所有已经连接的客户端中是否有事件发生for (i = sockfd+1; i <= maxfd; i++){if (FD_ISSET(i, &tmpfds)){ret = HandleConnection(i);if (-1 == ret){printf("连接异常\n");FD_CLR(i, &rdfds);close(i);continue;}else if (0 == ret){printf("连接断开\n");FD_CLR(i, &rdfds);close(i);continue;}}}}close(sockfd);return 0;
}


http://www.mrgr.cn/news/2229.html

相关文章:

  • 海康相机LinuxSDK CMakeLists.txt
  • flume系列之:定位flume没有关闭某个时间点生成的tmp文件的原因,并制定解决方案
  • 【嵌入式开发之网络编程】网络分层、OSI七层模型、TCP/IP及五层体系结构
  • PE文件格式
  • HarmonyOS开发:长列表界面实现详解(使用懒加载)
  • python3爬虫(未完结)
  • 「OC」探索CALayer:基础知识与实用技巧简要介绍
  • 【知识分享】ubuntu22.04-ESP32环境搭建
  • AI系列-黑神话:悟空
  • 如何将excel以文本形式储存的数字一键转换为数字
  • 8.20刷题笔记
  • SQL学习五大步骤
  • 可复制拖拽流程图
  • AI 进阶实战 | 走进大模型(LLM)+智能体(Agent)+提示词(Prompt)
  • 深度学习基础—Batch Norm
  • PMP考试不用慌,收下这份备考指南(附PMP学习资料包)
  • Tomcat部署项目get请求中文乱码
  • 为什么apple email的邮件显示不全
  • avx2 计算一个uint8_t数组的平方加速
  • react中 useContext 和useReducer的使用