生活随笔
收集整理的這篇文章主要介紹了
IO多路转接之poll
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
IO多路轉接之poll
文章目錄
一、Poll
int poll(struct pollfd
*fds
, nfds_t nfds
, int timeout
);
參數作用
| struct pollfd *fds | fds是一個poll函數監聽的結構列表. 每一個元素中, 包含了三部分內容: 文件描述符, 監聽的事件集合, 返回的事件集合 |
| nfds_t nfds | nfds表示fds數組的長度 |
| timeout | timeout表示poll函數的超時時間, 單位是毫秒(ms) |
返回值
- 返回值小于0, 表示出錯;
- 返回值等于0, 表示poll函數等待超時;
- 返回值大于0, 表示poll由于監聽的文件描述符就緒而返回
- 2.理解struct pollfd *fds
struct pollfd
{int fd
; short events
; short revents
;
};
其中enent和revent的取值(主要取值),不清楚可以找Man
取值描述
| POLLIN | 數據可讀(普通數據和優先級數據) |
| POLLOUT | 數據可寫(普通數據和優先級數據) |
| POLLERR | 錯誤 |
-
3.socket就緒條件
-
讀就緒
-
sochet內核中,接收緩沖區中的字節數,大于等于低水位標記SO_RECVLOWAT,此時可以無阻塞的讀該文件描述符,并且返回值大于0
-
socket TCP通信中,對端關閉連接,此時對該socket讀,則返回0 監聽的socket上有新的連接請求時
-
socket上有未處理的錯誤時
-
寫就緒
-
socket內核中,發送緩沖區中的可用字節數(發送緩沖區的空閑位置大小),大于等于低水位標記,SO_SNDLOWAT,此時可以無阻塞的寫,并且返回值大于0
-
socket的寫操作被關閉(close或者shutdown),對于一個寫操作被關閉的socket進行寫操作,會觸發SIGPIPE信號
-
socket使用非阻塞connect連接成功或失敗之后 socket上有未讀取的錯誤
-
異常就緒
-
socket上收到帶外數據(關于帶外數據,和TCP緊急模式相關,在TCP報頭中有一個緊急指針的字段)
-
4.poll的優點及缺點
poll的優點
- 不同與select使用三個位圖來表示三個fdset的方式,poll使用一個pollfd的指針實現.pollfd結構包含了要監視的event和發生的event,不再使用select“參數-值”傳遞的方式. 接口使用比 select更方便.
- poll并沒有最大數量限制 (但是數量過大后性能也是會下降).
poll的缺點
- poll中監聽的文件描述符數目增多時 和select函數一樣,poll返回后,需要輪詢pollfd來獲取就緒的描述符.
- 每次調用poll都需要把大量的pollfd結構從用戶態拷貝到內核中. 同時連接的大量客戶端在一時刻可能只有很少的處于就緒狀態,因此隨著監視的描述符數量的增長, 其效 率也會線性下降
- 5.基于poll實現tcp服務器(多用戶)
#pragma once
#include <functional>
#include "tcp_socket.hpp"
#include <poll.h>
#include <unistd.h>
#include <cstring>
#include <vector>
#include <string>
#include <iostream>class Poll
{public:Poll():fds_(nullptr),fds_size_(15),nfds_(0),timeout_(1000){fds_
=new pollfd
[fds_size_
];}bool Add(TcpSocket
& sock
){if(nfds_
== fds_size_
){pollfd
* new_fds
= new pollfd
[2 * fds_size_
];memcpy(new_fds
,fds_
,fds_size_
* sizeof(pollfd
));fds_size_
*= 2;delete[] fds_
;fds_
= new_fds
;}fds_
[nfds_
].fd
= sock
.GetFd();fds_
[nfds_
].events
= POLLIN
;++nfds_
;return true;}bool Del(TcpSocket
& sock
){int del_fd
= sock
.GetFd();for(size_t i
= 0;i
< nfds_
;++i
){if(fds_
[i
].fd
== del_fd
){fds_
[i
] = fds_
[--nfds_
];break;}}return true;}bool Wait(std
::vector
<TcpSocket
>& list
,int timeout
= 1000){timeout_
=timeout
;int ret
= poll(fds_
,nfds_
,timeout_
);if(ret
< 0){perror("Poll Wait");return false;}else if(ret
== 0){std
::cout
<<"poll wait timeout"<<std
::endl
;return false;}for(nfds_t i
= 0;i
< nfds_
;++i
){if(fds_
[i
].revents
== POLLIN
){TcpSocket
sock(fds_
[i
].fd
);list
.push_back(sock
);}}return true;}int GetNfds(){return nfds_
;}const pollfd
* GetFds(){return fds_
;}private:pollfd
* fds_
;nfds_t fds_size_
;nfds_t nfds_
;int timeout_
;
};typedef std
::function
<void(const std
::string
& req
,std
::string
* resp
)> Handler
;class TcpPollServer
{public:TcpPollServer(const std
::string
& ip
,const uint16_t port
):ip(ip
),port(port
){}~TcpPollServer(){_sock
.Close();}bool Start(Handler handler
){if(!_sock
.Socket())return false;if(!_sock
.Bind(ip
,port
))return false;if(!_sock
.Listen())return false;Poll poll
;poll
.Add(_sock
);while(1){std
::vector
<TcpSocket
> list
;if(!poll
.Wait(list
))continue;for(size_t i
= 0;i
< list
.size();++i
){if(list
[i
].GetFd() == _sock
.GetFd()){TcpSocket NewSock
;if(!_sock
.Accept(&NewSock
,NULL,NULL))continue;printf("客戶端已連接!~\n");poll
.Add(NewSock
);}else{std
::string req
;int n
= list
[i
].Recv(&req
);if(n
== 0){poll
.Del(list
[i
]);list
[i
].Close();continue;}else{std
::cout
<<"客戶端發送:"<<req
<<std
::endl
;std
::string resp
;handler(req
,&resp
);list
[i
].Send(resp
);}}}}return true;}private:TcpSocket _sock
;std
::string ip
;uint16_t port
;
};
總結
以上是生活随笔為你收集整理的IO多路转接之poll的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。