您的位置:首页 > 运维架构 > Linux

初玩linux epoll------一个服务器如何与多个客户端进行通信? (笔试面试常考)

2017-06-24 17:41 731 查看
        select和poll被鄙视得厉害, 因为有了linux epoll的存在。 网上讲解select/poll/epoll的例子不胜枚举, 各种比喻, 各种图示。 其实, epoll并没有那么玄乎。 本文中, 我们不进行画图讲解, 也尽量避免过多的文字描述, 只是初步来感受一下epoll.

        服务端代码为:

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <fcntl.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#define BACKLOG 100

int main()
{
int iListenSock = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
inet_aton("0.0.0.0", &addr.sin_addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);

int iOpt = 1;
setsockopt(iListenSock, SOL_SOCKET, SO_REUSEADDR, &iOpt, sizeof(iOpt)); // 标配
bind(iListenSock, (sockaddr*)&addr, sizeof(addr));
listen(iListenSock, BACKLOG);

epoll_event ev;
ev.data.fd = iListenSock;
ev.events = EPOLLIN;

epoll_event events[BACKLOG + 1];

int epollFD = epoll_create(BACKLOG + 1);   // 告诉内核监测的数目, 返回的epollFD为epoll管理句柄
epoll_ctl(epollFD, EPOLL_CTL_ADD, iListenSock, &ev);  // 将ev和对应的iListenSock添加到epoll句柄,用于被epollFD管理
while(1)
{
int timeoutMS = -1; // 永不超时
int nfds = epoll_wait(epollFD, events, BACKLOG + 1, timeoutMS);   // events和nfds是一对输出值
printf("nfds is %d\n", nfds);

for(int i = 0; i < nfds; i++)
{
if(events[i].data.fd == iListenSock)         // 用于监听客户端连接的socket
{
int iConnSock = accept(iListenSock, NULL, NULL);
if (iConnSock < 0)

{
continue;
}

ev.data.fd = iConnSock;
ev.events = EPOLLIN;
epoll_ctl(epollFD, EPOLL_CTL_ADD, iConnSock, &ev);  // 将ev和对应的iConnSock添加到epoll句柄,用于被epollFD管理

printf("new sock came, fd is %d\n", iConnSock);
}
else
{
int iConnSock = events[i].data.fd;      // 用于通信的socket
char szBuf[1024] = {0};
int recvLen = recv(iConnSock, szBuf, sizeof(szBuf) - 1, 0);
if (recvLen > 0)
{
printf("recv data [%s] from fd [%d]\n", szBuf, iConnSock);
}
else if(0 == recvLen)
{
ev.data.fd = iConnSock;
epoll_ctl(epollFD, EPOLL_CTL_DEL, iConnSock, &ev);
close(iConnSock);
printf("connection closed, local fd is [%d]\n", iConnSock);
}
else
{
ev.data.fd = iConnSock;
epoll_ctl(epollFD, EPOLL_CTL_DEL, iConnSock, &ev);
close(iConnSock);
printf("recv error, local fd is [%d]\n", iConnSock);
}
}
}
}

close(epollFD);
close(iListenSock);
return 0;
}
        客户端代码为:

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <fcntl.h>

int main()
{
int sockClient = socket(AF_INET, SOCK_STREAM, 0);

struct sockaddr_in addrSrv;
addrSrv.sin_addr.s_addr = inet_addr("127.0.0.1");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(8888);
connect(sockClient, ( const struct sockaddr *)&addrSrv, sizeof(struct sockaddr_in));

char szSendBuf[100] = "this is me";
while(1)
{
send(sockClient, szSendBuf, strlen(szSendBuf) + 1, 0);
scanf("%s", szSendBuf);
}

close(sockClient);
return 0;
}
      makefile代码为:

all: server client

server: server.o
g++ -o server  server.o

client: client.o
g++ -o client  client.o

server.o: server.cpp
g++ -c server.cpp

client.o:client.cpp
g++ -c client.cpp

clean:
rm -f server client *.o
        编译链接后, 先启动服务端, 然后在同一机器上启动三个不同的客户端, 此时,在服务端界面, 结果如下:

xxxxxx:~/network> ./server
nfds is 1
new sock came, fd is 5
nfds is 1
recv data [this is me] from fd [5]
nfds is 1
new sock came, fd is 6
nfds is 1
recv data [this is me] from fd [6]
nfds is 1
new sock came, fd is 7
nfds is 1
recv data [this is me] from fd [7]
       

       本文只是epoll的一个简单开始, 比较基础, 后面我们还会进行更多的介绍, 便于深入理解其思路。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐