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

972 篇文章 329 订阅
148 篇文章 34 订阅

        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的一个简单开始, 比较基础, 后面我们还会进行更多的介绍, 便于深入理解其思路。

 

 

 

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值