网站建设微信运营销售,win2008 挂网站 404,美食网页设计图片,网站优化人员TCP 并发
由于tcp协议只能实现一对一的通信模式。为了实现一对多#xff0c;有以下的的处理方式
1. 多进程 开销大 效率低
2. 多线程 创建线程需要耗时 3. 线程池 多线程模型创建线程耗时问题#xff0c;提前创建 4. IO多路复用 在不创建进程和线程的前提下#xff0c;对…TCP 并发
由于tcp协议只能实现一对一的通信模式。为了实现一对多有以下的的处理方式
1. 多进程 开销大 效率低
2. 多线程 创建线程需要耗时 3. 线程池 多线程模型创建线程耗时问题提前创建 4. IO多路复用 在不创建进程和线程的前提下对多个文件描述符进行同时监测 IOfd读写 共用一个进程
1.多进程服务端
#includestdio.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include netinet/ip.h
#include arpa/inet.h
#include string.h
#include signal.h
#include sys/wait.h
#include unistd.hint init_tpc_ser(const char *ip , unsigned short port)
{int sockfd socket(AF_INET, SOCK_STREAM, 0);if(sockfd 0){perror(fail socket);return -1;}struct sockaddr_in ser;ser.sin_family AF_INET;ser.sin_port htons(port);ser.sin_addr.s_addr inet_addr(ip);int ret bind(sockfd,(struct sockaddr *)ser,sizeof(ser));if(ret 0){perror(fail bind );return -1;}return sockfd;}void handler(int signo)
{wait(NULL);
}int main(int agrc, char *agrv[])
{signal (17,handler);int sockfd init_tpc_ser(192.168.0.179,50000);int ret listen (sockfd,100);char buff[1024] {0};if(ret 0){perror(fail listen);return -1;}while(1){int connfd accept(sockfd,NULL,NULL);if(connfd 0){perror(fail accept);return -1;}pid_t pid fork();if(pid 0){}else if(pid 0){while(1){memset(buff, 0 ,sizeof(buff));int ret recv(connfd,buff ,sizeof(buff),0);printf(%s\n,buff);if(ret 0){return -1;}strcat(buff,-----ok);ret send(connfd, buff,sizeof(buff),0);if(ret 0){return -1;} }return 0;}else{printf(fork fail);return -1;}}close(sockfd);return-1;}2.多线程服务端
#includestdio.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include netinet/ip.h
#include arpa/inet.h
#include string.h
#include signal.h
#include sys/wait.h
#include unistd.h
#include pthread.hint init_tpc_ser(const char *ip , unsigned short port)
{int sockfd socket(AF_INET, SOCK_STREAM, 0);if(sockfd 0){perror(fail socket);return -1;}struct sockaddr_in ser;ser.sin_family AF_INET;ser.sin_port htons(port);ser.sin_addr.s_addr inet_addr(ip);int ret bind(sockfd,(struct sockaddr *)ser,sizeof(ser));if(ret 0){perror(fail bind );return -1;}return sockfd;}char buff[1024] {0};
void* task(void *reg)
{int connfd *(int *)reg;//传过来保存就不会收到其他线程影响while(1){memset(buff, 0 ,sizeof(buff));int ret recv(connfd,buff ,sizeof(buff),0);printf(%s\n,buff);if(ret 0){break;}strcat(buff,-----ok);ret send(connfd, buff,sizeof(buff),0);if(ret 0){break;} }close(connfd);pthread_detach(pthread_self());//设置线程分离属性。主线程无暇回收次线程return 0;
}int main(int agrc, char *agrv[])
{int sockfd init_tpc_ser(192.168.0.179,50000);int ret listen (sockfd,100);int connfd 0;pthread_t tid 0;if(ret 0){perror(fail listen);return -1;}while(1){connfd accept(sockfd,NULL,NULL);if(connfd 0){perror(fail accept);return -1;}pthread_create(tid,NULL,task,connfd); }close(sockfd);return-1;}3.io多路复用具体见下
2.IO模型
Linux下常用的IO模型
1. 阻塞IO
fgets
read
getchar
fread
fgetc
recv
recvfrom
1. 让多个IO事件以同步方式处理顺序处理
2. 多个IO事件之间互相影响
3. CPU占有率低2. 非阻塞IO 将IO对应的文件描述符设置成非阻塞方式。O_NONBLOCKfcntl1.非阻塞一般搭配轮询方式同时监测多个IO2.cpu占有率高#include head.hint main(int argc, const char *argv[])
{mkfifo(./fifo, 0664); char buff[1024] {0};int fifofd open(./fifo, O_RDONLY);if (fifofd 0){perror(fail open fifo);return -1;}//1 获取文件描述符(终端)原flags特性int flags fcntl(0, F_GETFL);//2. 给原flags增加非阻塞特性flags flags | O_NONBLOCK;//3. 给文件描述符设置新flags特性fcntl(0, F_SETFL, flags);while (1){memset(buff, 0, sizeof(buff));fgets(buff, sizeof(buff), stdin);//不会堵塞在这里内核检测到键盘输入自动运行printf(STDIN : %s\n, buff);memset(buff, 0, sizeof(buff));read(fifofd, buff, sizeof(buff));printf(FIFO : %s\n, buff);}close(fifofd);return 0;
}3. 信号驱动IO SIGIO1.实现异步IO操作节省CPU开销2.只能针对比较少的IO事件信号很少#include head.hchar buff[1024] {0};
void handler (int signo)
{memset(buff, 0, sizeof(buff));fgets(buff, sizeof(buff), stdin);printf(STDIN : %s\n, buff);}
int main(int argc, const char *argv[])
{signal(SIGIO,handler);mkfifo(./fifo, 0664); int fifofd open(./fifo, O_RDONLY);if (fifofd 0){perror(fail open fifo);return -1;}//1 获取文件描述符原flags特性int flags fcntl(0, F_GETFL);//2. 给原flag异步flags flags |O_ASYNC;//3. 给文件描述符设置新flags特性fcntl(0, F_SETFL, flags);//关联信号与当前进程fcntl(0,F_SETOWN,getpid());while (1){memset(buff, 0, sizeof(buff));read(fifofd, buff, sizeof(buff));printf(FIFO : %s\n, buff);}close(fifofd);return 0;
}4. IO多路复用IO多路转接 在不创建新的进程和线程的前提下 在一个进程中同时监测多个IO
1. select1. 文件描述符集合以数组位图的方式保存最多监测1024个文件描述符。2. 文件描述符集合创建在应用层需要应用层和内核层反复拷贝3. 内核层返回整个文件描述符集合需要用户层遍历查找到达事件的文件描述符4. 只能工作在水平触发模式低速模式
2. poll1. 文件描述符集合以链表的方式保存监测文件描述符可超过1024。2. 文件描述符集合创建在应用层需要应用层和内核层反复拷贝3. 内核层返回整个文件描述符集合需要用户层遍历查找到达事件的文件描述符4. 只能工作在水平触发模式低速模式
3. epoll 1. 文件描述符集合以树型结构红黑树保存监测文件描述符可超过1024 提高了数据的查找速度。2. 文件描述符集合创建在内核层3. 返回的是到达事件的文件描述符集合无需遍历查找4. 可以工作在水平触发模式低速模式也可以工作在边沿触发模式高速模式1.select
创建文件描述符集合 2. 添加关注的文件描述符到集合 3. 传递给内核内核开始监测IO事件 select 4. IO事件到达则返回结果–》 2.哪个fd事件到达执行哪个
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
功能通知内核监测IO事件并返回事件结果
参数nfds最大文件描述符1readfds: 文件描述符读事件集合writefds文件描述符写事件集合exceptfds其他timeout 超时时间
返回值成功返回达到的事件的个数失败-1设置超时超时时间到但没有事件到达0void FD_CLR(int fd, fd_set *set);int FD_ISSET(int fd, fd_set *set);void FD_SET(int fd, fd_set *set);void FD_ZERO(fd_set *set);
#include head.hint main(int argc, const char *argv[])
{mkfifo(./fifo, 0664); char buff[1024] {0};int fifofd open(./fifo, O_RDONLY);if (fifofd 0){perror(fail open fifo);return -1;}int maxfd 0;fd_set rdfds;//创建集合FD_ZERO(rdfds);//清0//加入集合FD_SET(0,rdfds);FD_SET(fifofd,rdfds);maxfd fifofd maxfd ?fifofd:maxfd;fd_set rdfdscopy;while(1){rdfdscopy rdfds;//防止rdfds被修改//发送位图给内核int cnt select(maxfd1,rdfdscopy,NULL,NULL,NULL);if(cnt 0){perror(fail select);return -1;}//判断是否事件到达if(FD_ISSET(0,rdfdscopy)){memset(buff,0,sizeof(buff));fgets(buff,sizeof(buff),stdin);printf(stdin : %s,buff);}if(FD_ISSET(fifofd,rdfdscopy)){memset(buff,0,sizeof(buff));read(fifofd,buff,sizeof(buff));printf(fifo : %s\n,buff);}}close(fifofd);return 0;
}select实现并发tcp
#include head.hint init_tcp_ser(const char *ip, unsigned short port)
{int sockfd socket(AF_INET, SOCK_STREAM, 0);if (sockfd 0){perror(fail socket);return -1;}struct sockaddr_in ser;ser.sin_family AF_INET;ser.sin_port htons(port);ser.sin_addr.s_addr inet_addr(ip);int ret bind(sockfd, (struct sockaddr *)ser, sizeof(ser));if (ret 0){perror(fail bind);return -1;}ret listen(sockfd, 100);if (ret 0){perror(fail listen);return -1;}return sockfd;
} int main(int argc, const char *argv[])
{char buff[1024] {0};int sockfd init_tcp_ser(192.168.0.166, 50000); if (sockfd 0){return -1;}struct sockaddr_in cli;socklen_t clilen sizeof(cli);int maxfd 0;//1、 创建文件描述符集合fd_set rdfds, rdfdstmp;FD_ZERO(rdfds);//2. 将关注的监听套接字加入到集合FD_SET(sockfd, rdfds);maxfd sockfd;while (1){//3. 内核开始检测套接字事件rdfdstmp rdfds;int cnt select(maxfd1, rdfdstmp, NULL, NULL, NULL);if (cnt 0){perror(fail select);return -1;}// 是否有监听套接字事件到达if (FD_ISSET(sockfd, rdfdstmp)){int connfd accept(sockfd, (struct sockaddr *)cli, clilen);if (connfd 0){perror(fail accept);return -1;}printf([%s : %d] get online!\n, inet_ntoa(cli.sin_addr), ntohs(cli.sin_port));FD_SET(connfd, rdfds);maxfd maxfd connfd ? maxfd : connfd;}//循环遍历剩余套接字是否有通讯套接字事件到达for (int i sockfd1; i maxfd; i){if (FD_ISSET(i, rdfdstmp)){memset(buff, 0, sizeof(buff));ssize_t size recv(i, buff, sizeof(buff), 0);if (size 0){perror(fail recv);FD_CLR(i, rdfds);close(i);continue;}else if (0 size){FD_CLR(i, rdfds);close(i);continue;}printf(%s\n, buff);strcat(buff, -----ok!);size send(i, buff, strlen(buff), 0);if (size 0){perror(fail recv);FD_CLR(i, rdfds);close(i);continue; }}}}close(sockfd);return 0;
}