/*
* File: main.c
* Author: xys
*
* Created on 2013年12月12日, 下午3:14
*
* 实现功能:通过epoll,处理多个socket请求
* 监听一个端口,监听到有链接时,添加epoll_event
*
* 命令行下 telnet localhost 3304
*/
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <fcntl.h>
#define MYPORT 3304
//最多处理的connect
#define MAX_EVENTS 500
//当前连接数
int currentClinet = 0;
//数据接受buf
#define REVLEN 1024
char recvBuf[REVLEN];
//epoll相关
//epoll 描述符
int epollfd;
//事件数组
struct epoll_event eventList[MAX_EVENTS];
void AcceptConn(int srvfd);
void RecvData(int fd);
void setSockNonBlock(int sock);
int main(int argc, char** argv) {
int sockListen;
struct sockaddr_in server_addr;
struct epoll_event event;
int yes = 1;
//socket
if((sockListen = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("socket error.\n");
return -1;
}
//in case of 'address already in use' error message
if (setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))) {
perror("setsockopt failed");
exit(EXIT_FAILURE);
}
//设置sock为non-blocking
setSockNonBlock(sockListen);
//创建要bind的socket address
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(MYPORT);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//bind
if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){
printf("bind error.\n");
return -1;
}
//listen
if(listen(sockListen, 5) < 0){
printf("listen error.\n");
return -1;
}
//1.epoll 初始化
epollfd = epoll_create(MAX_EVENTS);
if(epollfd == -1){
perror("epoll create failed.");
exit(EXIT_FAILURE);
}
event.events = EPOLLIN|EPOLLET;
event.data.fd = sockListen;
//2.epoll_ctrl
if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sockListen, &event) < 0){
printf("epoll add fail : fd = %d\n", sockListen);
return -1;
}
//epoll
while(1){
int n = 0;
int timeout = 3000;
//3.epoll_wait
int ret = epoll_wait(epollfd, eventList, MAX_EVENTS, timeout);
if(ret < 0){
if(errno == EINTR && epollfd > 0){
usleep(1*1000);
continue;
}
printf("epoll wait failed\n");
break;
}else if(ret == 0){
fprintf(stderr, "no socket ready for read within %d secs\n",
timeout/1000);
sleep(1);
continue;
}
//直接获取了事件的量,给出了活动的流,这里是和poll区别的关键
//检测到ret个IO file descriptor的events,loop各个fd进行响应
for(n = 0; n < ret; n++){
//错误退出
/*events[n]即为检测到的event,域events[n].events表示具体哪些events,
域events[n].data.fd即对应的IO fd*/
if((eventList[n].events & EPOLLERR) ||
(eventList[n].events & EPOLLHUP) ||
!(eventList[n].events & EPOLLIN))
{
//由于events[i].events使用每个bit表示event,因此判断是否包含某个具体事件可以使用`&`操作符
printf("epoll error.\n");
close(eventList[n].data.fd);
return -1;
}
//对检测到event的各IO fd进行响应
if(eventList[n].data.fd == sockListen){
//当前fd是server的socket,不进行读而是accept所有client连接请求
AcceptConn(sockListen);
}else{
//当前fd是client连接的socket,可以读(read from client)
RecvData(eventList[n].data.fd);
//不删除
//epoll_ctl(epollfd, EPOLL_CTL_DEL, eventList[n].data.fd, &eventList[n]);
}
}
}
close(epollfd);
close(sockListen);
printf("test.\n");
return 0;
}
/**************************************************
函数名:AcceptConn
功能:接受客户端的链接
参数:srvfd:监听SOCKET
***************************************************/
void AcceptConn(int srvfd)
{
int confd;
struct epoll_event event;
char client_ip_str[INET_ADDRSTRLEN];
struct sockaddr_in sin;
socklen_t len = sizeof(struct sockaddr_in);
bzero(&sin, len);
confd = accept(srvfd, (struct sockaddr*)&sin, &len);
if (confd == -1) {
if ( (errno == EAGAIN) || (errno == EWOULDBLOCK) ) {
//non-blocking模式下无新connection请求,跳出while (1)
return;
} else {
perror("accept failed");
exit(EXIT_FAILURE);
}
}
if (!inet_ntop(AF_INET, &(sin.sin_addr), client_ip_str, sizeof(client_ip_str))) {
perror("inet_ntop failed");
exit(EXIT_FAILURE);
}
printf("accept a client from: %s, client=%d\n", client_ip_str, confd);
//设置conn_sock为non-blocking
setSockNonBlock(confd);
//4.epoll_wait
//将新建的链接添加到EPOLL的监听中
event.data.fd = confd;
event.events = EPOLLIN|EPOLLET;
if ( epoll_ctl(epollfd, EPOLL_CTL_ADD, confd, &event) == -1 ) {
perror("epoll_ctl(EPOLL_CTL_ADD) failed\n");
exit(EXIT_FAILURE);
}
}
//读取数据
void RecvData(int fd)
{
int ret;
memset(recvBuf, 0, REVLEN);
printf("RecvData function.\n");
//recv数据
ret = recv(fd, (char *)recvBuf, sizeof(recvBuf), 0);
if(ret == 0){
return;
}
//数据接受完毕
printf("client=%d,buf=%s\n", fd, recvBuf);
}
//函数:设置sock为non-blocking mode
void setSockNonBlock(int sock)
{
int flags;
flags = fcntl(sock, F_GETFL, 0);
if (flags < 0) {
perror("fcntl(F_GETFL) failed");
exit(EXIT_FAILURE);
}
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("fcntl(F_SETFL) failed");
exit(EXIT_FAILURE);
}
}
socket epoll 模型
最新推荐文章于 2023-12-29 16:46:20 发布