前言:
在阅读并且转载了华科师兄的ipoll详解 的文章之后,感觉自己需要亲手使用epoll实现一个程序才行,不然得来的知识是记不住的,于是决定修改UNIX网络编程中的回射客户端程序,改用epoll来实现。
程序流程:
传递一个ip地址给主程序,主程序连接成功后把标准输入流和套接字传递给str_cli子程序,该子程序就使用epoll实现。最终实现代码如下所示:
/************************************************************************* > File Name: strcli_epoll.c > Author: yinwen > Mail: [email protected] > Created Time: Sat 13 Jun 2015 04:21:01 PM CST ************************************************************************/ #include "unp.h" #include <sys/epoll.h> #define EPOLLSIZE 1024 void str_cli(FILE* fp, int sockfd) { char sendline[MAXLINE], recvline[MAXLINE]; struct epoll_event events; int epollfd; int i,n, fpno, num, j; int stdineof =0; epollfd = epoll_create(EPOLLSIZE); fpno = fileno(fp); events.events = EPOLLOUT; events.data.fd = fpno; epoll_ctl(epollfd, EPOLL_CTL_ADD, fpno, &events); events.data.fd = sockfd; events.events = EPOLLIN; epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &events); struct epoll_event* res = calloc(EPOLLSIZE, sizeof(struct epoll_event)); j =0; while((n = epoll_wait(epollfd, res, 4, -1)) >0) { for(i=0; i<n; i++) { printf("res[0].data.fd is %d and events is %d\n", res[0].data.fd, res[0].events); printf("res[1].data.fd is %d and events is %d\n", res[1].data.fd, res[1].events); if(res[i].data.fd == sockfd) { printf("now is in sockfd\n"); if((num = read(sockfd,recvline, MAXLINE)) ==0) { if(stdineof ==1) return; else err_quit("str_cli: server terminated prematurely"); } write(fpno, recvline, num); } } if(res[i].data.fd == fpno) { printf("now is in stdin\n"); if(( num = read(fpno, sendline, MAXLINE)) ==0) { stdineof =1; shutdown(sockfd, SHUT_WR); continue; } write(sockfd, sendline, num); } j++; printf("j is %d\n", j); } }遇到的问题:
编写这个程序的时候本来以为能轻松解决的,没想到居然花了我3个小时来调试。
1.epoll_ctl使用错误:
最容易犯的错误直接就犯了,忘记调用该函数把套接字加入了。
2.if判断错误:
这里又犯了一个脑残错误,if里比较返回的套接字,居然使用的一个=号,变成了赋值,结果程序一直阻塞在read调用之上
3.判断的先后顺序:
我现在贴在上面的代码,在调用了epoll_wait之后,先对比套接字,再对比标准输入。这才是正确的顺序。我第一次编写的时候顺序反过来了。所以第一次调用的epoll_wait的时候返回结果是只有标准输入可以写,写入之后发给了服务器程序,然后服务器程序发回给套接字。但是这时需要重新调用epoll_wait才能收到可读的信息。
第二次调用epoll_wait,当然标准输入还是可以写的,于是要先输入第二次发送的结果,然后才会进入读取套接字的程序。所以最后的结果变成了这次写入字符串下一次返回。
为了解决这个问题,我最开始使用gdb进行调试,并且好好复习了gdb的使用,然后发现收到的数据是对的,只不过顺序不对,于是决定在程序里直接加入输出顺序的printf语句,终于发现是顺序反了。
其实这个问题UNIX程序设计的书里所有的代码都避免了。我之前照着书敲代码的时候完全没有想到这个问题。果然东西还是得自己重头亲手写出来才行。
输入结果截图:
这个结果里包含了很多排序的输出,不过不影响结果