linux学习笔记19-使用epoll
简言
前面学习linux系统的网络通信,套接字(socket),学习是同步的阻塞的socket,正是因为是阻塞的,才有epoll的学习.如何将socket设置为非阻塞的
将socket设置为非阻塞的void setnonblocking(int st)
{
int opt = fcntl(st, F_GETFL); //fcntl是对文件描述符提供控制,并非只对socket
if (opt < 0)
{
printf("fcntl failed:%s\n", strerror(errno));
return;
}
opt = opt | O_NONBLOCK;
if (fcntl(st, F_SETFL, opt) < 0) //socket返回的也是文件描述符,通过opt将该文件描述符设置为非阻塞的
{
printf("fcntl set failed:%s\n", strerror(errno));
return;
}
}
先贴代码(具体等下节在详细深入)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <iconv.h>
//将gbk编码字符转换utf8字符集
int gbk2utf8(char *src, size_t *srclen, char *dest, size_t *destlen)
{
//源字符集为gbk,目标字符集为utf8
iconv_t result = iconv_open("UTF8", "GBK");
if (result == (iconv_t)-1)
{
printf("open iconv failed:%s\n", strerror(errno));
return -1;
}
size_t rc = iconv(result, &src, srclen, &dest, destlen);
if (rc == (size_t)-1)
{
printf("iconv error:%s\n", strerror(errno));
return -1;
}
iconv_close(result);
return 0;
}
int socket_create(int port)
{
int st = socket(AF_INET, SOCK_STREAM, 0);
int on = 1;
//设置socket可以重用
if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
{
printf("setsockopt failed:%s\n", strerror(errno));
return -1;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY); //设置ip为任意
if (bind(st, (struct sockaddr *)&addr, sizeof(addr)) == -1) //绑定端口号
{
printf("bind port (%d) failed:%s\n", port, strerror(errno));
return -1;
}
if (listen(st, 2000) == -1) //开始监听
{
printf("listen failed:%s\n", strerror(errno));
return -1;
}
return st;
}
int socket_accept(int st)
{
struct sockaddr_in client_addr;
int len = sizeof(client_addr);
memset(&client_addr, 0, len);
int client_st = accept(st, (struct sockaddr *)&client_addr, &len);
if (client_st < 0)
{
printf("socket_accept failed:%s\n", strerror(errno));
return -1;
}
else
{
printf("accept ip:%s\n", inet_ntoa(client_addr.sin_addr));
return client_st;
}
}
int socket_recv(int st)
{
char buf[1024];
memset(buf, 0, sizeof(buf));
int result = recv(st, buf, sizeof(buf), 0);
if (result < 0)
{
printf("socket_recv failed:%s\n", strerror(errno));
return -1;
}
else
{
char show_buf[1024];
size_t show_len = sizeof(show_buf);
size_t buf_len = (size_t)result;
//进行字符串转换
gbk2utf8(buf, &buf_len, show_buf, &show_len);
printf("recv msg:%s\n", show_buf);
return st;
}
}
//根据文件描述符,将文件(这里是套接字)设置为非阻塞
void setnonblocking(int st)
{
int opt = fcntl(st, F_GETFL); //
if (opt < 0)
{
printf("fcntl failed:%s\n", strerror(errno));
return;
}
opt = opt | O_NONBLOCK;
if (fcntl(st, F_SETFL, opt) < 0)
{
printf("fcntl set failed:%s\n", strerror(errno));
return;
}
}
int main(int argc, char *argv[])
{
if (argc < 2)
{
printf("argc < 2\n");
return -1;
}
int port = atoi(argv[1]);
int listen_st = socket_create(port);
if (listen_st == -1)
{
return -1;
}
struct epoll_event ev, events[2000]; //声明epoll_event变量,ev用于注册事件,events用于回传要处理的事件
int epoll_fd = epoll_create(2001); //指定创建最大的句柄数量为100
setnonblocking(listen_st); //将socket设置为非阻塞模式
ev.data.fd = listen_st; //关联要处理事件相关的文件描述符
ev.events = EPOLLIN | EPOLLERR | EPOLLHUP; //设置要处理事件的类型,分别可读/错误/被挂断的事件
int epoll_result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_st, &ev); //注册epoll事件
int st = 0;
for (;;)
{
//epoll_wait 最后一个参数 -1为一直等待下去,0为立马返回
//等待epoll事件的触发
int fds = epoll_wait(epoll_fd, events, 2000, -1);
if (fds == -1)
{
printf("epoll_wait failed:%s\n", strerror(errno));
break;
}
int i;
for (i = 0; i < fds; i++)
{
printf("listenfd=%d fd=%d", listen_st, events[i].data.fd);
if (events[i].data.fd < 0)
{
continue;
}
if (events[i].data.fd == listen_st) //监测到listen_st接收客户端请求
{
st = socket_accept(listen_st);
if (st >= 0)
{
setnonblocking(st);
ev.data.fd = st; //设置epoll event的文件描述符和要处理的事件
ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLET;
//将epoll事件添加到epoll池中
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, st, &ev);
printf("client fd=%d\n", st);
continue;
}
}
if (events[i].events & EPOLLIN) //收到socket数据
{
st = events[i].data.fd;
if (socket_recv(st) < 0)
{
if (st > 0)
{
close(st);
}
events[i].data.fd = -1;
}
}
if (events[i].events & EPOLLERR) //发生错误
{
st = events[i].data.fd;
if (st > 0)
{
close(st);
}
events[i].data.fd = -1; //将描述符值为-1,相当于EPOLL_CTL_DEL
}
if (events[i].events & EPOLLHUP) //socket中断(或关闭)
{
st = events[i].data.fd;
if (st > 0)
{
close(st);
}
events[i].data.fd = -1;
}
}
}
close(epoll_fd); //使用完毕,一定要记得关闭
return 0;
}
画图理解epoll

秋风
2017-04-18