?單循環服務器:服務端同一時刻只能處理一個客戶端的任務
并發服務器:服務端同一時刻可以處理多個客戶端的任務
TCP并發服務器構建:
TCP:
1. 建立連接,一對一
TCP服務端并發模型:
1. ?多進程
進程資源開銷大;安全性高
2. ?多線程
線程相對與進程資源開銷小,相同資源環境下,并發量比進程大。
3. ?線程池
為了解決多線程或者多進程模型,在服務器運行過程,頻繁創建和銷毀線程(進程)帶來的時間消耗問題。
基于生產者和消費者編程模型,以及任務隊列等,實現的一套多線程框架。
4. ?IO多路復用
I-->O:fd
對多個文件描述符的讀寫可以復用一個進程。
? ? ? 在不創建新的進程和線程的前提下,使用一個進程實現對多個文件讀寫的同時監測。
? ? ? fgets(stdin);
recv(connfd);
阻塞IO模式:
1. 多個任務之間是同步的效果
? ? ? 1)select
2)poll
3)epoll
select實現IO多路復用:
? ? ? 1. 創建文件描述符集合 ? ? ? ? ? ? ? ? ?fd_set
2. 添加關注的文件描述符到集合 ? FD_SET();
3. 使用select傳遞集合表給內核,內核開始監測事件 ?select()
4. 當內核監測到事件時,應用層select將解除阻塞,并獲得相關的事件結果
5. 根據select返回的結果做不同的任務處理
void 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);
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
功能:傳遞文件描述符結合表給內核并等待獲取事件結果
參數:
nfds : 關注的最大文件描述符+1
readfds:讀事件的文件描述符集合
writefds:寫事件的文件描述符集合
exceptfds:其他事件的文件描述符集合
timeout:設置select監測時的超時時間
NULL : 不設置超時時間(select一直阻塞等待)
? ? ? ? 返回值:
成功:返回內核監測的到達事件的個數
失敗:-1
0 : 超時時間到達,但沒有事件發生,則返回0
利用select搭建并發服務器
#include "head.h"#define SER_PORT 50001
#define SER_IP "192.168.0.180"int init_tcp_ser()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){perror("socket error");return -1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(SER_PORT);seraddr.sin_addr.s_addr = inet_addr(SER_IP);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if(ret < 0){perror("bind error");close(sockfd);return -1;}ret = listen(sockfd,5);if(ret < 0){perror("listen error");close(sockfd);return -1;}return sockfd;
}int main()
{struct sockaddr_in cliaddr;socklen_t clilen = sizeof(cliaddr);int sockfd = init_tcp_ser();if(sockfd < 0){return -1;}fd_set sockfds;fd_set sockfdstmp;FD_ZERO(&sockfds);FD_SET(sockfd,&sockfds);int maxfd = sockfd;char buff[1024] = {0};while(1){sockfdstmp = sockfds;int cnt = select(maxfd + 1,&sockfdstmp,NULL,NULL,NULL);if(cnt < 0){perror("select error:");return -1;}if(FD_ISSET(sockfd,&sockfdstmp)){int connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&clilen);if(connfd < 0){perror("accept error:");return -1;}FD_SET(connfd,&sockfds);maxfd = maxfd > connfd ? maxfd : connfd;}for(int i = sockfd + 1;i <= maxfd;++i){if (FD_ISSET(i, &sockfdstmp)){memset(buff,0,sizeof(buff));ssize_t cnt = recv(i,buff,sizeof(buff),0);if(cnt < 0){perror("receive error:");FD_CLR(i,&sockfdstmp);close(i);continue;}else if(cnt == 0){printf("[%s:%d]:offline\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));FD_CLR(i, &sockfds);close(i);continue;}printf("[%s:%d]:%s\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port),buff);strcat(buff,"->ok");cnt = send(i,buff,strlen(buff),0);if(cnt < 0){perror("send error:");FD_CLR(i,&sockfdstmp);close(i);continue;}}}}close(sockfd);return 0;
}