代码还没有实测。已经修改了。
B站就业班视频代码搬运
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
?poll 模型和select模型的作用相似,用于I/O 多路复用
是结构体pollfd 组成的 数组的首地址,
struct pollfd
{
int fd;
short events;
short revents;
};
(1)int fd :文件描述符。如果fd == -1 表明内核不再监控。
(2)short events :输入参数,让内核去监视的事件,就像是 select 中让内核去监视读事件,写事件,异常事件
(3)short revents :输出参数,表示告诉内核,有哪些事件发生了改变。
调用 poll 函数,当监视的文件描述符有事件发生,内核修改的是 revents,而不修改 events 和 fd,这样做events 和 fd 就可以重用;poll 返回的是 revents 。
表示内核监控的范围,具体:数组最大下标 ?+1
也就是说,现在数组开头6个文件描述符,? 0? , 100, 200, 0,300, 400,然后后面的文件描述符都是0 ,ndfs就是 5 ,因为最大的不是0的文件描述符400的下标是5 ,表示poll监视着6个的变化。所以这个参数的含义是:第一个参数中,让内核监控的文件描述符的个数
=0 ?不阻塞,立刻返回
?-1 ?表示一直阻塞,直到有事件发生,
?> 0 表示阻塞时长,在时长范围内如果有事件发生会立刻返回,如果超时,也会立刻返回,
POLLIN ?表示监控读事件
POLLPRI 紧急事件去读
POLLOUT ?可写事件
POLLERR ?错误事件。但是仅仅返回在revents , 在events 中错误被忽略了。
读事件,客户端有连接过来,才能读, 而写事件基本上用不着监控(主动写发送的),除非写的太多把缓冲区写满了。。
一个正数, 代表revents 不是0的 个数,表示的意思是有事件返回或者有错误返回的文件描述符的总数。
如果返回0,表示没有任何文件描述符有变化。
如果返回-1 ,表明有一个错误errno没有被正确设置。
- 创建socket ,得到监听文件描述符 lisenfd; ????--socket()
- 设置端口复用 ?--setsockopt();
- 绑定 --bind ()
- 定义pollfd结构体的数组fdarray[1024]
第一次写的代码,不知道怎么的有BUG ,第一个开启的客户端完全正常,后面再连接的客户端,打字了服务器没有丝毫反应,必须等客户1的信息完成后,它们之前发的信息才全部从服务器返回,但是看网络状态netstat -a | grep '8888' 又是完全正常的,都是established 状态。。
原因已经找到,poll的第二个参数写错了 。。等待明天修改
正确的写法:老师设置了一个变量maxindex, 表示poll函数的第一个参数中,不为-1的 文件描述符的那个最大位置的下标,例如, 这里有8个文件描述符,?
-1, 20,-1,-1,10, 1, -1,? ? 3,? -1那么maxindex =7, 指向那个3? 。
maxindex +1 就是poll函数的第二个参数,写进去。表示内核监控 这8个文件描述符。最后那个-1 不管( 数组里,都是下标从0 开始,最后一个 - 1? 下标是 8)
//poll模型 第1次代码,
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <poll.h>
int main() {
int sfd = socket(AF_INET, SOCK_STREAM, 0);
//设置端口复用
int opt =1;
setsockopt ( sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
//定义一个服务器地址的 结构体,利用本机任意网络地址接口,端口为8888
struct sockaddr_in servad;
bzero(&servad, sizeof(servad));
servad.sin_family =AF_INET;
servad.sin_port =htons(8888);
servad.sin_addr.s_addr = htonl (INADDR_ANY);
int ret = bind(sfd, (struct sockaddr*)&servad, sizeof( servad));
//将socket从主动变为被动(服务器必备),监听来自客户的请求
listen(sfd,128);
//定义pollfd 结构体,并建立一个1024长度的数组fdarray ,
struct pollfd fdarray[1024];
//把all文件描述符都变成-1
int i =0;
for (i =0; i<1024; i++){
fdarray[i].fd = -1;
}
fdarray[0].fd = sfd;
fdarray[0].events = POLLIN;
//zhuy注意别写成revents了,现在监听呢,没有什么返回,没有revents
int newfd ;
int nready =0;
int sockfd;
int maxindex =0;
while (1) {
//因为超时时间设置为NULL ,所以进程会无限阻塞在这一步,除非有了变化才往下走
nready = poll (fdarray, maxindex+1, -1);
//第二个参数每次有变化的描述符+1 ,第二个参数也跟着加1, 表明客户端连接来的
//越多,内核监控的描述符跟着增多
if (nready < 0) {
if (errno == EINTR){
continue;
}
break;
}
//第一种情况,有客户端的新请求
//但是我现在不懂为什么新连接请求是pollin 这不是可写事件
if (fdarray[0].revents == POLLIN) {
newfd = accept (sfd,NULL, NULL);
//寻找在fdarray中最靠近的不是-1的位置方下newfd
for (i =0;i<1024;i++){
if( fdarray[i].fd ==-1){
fdarray[i].fd = newfd;
fdarray[i].events = POLLIN;
break;
}
}
if ( i==1024){
close (newfd);
continue;
}
if (maxindex <i ){
maxindex =i;
}
}
//第二种情况,眼前的描述符,有可读事件发生
//为什么不从0开始遍历?0是你的监听描述符
for (i =1;i <1024;i++){
if (fdarray[i].fd ==-1 ){
continue;
}
// i如果不能使用是要关闭的,为了避免代码变化,用sockfd代替i
sockfd = fdarray[i].fd;
char buf[256];
//读数据
memset(buf,0x00,sizeof(buf));
int n =read (sockfd, buf, sizeof(buf));
if (n <=0){
printf("这里是服务器端, read error or client close\n");
close (sockfd);
fdarray[i].fd =-1;
}
else {
printf ("这里是服务器端n =%d ,读到的是%s \n", n,buf);
//将小写字母都转化wie大写
for (int j =0; j<n; j++) {
buf[j] =toupper (buf[j]);
}
//发送数据
write (sockfd, buf, n);
}
}
}
close (sfd);
return 0;
}
#include <poll.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
} while (0)
int main(int argc, char *argv[]){
int nfds, num_open_fds;
struct pollfd *pfds;
if (argc < 2) {
fprintf(stderr, "Usage: %s file...\n", argv[0]);
exit(EXIT_FAILURE);
}
num_open_fds = nfds = argc - 1;
pfds = calloc(nfds, sizeof(struct pollfd));
if (pfds == NULL){
errExit("malloc");
}
/* Open each file on command line, and add it 'pfds' array */
for (int j = 0; j < nfds; j++) {
pfds[j].fd = open(argv[j + 1], O_RDONLY);
if (pfds[j].fd == -1){
errExit("open");
}
printf("Opened \"%s\" on fd %d\n", argv[j + 1], pfds[j].fd);
pfds[j].events = POLLIN;
}
/* Keep calling poll() as long as at least one file descriptor is open */
while (num_open_fds > 0) {
int ready;
printf("About to poll()\n");
ready = poll(pfds, nfds, -1);
if (ready == -1){
errExit("poll");
}
printf("Ready: %d\n", ready);
/* Deal with array returned by poll() */
for (int j = 0; j < nfds; j++) {
char buf[10];
if (pfds[j].revents != 0) {
printf(" fd=%d; events: %s%s%s\n", pfds[j].fd,
(pfds[j].revents & POLLIN) ? "POLLIN " : "",
(pfds[j].revents & POLLHUP) ? "POLLHUP " : "",
(pfds[j].revents & POLLERR) ? "POLLERR " : "");
}
if (pfds[j].revents & POLLIN) {
ssize_t s = read(pfds[j].fd, buf, sizeof(buf));
if (s == -1)
errExit("read");
printf(" read %zd bytes: %.*s\n",s, (int) s, buf);
}
else { /* POLLERR | POLLHUP */
printf(" closing fd %d\n", pfds[j].fd);
if (close(pfds[j].fd) == -1)
errExit("close");
num_open_fds--;
}
}
}
printf("All file descriptors closed; bye\n");
exit(EXIT_SUCCESS);
}
/* Suppose we run the program in one terminal, asking it to open a FIFO:
? ? ? ? ? ?$ mkfifo myfifo
? ? ? ? ? ?$ ./poll_input myfifo
? ? ? ?In a second terminal window, we then open the FIFO for writing, write some data to ?it, ?and ?close ?the
? ? ? ?FIFO:
? ? ? ? ? ?$ echo aaaaabbbbbccccc > myfifo
? ? ? ?In the terminal where we are running the program, we would then see:
*/
这段代码首先定义了 2个整数 nfds? 和 num_open_fds ,让它们等于你写的参数的个数,。也就是说它默认你必须在命令行写出? ?./脚本名? ?文件名? (文件名可以不止一个),你写几个文件,这篇代码就打开几个文件,然后把open函数返回的 fd 文件描述符,全部纳入内核的监控,然后让poll 函数管理。
? ? ? ?然后当这些描述符有变化时,先判断事件类型
?(pfds[j].revents & POLLIN) ?? "POLLIN " ?: "",
这行的意思是,返回结果revents和POLLIN这个宏,做“ 按位与” 运算,如果结果是POLLIN ,说明revents 就是POLLIN, 那么这行字符串结果就是 'POLLIN'? ?不然这个字符串就是 “”
,然后当revents 就是POLLIN时,开始读取,你刚才在另一终端写的东西都会体现在当前终端的屏幕上。