服務器模型
tcp服務器:
socket
bind
listen
accept
recv/send
close
1.支持多客戶端訪問?
//單循環服務器?
socket
bind
listen
while(1)
{?? ?
?? ?accept
?? ?while(1)
?? ?{
?? ? ?recv/send
?? ?}
}
close
2.支持多客戶端同時訪問 (并發能力)?
并發服務器
socket
bind
listen
while(1)
{?? ?
?? ?connf = accept
?? ?pid_t pid = fork();
?? ?
?? ?if(pid > 0)
?? ?{
?? ??? ?continue;
?? ?}else if (pid == 0)
?? ?{
?? ??? ?while(1) //負責 與 客戶端通信的?
?? ??? ?{?
?? ??? ? ?recv/send
?? ??? ?}
?? ?}
}
close
2.1進程
socket
bind
listen
while(1)
{?? ?
?? ?connf = accept
?? ?pid_t pid = fork();
?? ?//出錯處理
?? ?if (pid == 0)
?? ?{
?? ??? ?while(1) //負責 與 客戶端通信的?
?? ??? ?{?
?? ??? ? ?recv/send
?? ??? ?}
?? ?}
}
close
2.2線程
void *handle_client(void *arg)
{
? ??? ?while(1) //子線程中 負責 與 客戶端通信的?
?? ??? ?{?
?? ??? ? ?recv/send
?? ??? ?}
}
socket
bind
listen
while(1)
{?? ?
?? ?connf = accept
?? ?pthread_create();
?? ?//出錯處理
}
close
-------------------------------------------------------------------------
三種服務器模型比較
1.單循環服務器?
2.并發服務器?
? 進程?
? 線程?
??
?? ??? ?1、簡單循環服務器
?? ??? ??? ?http?
?? ??? ??? ?web 服務器,apache--》cgi,php,perl,IIS--》asp,NGIX,Nlighty
?? ??? ??? ?
?? ??? ??? ?while(1)
?? ??? ??? ?{
?? ??? ??? ??? ?newfd = accept();
?? ??? ??? ??? ??? ?recv();
?? ??? ??? ??? ?close(newfd);
?? ??? ??? ?}
?? ??? ??? ?特點:可以接入多個客戶端的信息。
?? ??? ??? ?缺點:數據通信過程短,客戶端只能一次有效。
?? ??? ??? ??? ? ?實時性效果差。
?? ??? ?2、fork循環服務器===>每次有鏈接則fork一個子進程為該
?? ??? ??? ??? ??? ??? ??? ?鏈接處理通信過程,父進程繼續等待新鏈接。
?? ??? ??? ?while(1)
?? ??? ??? ?{
?? ??? ??? ??? ?newfd ?= accept();
?? ??? ??? ??? ?pid = fork()
?? ??? ??? ??? ?if(pid ?== 0)
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?///接收數據
?? ??? ??? ??? ?}
?? ??? ??? ??? ?if(pid < 0)
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?perror("fork");
?? ??? ??? ??? ??? ?return -1;
?? ??? ??? ??? ?}
?? ??? ??? ??? ?waitpid()
?? ??? ??? ?}
?? ??? ??? ?
?? ??? ??? ?特點:可以完成多個進程的實時交互,信息的完整性可以保證。
?? ??? ??? ?缺點:回收資源不方便,每次fork 占用系統資源多。
?? ??? ??? ??? ? ?可能出現僵尸進程
?? ??? ??? ??? ? ?
?? ??? ??? ?多線程:
?? ??? ??? ? ? 特點:
?? ??? ??? ? ? ? ? 創建速度快,調度快?
?? ??? ??? ? ? 缺點:
?? ??? ??? ? ? ? ? 線程共享進程資源,穩定性,安全性 較差?
?? ??? ??? ??? ? ??
? ? ? 3.并發的服務器模型 ---更高程度上的并發?
?? ? ?
? ? ? IO模型?
? ? ? 阻塞IO
?? ? ?非阻塞IO?
?? ? ?
?? ?1、阻塞IO ?
?? ? ? 用的最多。
?? ? ? 讀阻塞。
?? ? ? 寫阻塞。
?? ?2、非阻塞IO?
? ? ??? ?-1 errno EAGAIN ?whild(1){read()break;}忙等待
?? ??? ?control
? ? ? ? cntl?? ??? ?
?? ?3、IO多路復用
?? ?4、信號驅動IO ?SIGIO ?---異步?
?? ?5, 并行模型 進程,線程
??
? ? #include <unistd.h>
?? ?#include <fcntl.h>
?? ?int fcntl(int fd, int cmd, ... /* arg */ );
?? ?功能:修改指定文件的屬性信息。
?? ?參數:fd 要調整的文件描述符
?? ??? ? ?cmd 要調整的文件屬性宏名稱
?? ??? ? ?... 可變長的屬性值參數。
?? ?返回值:成功 ?不一定,看cmd
?? ??? ??? ?失敗 ?-1;
?? ??? ??? ? ?
?? ??? ??? ? ?
? ? int fcntl(int fd, int cmd, ... /* arg */ );
? ? ?//驅動:?? ??
?? ? //1.驅動程序 ---- 驅使硬件工作起來的程序?
?? ? 讓燈亮起來?
?? ??
?? ?eg:修改文件的非阻塞屬性:
?? ??? ?int flag ;
?? ??? ?flag ?= fcntl(fd,F_GETFL,0); ?///獲取fd文件的默認屬性到flag變量中。
?? ??? ?flag ?= flag | O_NONBLOCK; ? ?///將變量的值調整并添加非阻塞屬性
?? ??? ?fcntl(fd,F_SETFL,flag); ? ? ? ///將新屬性flag設置到fd對應的文件生效。
?? ??? ?以上代碼執行后的阻塞IO將變成非阻塞方式。
?? ?信號驅動IO
?? ?//signal
?? ?1.fcntl ?--- 設置 信號接受者?
?? ?flags = fcntl(fd,F_GETFL); //獲得當前標志?
?? ?fcntl(fd,F_SETFL,flags|O_ASYNC); //異步通信的標志?
?? ?//同步 通信
?? ?//異步 通信?
?? ?2.將該程序 和 SIGIO信號關聯起來?
?? ?fcntl(fd,F_SETOWN,pid);
?? ?3.設置信號處理函數?
?? ?signal?
?? ?owner //所有者?
?? ?缺點:
?? ??? ?處理的數量有限 ?(超過了1個不好判斷了)
?? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /---client1
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/?
? ? server <--------------->|-----client2
?? ? ? ? ? ? ? ? ? ? ? ? ? ? \?
?? ??? ??? ??? ??? ??? ??? ? ?\---client3
?? ??? ??? ??? ??? ??? ??? ??
?? ??? ??? ??? ??? ??? ??? ??
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | --->A
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/---client1(子進程/線程)-- | --->B
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/ ? ? ? ? ? ? ? ? ? ? ? ? ? | --->C
? ? server <--------------->|-----client2(子進程/線程)-- | --->D
?? ? ? ? ? ? ? ? ? ? ? ? ? ? \ ? ? ? ? ? ? ? ? ? ? ? ? ? | --->E
?? ??? ??? ??? ??? ??? ??? ? ?\---client3(子進程/線程)-- | --->F
?? ??? ??? ??? ??? ??? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | --->G
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??
?? ? ?
?? ?stdin ? //讀?
?? ?stdout ?//寫?
?? ?stderr ?//--寫出錯信息?
?? ??
?? ? IO多路服用?
?? ??
?? ? 多路 --- 多個輸入輸出?
?? ? 復用 --- 復用同一個進程或線程
? ? ?
? ?select?? ?//linux提供實現Io多路復用函數
? ?
? ?
? ? ???#include <sys/time.h>
? ? ? ?#include <sys/types.h>
? ? ? ?#include <unistd.h>
? ? ? ?int select(int nfds,?
?? ? ? ? ? ? ? ? ?fd_set *readfds,?
?? ??? ??? ??? ? ?fd_set *writefds,
? ? ? ? ? ? ? ? ? fd_set *exceptfds,?
?? ??? ??? ??? ? ?struct timeval *timeout);
?? ??? ??? ??? ? ?
?? ? ? 功能:
? ? ? ? ? ?io多路復用函數?
? ? ? ?參數:
? ? ? ? ? nfds ? ? ?//三個集合中最大的文件描述符 + 1
? ? ? ? ? readfds ?? ?//關心的 讀操作的 文件描述符的集合 ??
?? ??? ? ?writefds?? ?//關心的 寫操作的 文件描述符的集合 ?
?? ??? ? ?exceptfds //關心的 異常操作的 文件描述符的集合?
?? ??? ? ?timeout ? //
?? ??? ? ? ? ? ? ? ?NULL ?--- select 阻塞操作?
?? ??? ??? ??? ??? ?timeout --- 設定超時時間 ?> 0 ?等 這么長時間
?? ??? ??? ??? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ?== 0 不阻塞?
?? ??? ??? ??? ??? ?struct timeval {
?? ??? ??? ??? ??? ??? ? ? long ? ?tv_sec; ? ? ? ? /* seconds */
?? ??? ??? ??? ??? ??? ? ? long ? ?tv_usec; ? ? ? ?/* microseconds */
?? ??? ??? ??? ??? ? ? };?? ?
? ? ? ? ? ? ? ? ? ? ?? ??? ??? ??? ??? ?
? ? ? ?返回值:
?? ? ? ? ?成功 表示 就緒的文件描述符的數量
?? ??? ? ?失敗 -1 && errno設置?
?? ??? ? ?
? ? ? ?void FD_CLR(int fd, fd_set *set); //clear --- 將fd從 set中清除
? ? ? ?int ?FD_ISSET(int fd, fd_set *set); //判斷 set 表中 fd是否就緒
? ? ? ?void FD_SET(int fd, fd_set *set); //將fd設置(添加)到set中
? ? ? ?void FD_ZERO(fd_set *set); ? ? ? ?//表示將set表清零?
? ? ? ?
?? ? ??
?? ? ? 注意:
?? ? ? ? ?1. select 函數在監控到 有fd就緒后,
?? ??? ? ? ? 它會把未就緒的fd清除掉,每次需要重新獲取?
? ? ? ? ? 2. 超時一次后,時間的變量值為為0
? ? ? ? ? ? ?如果,需要每次都有超時時間,需要每次重新給值?
?? ??? ??? ??
?? ??? ? ?
?? ? ??
//單循環服務器?
//第1路IO ?fgets --收鍵盤 --- 打印出來 --- stdin 能不能讀??
//第2路IO ?讀管道數據 -- 修改 --- 發回去 ?--- 管道 能不能讀?
select?
1.建一張表?
? 放 要監控 的文件描述符
? readfds(); ?
2.添加 要監控的文件描述符 到表中?
? FD_SET()
3.select?
?
?
eg:
1. 管道雙向通信:
?? ?A.c ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?B.c
?? ??? ? ---------A2B----------------->
?? ??? ? <--------B2A-----------------
? ? A.c代碼如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>
//./a.out fifo_A2B fifo_B2A
int main(int argc, const char *argv[])
{if (argc != 3){printf("Usage: %s <fifo_A2B> <fifo_B2A>\n",argv[0]);return -1;}if(mkfifo(argv[1],0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}if(mkfifo(argv[2],0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}int fd1 = open(argv[1],O_WRONLY);int fd2 = open(argv[2],O_RDONLY);if (fd1 < 0 || fd2 < 0){perror("open fail");return -1;}//1.準備表 fd_set readfds;FD_ZERO(&readfds);//2.添加要監控的 fd2FD_SET(0,&readfds);FD_SET(fd2,&readfds);int nfds = fd2 + 1;int i = 0;char buf[1024];while (1){fd_set backfds = readfds;int ret = select(nfds,&backfds,NULL,NULL,NULL);if (ret < 0){perror("select fail");return -1;}if (ret > 0){for (i = 0; i < nfds; ++i){if (FD_ISSET(i,&backfds)){if (i == 0){printf("> ");fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] = '\0';write(fd1,buf,strlen(buf)+1);if (strncmp(buf,"quit",4) == 0){printf("1 exit......\n");exit(0);}}else if (i == fd2){printf("c> ");read(fd2,buf,sizeof(buf));printf("%s \n",buf);if (strncmp(buf,"quit",4) == 0){printf("2 exit......\n");exit(0);}}}}}}return 0;
}
B.c代碼如下:
??
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>//./a.out fifo_A2B fifo_B2A
int main(int argc, const char *argv[])
{if (argc != 3){printf("Usage: %s <fifo_A2B> <fifo_B2A>\n",argv[0]);return -1;}if(mkfifo(argv[1],0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}if(mkfifo(argv[2],0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}int fd1 = open(argv[1],O_RDONLY);int fd2 = open(argv[2],O_WRONLY);if (fd1 < 0 || fd2 < 0){perror("open fail");return -1;}//1.準備表 fd_set readfds;FD_ZERO(&readfds);//2.添加要監控的 fd2FD_SET(0,&readfds);FD_SET(fd1,&readfds);int nfds = fd1 + 1;int i = 0;char buf[1024];while (1){fd_set backfds = readfds;int ret = select(nfds,&backfds,NULL,NULL,NULL);if (ret < 0){perror("select fail");return -1;}if (ret > 0){for (i = 0; i < nfds; ++i){if (FD_ISSET(i,&backfds)){if (i == 0){printf("> ");fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] = '\0';write(fd2,buf,strlen(buf)+1);if (strncmp(buf,"quit",4) == 0){printf("1 exit......\n");exit(0);}}else if (i == fd1){printf("c> ");read(fd1,buf,sizeof(buf));printf("%s \n",buf);if (strncmp(buf,"quit",4) == 0){printf("2 exit......\n");exit(0);}}}}}}return 0;
}
?
?? ? ?
?