1.單循環服務器
2.并發服務器
1. 設置socket屬性
2. 進程
?3. 線程
3.多路IO復用模型 - 提高并發程度
1. 區別
2. IO處理模型
1. 阻塞IO模型
2. 非阻塞IO模型
3. 信號驅動IO
4. IO多路復用
3. 特點
4. 函數接口
1. select
2. poll
3. epoll
半包
1.單循環服務器
?
2.并發服務器
1. 設置socket屬性
原型:int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
功能:
在bind前 設置socket的屬性
參數:
????????sockfd????????要設置的socket
????????level???????????設置socket層次//socket本身 tcp ip
????????optname????選項名字
????????optval????????選項值
????????optlen????????長度
設置一個選項(開啟一個功能))---讓地址重用,即可停止后立即使用
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int));
2. 進程:
ps aux | grep ser????????查找包含ser的進程
signal(SIGCHILD, do_child)???????父進程接收子進程結束信號exit(0)后回收子進程資源,防止僵尸態
void do_child(int signo)
{????????wait(NULL);}
?
3. 線程:
退出個體線程使用pthread_exit(NULL)
pthread_join()會阻塞程序,無法接收多個客戶端信息,所以選擇pthread_detach()回收資源
3.多路IO復用模型 - 提高并發程度
1. 區別
多進程和多線程:一個客戶端對應一個進程或線程
IO多路復用???????:多個客戶端對應一個進程或線程
2. IO處理模型
1. 阻塞IO模型:
特點:簡單,低效
i - 讀:scanf, getchar,read,recv
? ? ? ? ? ? 操作:從終端stdin讀取數據 =>?阻塞等待讀取數據 =>?返回用戶空間o- 寫:pipe(一直向管道寫入數據,管道內存滿時阻塞)
2. 非阻塞IO模型
特點:輪詢,CPU負擔重
操作:從終端stdin讀取數據 => 不等待讀取數據 => 直接返回用戶空間
? ? ? ? ? ?O_NONBLOCK 打開文件時可以設置為非阻塞模式
3. 信號驅動IO
設置非阻塞:fcntl,并與信號結合
特點:處理多路IO時資源數量有限
- 原型:int fcntl(int fd, int cmd, ...)
- 功能:
維護文件描述符
- 參數
????????fd??????????????????要操作的fd
????????cmd??????????????要做的操作? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? F_GETFL? ? ? ? ? ? ? ? ? ?開啟異步信號
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? F_SETFL? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? F_SETOWN? ? ? ? ? ? ? 設置與信號的關聯
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????? fcntl(fd, F_SETOWN, gtpid());
????????...??????????????????可變參數? ? ? ? ? ? ? ? ? ? ? ? ? ? ?eg:printf(const char *format,...);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("hello");????????printf("a = %d\n", a);
- 返回值
成功時返回值取決于所作的操作
與信號的結合:signo
4. IO多路復用
3. 特點
1. 可以處理多路IO
2. 不需要阻塞
3. 不需要輪詢
4. 函數接口
1. select
- 原型:int select(int nfds,
????????????????????????fd_set *readfds,
????????????????????????fd_set *writefds,
????????????????????????fd_set *exceptfds,
????????????????????????struct timeval *timeout);- 功能:
? ? ? ? 實現IO多路復用
- 參數
????????????????nfds? ? ? ? ? ? ? ? ? ? ? 文件描述符中最大文件描述符+1
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 遍歷時即可遍歷到自己? for(i = 0; i < nfds; i++)
????????????????readfds? ? ? ? ? ? ? ? ?讀操作的文件描述符的集合
????????????????writefds? ? ? ? ? ? ? ? 寫操作的文件描述符的集合
????????????????exceptfds? ? ? ? ? ? ?異常的文件描述符的集合
????????????????timeout? ? ? ? ? ? ? ? ?設置超時時間? ? ? ? ? ? ? ?struct timeval_t = {3, 0}; //阻塞3秒? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?NULL表示select是阻塞功能
????????????????????????????????????????????????????????????????0? ? ? ? ?-? 非阻塞
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? n > 0? -? 每次阻塞的時間
- 返回值
? ? ? ? 成功返回就緒的文件描述符的數量
? ? ? ? 失敗返回-1
1. 輔助函數
void FD_CLR(int fd, fd set *set);? ? ? ? ? ? ? ? // 將fd從set集合中清除
int FD_ISSET(int fd, fd_set *set);???????????????// 判斷fd是否在set中
void FD_SET(int fd, fd_set *set);? ? ? ? ? ? ?// 將fd添加到set集合中
void FD_ZERO(fd_set *set);t*set); ????????????// 將set集合清空
2. 步驟
1. 建立一張表? ? ? ? 監控
? ? ? ? fd_set readfds ;? ? ? ? ? ? ? ? ? ? ? ? 建立表
????????FD_ZERO(&readfds)? ? ? ? ? ? ? ? 清空表
2. 將要監控的文件描述符加入表中
????????FD_SET(0,&readfds);? ? ? ? ? ? ?0表示標準輸入
????????FD_SET(fd,&readfds);?
3.準備參數
????????nfds = fd + 1
? ? ? ? select(nfs, &readfds, NULL, NULL, NULL);
4. 監控文件描述符
3. 缺點
1.最大監聽數受限:FD_SETSIZE 默認1024(Linux)
2.每次調用需重置fdSet:內核會修改集合,必須每次重新
3.用戶態與內核態拷貝開銷大
4.返回后仍需遍歷所有fd才能知道哪個就緒
5.效率隨fd數量增長下降明顯
4. 點對點聊天
server.c:
client.c:
超時設置:
5. 多路IO復用
server.c
client.c:
???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????(退出.quit不管用)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 解決:?使用strncpy函數除去輸入的\n
2. poll
- 原型:int poll(struct pollfd *fds, nfds_t nfds, int timeout);
????????struct pollfd {
? ? ? ? ? ? ? ?int ? fd; ? ? ? ? /* file descriptor */
? ? ? ? ? ? ? ?short events; ? ? /* requested events */
? ? ? ? ? ? ? ?short revents; ? ?/* returned events */
? ? ? ? ? ?};
- 功能:
? ? ? ? 對文件描述符監控
- 參數:
? ? ? ? ? ? ? ?events? ? ? ? ? ? ? ? 事件:POLLIN? ? ? ? 讀
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? POLLOUT? ? ?寫
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? POLLERR? ? ?錯誤
? ? ? ? ? ? ? ?revents? ? ? ? ? ? ? ? 返回就緒的事件? ? ? ? ? ? ? ? nfds? ? ? ? ? ? ? ? ? ? 監控的文件描述符的個數
? ? ? ? ? ? ? ? timeout? ? ? ? ? ? ? ? 時間值,-1阻塞? ? ? ? 時間值,單位ms
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0非阻塞
- 返回值
? ? ? ? 成功返回就緒文件的數量,0 超時情況下沒有就緒事件
? ? ? ? 失敗返回 -1
1. 步驟
1. 建立監控表? ? ? ??
? ? ? ? struct pollfd fds[10];? ? ? ? // 監控10個fd
2. 將要監控的文件描述符加入表中
? ? ? ? 兩路:int nfds = 0;
? ? ? ? ? ? ? ? ? ?fds[0].fd = 0;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fds[1].fd = sockfd;
? ? ? ? ? ? ? ? ? ?fds[0].events = POLLIN | POLLOUT;? ? ? ? ? fds[0].events = POLLIN | POLLOUT;
? ? ? ? ? ? ? ? ? ?nfds++;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? nfds++;
? ? ? ? ? ? ? ? ? ?// 關心讀和寫,只會返回就緒的事件
3.準備參數
4. 監控文件描述符
2. 改進與缺點
- 相比select的改進
1.無1024限制:只要系統允許打開足夠多fd
2.無需重置集合:^events和」revents丶分離
3.更清晰的事件機制
4.效率更高:僅遍歷傳入的數組,不遍歷整個fd范圍
- 仍存在的問題
1.每次調用仍需將整個fds[]拷貝到內核
2.返回后仍需遍歷全部元素查找就緒fd
3.時間復雜度仍是O(n),連接數多時性能下降
3. 點對點聊天
server.c:
client.c:
4. 多路IO復用
server.c:
3. epoll
1. epoll_create
- 原型:int epoll_create(int size)
- 功能:
? ? ? ? 創建一個epoll對象
- 參數:
? ? ? ? size? ? ? ? 忽略,但是必須大于0
- 返回值
? ? ? ? 成功返回epoll對象的fd
? ? ? ? 失敗返回-1
2. epoll_ctl
- 原型:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
- 功能:
? ? ? ? 控制epoll對象
- 參數:
? ? ? ? epfd? ? ? ? ? ? ? ? ? ? ? ? ? epoll對象的fd
????????op? ? ? ? ? ? ? ? ? ??EPOLL_CTL_ADD? ? ? ? ?添加
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??EPOLL_CTL_MOD? ? ? ? 修改
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? EPOLL_CTL_DEL? ? ? ? ? 刪除
????????fd? ? ? ? ? ? ? ? ? ? ?????????關心的文件描述符
????????event? ? ? ? ? ? ? ?事件?? ? ? ? ? ? ? ?
????????????????????????????????typedef union epoll_data {
? ? ? ? ? ? ? ?????????????????????????void? ? ? ? ? ?*ptr;
? ? ? ? ? ? ? ?????????????????????????int? ? ? ? ? ? ? ?fd;
? ? ? ? ? ? ????????????????????????? ?uint32_t ? ? u32;
? ? ? ? ????????????????????????? ? ? ?uint64_t ? ? u64;
? ? ? ? ??????????????????????? } epoll_data_t;? ? ? ? ? ?-------------------------------------------------------------------------------------------------------
????????????????????????????????struct epoll_event {
? ? ? ? ? ? ????????????????????????? ?uint32_t ? ? events; ? ? ?// EPOLLIN? ? ? ? ? ? ? ? 讀? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //?EPOLLOUT? ? ? ? ? ? ?寫
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // EPOLLERR? ? ? ? ? ? ?出錯
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // EPOLLET? ? ? ? ? ? ? ? 邊沿觸發
? ? ? ? ? ? ??????????????????????????epoll_data_t data;? ? ? ??
? ? ? ? ? ??????????????????????};
- 返回值
? ? ? ? 成功返回0,失敗返回-1
中斷
1. 水平(電平)觸發
? ? ? ? 有數據就會一直通知
2. 邊沿觸發
? ? ? ? 每完成從沒數據到有數據的過程,通知一次
3. epoll_wait
- 原型:int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
- 功能:
? ? ? ? 監控對應文件描述符,查看是否就緒
- 參數:
????????epfd? ? ? ? ? ? ? ? ? ? epoll對象
????????events? ? ? ? ? ? ? ? 保存就緒結果的數組的首地址
????????maxevents? ? ? ? ? 數組大小????????timeout? ? ? ? ? ? ? ? 設置超時時間,單位ms
- 返回值:
? ? ? ? 成功返回就緒數量,失敗返回-1
4. 步驟
1. 創建一個epoll對象 -- 監控的表
? ? ? ? int epfd = epoll_create(2);
2. 添加文件描述符
? ? ? ? int add_fd(int fd, int epfd)
? ? ? ? {
? ? ? ? ????????struct epoll_event ev;
? ? ? ? ????????ev.events = EPOLLIN;
? ? ? ? ????????ev.data.fd = fd;
? ? ? ? ? ? ? ? if( epoll_ctl(epfd,EPOLL_CTL_ADD, fd, &ev) < 0)
? ? ? ? ? ? ? ? {? ? ? ? perror("fail to add");? ? ? ? return -1;}
? ? ? ? ? ? ? ? return 0;
????????}
????????add_fd(0,?epfd);
????????add_fd(fd,?epfd);
刪除文件描述符
????????int del_fd(int fd, int epfd)?
????????{
?? ?????????if ( epoll_ctl(epfd,EPOLL_CTL_DEL, fd, NULL))
?? ?????????{????????perror("fail to delte");????????return -1;?}?? ?
?????????? ?return 0;
????????}
?3. 監控文件描述符
5. 點對點聊天
server.c
client.c
半包
想要接受4000的數據,但網絡層一次只能打包1500字節的數據
解決:多接受幾次,注意越界判斷