1 如果说你不用muduo的net库,让你用epoll监听,你会怎样写服务器?
下面是我写的,为了让代码的逻辑更加清晰,我这里没有进行出错判断。
这只是一个简单的回射服务器
#include <iostream>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
#include <vector>
#include <sys/epoll.h>
#include <errno.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
using namespace std;
#define SERVPORT 8888
#define SERVIP INADDR_ANY
//使用epoll进行监听
//为了使得代码更加简洁,这里就不进行出错判断
typedef std::vector<struct epoll_event> EventList;
int main()
{
//忽略两个SIGPIPE和SIGCHLD信号
signal(SIGPIPE,SIG_IGN);
signal(SIGCHLD,SIG_IGN);
int conn;
struct sockaddr_in clieaddr;
socklen_t clieaddr_len = sizeof(clieaddr);
//首先创建一个套接字
int listenfd;
listenfd = socket(AF_INET,SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC,0);
//设置端口复用
int opt = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
//绑定端口和ip
struct sockaddr_in servaddr;
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htobe16(SERVPORT);
servaddr.sin_addr.s_addr = htobe32(SERVIP);
bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
//设置监听上限
listen(listenfd,SOMAXCONN);
//创建一个epoll
int epollfd;
epollfd = epoll_create1(EPOLL_CLOEXEC);
//将套接字加入到epoll监听中
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = listenfd;
epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&event);
//这是发生事件的个数
int nready;
EventList events(16);
//存放ip的地方
char ip[32];
//循环进行监听
while(1){
nready = epoll_wait(epollfd,&*events.begin(),static_cast<int>(events.size()),-1);
if(nready == -1){
perror("epoll_wait");
exit(-1);
}
if(nready == 0){
continue;
}
if(((size_t)nready == events.size())){
events.resize(events.size()*2);
}
for(int i = 0; i < nready; i++){
//判断监听到的事件
//如果是创建链接的套接字有事件发生
if(events[i].data.fd == listenfd){
//定义的客户端的套接字用来接收链接服务器的套接字
conn = accept4(listenfd,(struct sockaddr*)&clieaddr,&clieaddr_len,SOCK_NONBLOCK|SOCK_CLOEXEC);
cout<<"ip:"<< inet_ntop(AF_INET,&clieaddr.sin_addr,ip,sizeof(ip))<< " port:"
<<ntohs(clieaddr.sin_port)<<endl;
//打印客户端的ip和端口号
//cout<<"ip:"<<inet_ntop(AF_INET,&clieaddr.sin_addr.s_addr,ip,sizeof(ip))<< " ";
//cout<<"port:"<<ntohs(clieaddr.sin_port);
event.events = EPOLLIN;
event.data.fd = conn;
epoll_ctl(epollfd,EPOLL_CTL_ADD,conn,&event);
clients.push_back(conn);
}
//如果是客户端的套接字有事件发生
else if(events[i].events & EPOLLIN){
conn = events[i].data.fd;
//读取套接字里面的内容
char buf[1024] = {0};
int ret = read(conn,buf,sizeof(buf));
cout<<ret<<endl;
if(ret == 0){
epoll_ctl(epollfd,EPOLL_CTL_DEL,conn,&events[i]);
}
//对内容进行处理之后,将处理后的内容写入到客户端的套接字中
write(conn,buf,ret);
}
}
}
cout << "Hello world!" << endl;
return 0;
}
2 如果将循环的那部分都放在一个类中来实现
这个类就是Eventloop类
简单的设计eventloop类
其中EventLoop.h
#ifndef EVENTLOOP_H
#define EVENTLOOP_H
#include <sys/epoll.h>
#include <netinet/in.h>
#include <vector>
#include <stdio.h>
#include <error.h>
#include <stdlib.h>
#include <iostream>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
using namespace std;
typedef std::vector<struct epoll_event> EventList;
class EventLoop
{
public:
EventLoop();
~EventLoop();
//有一个设置epollfd_的函数
void setEpollfd(int epollfd);
//有一个设置listenfd的函数
void setListenfd(int listenfd);
//有一个事件循环函数
void loop();
//有一个退出循环的函数
void quit();
protected:
private:
bool looping_; //是否处于循环中
bool quit_; //是否退出循环
//事件循环中的epollfd和listenfd
int epollfd_;
int listenfd_;
};
#endif // EVENTLOOP_H
EventLoop.cpp
#include "EventLoop.h"
EventLoop::EventLoop()
{
//ctor
looping_ = false;
quit_ = false;
}
EventLoop::~EventLoop()
{
//dtor
}
//有一个设置epollfd_的函数
void EventLoop::setEpollfd(int epollfd)
{
epollfd_ = epollfd;
}
//有一个设置listenfd的函数
void EventLoop::setListenfd(int listenfd)
{
listenfd_ = listenfd;
}
//有一个事件循环函数
void EventLoop::loop()
{
/*
这里面,目前必须要的是,
1.服务器的监听套接字 listenfd_
2.epoll监听的epollfd epollfd_
3.用来接受epoll监听事件返回的数组
4.客户端的套接字,和客户端的socket地址的定义
5.一个event用来接受客户端的套接字和需要监听的事件
*/
looping_ = true;
//3.用来接受epoll监听事件返回的数组
//接受epoll监听返回的数组
EventList events(16);
//客户端的一些链接信息
//4.客户端的套接字,和客户端的socket地址的定义
int conn;
struct sockaddr_in clieaddr;
socklen_t clieaddr_len = sizeof(clieaddr);
//5.一个event用来接受客户端的套接字和需要监听的事件
struct epoll_event event;
//这是发生事件的个数
int nready;
//存放ip的地方
char ip[32];
//循环进行监听
while(!quit_){
nready = epoll_wait(epollfd_,&*events.begin(),static_cast<int>(events.size()),-1);
if(nready == -1){
perror("epoll_wait");
exit(-1);
}
if(nready == 0){
continue;
}
if(((size_t)nready == events.size())){
events.resize(events.size()*2);
}
for(int i = 0; i < nready; i++){
//判断监听到的事件
//如果是创建链接的套接字有事件发生
if(events[i].data.fd == listenfd_){
//定义的客户端的套接字用来接收链接服务器的套接字
conn = accept4(listenfd_,(struct sockaddr*)&clieaddr,&clieaddr_len,SOCK_NONBLOCK|SOCK_CLOEXEC);
//打印客户端的ip和端口号
cout<<"ip:"<< inet_ntop(AF_INET,&clieaddr.sin_addr,ip,sizeof(ip))<< " port:"
<<ntohs(clieaddr.sin_port)<<endl;
event.events = EPOLLIN;
event.data.fd = conn;
epoll_ctl(epollfd_,EPOLL_CTL_ADD,conn,&event);
}
//如果是客户端的套接字有事件发生
else if(events[i].events & EPOLLIN){
conn = events[i].data.fd;
//读取套接字里面的内容
char buf[1024] = {0};
int ret = read(conn,buf,sizeof(buf));
cout<<ret<<endl;
if(ret == 0){
epoll_ctl(epollfd_,EPOLL_CTL_DEL,conn,&events[i]);
}
//对内容进行处理之后,将处理后的内容写入到客户端的套接字中
write(conn,buf,ret);
}
}
} //while循环结束
}
//有一个退出循环的函数
void EventLoop::quit()
{
quit_ = true;
}
main.cpp
#include <iostream>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
#include <vector>
#include <sys/epoll.h>
#include <errno.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <EventLoop.h>
using namespace std;
#define SERVPORT 8888
#define SERVIP INADDR_ANY
//使用epoll进行监听
//为了使得代码更加简洁,这里就不进行出错判断
int main()
{
//忽略两个SIGPIPE和SIGCHLD信号
signal(SIGPIPE,SIG_IGN);
signal(SIGCHLD,SIG_IGN);
//首先创建一个套接字
int listenfd;
listenfd = socket(AF_INET,SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC,0);
//设置端口复用
int opt = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
//绑定端口和ip
struct sockaddr_in servaddr;
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htobe16(SERVPORT);
servaddr.sin_addr.s_addr = htobe32(SERVIP);
bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
//设置监听上限
listen(listenfd,SOMAXCONN);
//创建一个epoll
int epollfd;
epollfd = epoll_create1(EPOLL_CLOEXEC);
//将套接字加入到epoll监听中
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = listenfd;
epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&event);
EventLoop eloop;
eloop.setEpollfd(epollfd);
eloop.setListenfd(listenfd);
eloop.loop();
return 0;
}
3. 将listenfd的设置端口复用,绑定,监听又都封装成一个Socket类
Socket.h
#ifndef SOCKET_H
#define SOCKET_H
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
//listenfd的绑定,监听过程在这个类中来实现
class Socket
{
public:
explicit Socket(int sockfd);
~Socket();
//套接字的ip和端口绑定函数
void bindAddress();
//套接字的监听函数
void listenfd();
//设置套接字端口复用函数
void setReuseAddr(bool on);
//返回套接字的fd
int fd();
protected:
private:
const int sockfd_;
};
#endif // SOCKET_H
Socket.cpp
#include "Socket.h"
#define SERVPORT 8888
#define SERVIP INADDR_ANY
Socket::Socket(int sockfd)
:sockfd_(sockfd)
{
//ctor
}
Socket::~Socket()
{
//dtor
}
//套接字的ip和端口绑定函数
void Socket::bindAddress()
{
//绑定端口和ip
struct sockaddr_in servaddr;
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htobe16(SERVPORT);
servaddr.sin_addr.s_addr = htobe32(SERVIP);
bind(sockfd_,(struct sockaddr*)&servaddr,sizeof(servaddr));
}
//套接字的监听函数
void Socket::listenfd()
{
listen(sockfd_,SOMAXCONN);
}
//设置套接字端口复用函数
void Socket::setReuseAddr(bool on)
{
int opt = on? 1:0;
setsockopt(sockfd_,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
}
//返回套接字的fd
int Socket::fd()
{
return sockfd_;
}
main.cpp
#include <iostream>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
#include <vector>
#include <sys/epoll.h>
#include <errno.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <EventLoop.h>
#include <Socket.h>
using namespace std;
//使用epoll进行监听
//为了使得代码更加简洁,这里就不进行出错判断
int main()
{
//忽略两个SIGPIPE和SIGCHLD信号
signal(SIGPIPE,SIG_IGN);
signal(SIGCHLD,SIG_IGN);
//首先创建一个套接字
int listenfd;
listenfd = socket(AF_INET,SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC,0);
Socket socket(listenfd);
socket.setReuseAddr(true);
socket.bindAddress();
socket.listenfd();
listenfd = socket.fd();
//创建一个epoll
int epollfd;
epollfd = epoll_create1(EPOLL_CLOEXEC);
//将套接字加入到epoll监听中
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = listenfd;
epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&event);
EventLoop eloop;
eloop.setEpollfd(epollfd);
eloop.setListenfd(listenfd);
eloop.loop();
return 0;
}