前言:
在阅读并且转载了华科师兄的ipoll详解 的文章之后,感觉自己需要亲手使用epoll实现一个程序才行,不然得来的知识是记不住的,于是决定修改UNIX网络编程中的回射客户端程序,改用epoll来实现。
程序流程:
传递一个ip地址给主程序,主程序连接成功后把标准输入流和套接字传递给str_cli子程序,该子程序就使用epoll实现。最终实现代码如下所示:
/*************************************************************************
> File Name: strcli_epoll.c
> Author: yinwen
> Mail: yinwenatbit@163.com
> 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程序设计的书里所有的代码都避免了。我之前照着书敲代码的时候完全没有想到这个问题。果然东西还是得自己重头亲手写出来才行。
输入结果截图:
这个结果里包含了很多排序的输出,不过不影响结果