博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
自测之Lesson16:并发通信
阅读量:6237 次
发布时间:2019-06-22

本文共 4941 字,大约阅读时间需要 16 分钟。

知识点:三个多路并发模型(select 、poll 、epoll)

 

题目:以epoll模型,编写一个可供多个客户端访问的服务器程序。

 

实现代码:

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SRV_PORT 60000#define BUF_SIZE 1024#define MAX_CONN 10000000 // 最大连接数 #define EVS_LEN 10 // epoll_wait()可保存的“收到数据”的fd的最大数目void startServer(){ int iRet; char szSnd[BUF_SIZE]; char szRcv[BUF_SIZE]; char szBuf[BUF_SIZE]; int fd; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd == -1) { perror("fail socket"); return; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(SRV_PORT); iRet = bind(fd, (struct sockaddr*)&addr, sizeof(addr)); if (iRet == -1) { perror("fail bind"); close(fd); return; } listen(fd, MAX_CONN);/*=========================================== epoll =======================================*/ int epfd = epoll_create(MAX_CONN); // function 1 struct epoll_event ev; ev.events = EPOLLIN; ev.data.fd = STDIN_FILENO; epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev); // function 2 ev.events = EPOLLIN; ev.data.fd = fd; epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); int clientFd; struct sockaddr_in cliAddr; socklen_t addrLen = sizeof(cliAddr); struct epoll_event evs[EVS_LEN]; // 可容纳10个变化的fd int cnt; while(1) { cnt = epoll_wait(epfd, evs, EVS_LEN, -1); // function 3 int i; for (i = 0; i < cnt; i++) { if (evs[i].data.fd == fd) { /* new connect */ clientFd = accept(fd, (struct sockaddr*)&cliAddr, &addrLen); ev.events = EPOLLIN; ev.data.fd = clientFd; epoll_ctl(epfd, EPOLL_CTL_ADD, clientFd, &ev); printf("New connect from %s:%d\n", inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port)); write(clientFd, "Welcome...", 11); } else if (evs[i].data.fd == STDIN_FILENO) { read(STDIN_FILENO, szSnd, BUF_SIZE); printf("command not found\n"); } else { memset (szRcv, 0, BUF_SIZE); iRet = read(evs[i].data.fd, szRcv, BUF_SIZE); if (iRet == 0) { /* 断开连接 */ printf("Disconnect fd:%d\n", evs[i].data.fd); ev.data.fd = evs[i].data.fd; epoll_ctl(epfd, EPOLL_CTL_DEL, evs[i].data.fd, NULL); close(evs[i].data.fd); } else if (iRet < 0) { perror("fail read"); return; } else { memset(szBuf, 0, BUF_SIZE); printf("Recv[%d]:%s\n", evs[i].data.fd, szRcv); memcpy(szBuf, "I have received!", 17); write(evs[i].data.fd, szBuf, strlen(szBuf)); } } } } close(fd); return;}int main(){ startServer(); return 0;}

 

 

题目:以select模型,编写一个可供多个客户端访问的服务器程序。

 

实现代码:

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#define SRV_PORT 60000#define BUF_SIZE 1024#define MAX_CONN 10000 void startServer(){ int iRet; char szSnd[BUF_SIZE]; char szRcv[BUF_SIZE]; int fd; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd == -1) { perror("fail socket"); return; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(SRV_PORT); iRet = bind(fd, (struct sockaddr*)&addr, sizeof(addr)); if (iRet == -1) { perror("fail bind"); close(fd); return; } listen(fd, MAX_CONN);/**************************************************** select *********************************************************/ fd_set fdset; int i; int maxfd = fd; int clientFd; int fdCnt = 0; // 当前连接的客户端数 int fdArr[MAX_CONN]; // 存放文件描述符fd的数组 struct sockaddr_in cliAddr; socklen_t addrLen = sizeof(cliAddr); while(1) { /* select模型每次都要将“fd们”重新加入fdset,开销很大 */ FD_ZERO(&fdset); FD_SET(STDIN_FILENO, &fdset); FD_SET(fd, &fdset); for (i = 0; i < fdCnt; i++) { FD_SET(fdArr[i], &fdset); // 将用于和客户端通信的fd都加入fdset } select(maxfd + 1, &fdset, NULL, NULL, NULL); if (FD_ISSET(fd, &fdset)) { clientFd = accept(fd, (struct sockaddr*)&cliAddr, &addrLen); if (fdCnt == MAX_CONN) { printf("Connect over count\n"); write(clientFd, "please wait...", 15); close(clientFd); } else { printf("Connect from %s:%d success...\n", inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port)); write(clientFd, "Welcome...", 11); fdArr[fdCnt++] = clientFd; if (clientFd > maxfd) { maxfd = clientFd; // 更新maxfd } } } if (FD_ISSET(STDIN_FILENO, &fdset)) { memset(szSnd, 0, BUF_SIZE); read(STDIN_FILENO, szSnd, 1024); printf("command not found\n"); } for (i = 0; i < fdCnt; i++) { if (FD_ISSET(fdArr[i], &fdset)) { memset(szRcv, 0, BUF_SIZE); iRet = read(fdArr[i], szRcv, BUF_SIZE); if (iRet > 0) { printf("Recv[%d]:%s\n", fdArr[i], szRcv); write(fdArr[i], "I received!", 12); } else if (iRet == 0) { close(fdArr[i]); printf("fd:%d disconnect...\n", fdArr[i]); int j; for(j = i; j < fdCnt - 1; j++) { fdArr[j] = fdArr[j+1]; } fdCnt--; i--; } else { perror("read fail"); return; } } } } return;}int main(){ startServer(); return 0;}

  

小结:epoll模型的优点在于:①对于客户端的数量没有限制;②内核主动将“可读”的fd写入到struct epoll_events数组内,所以节省了poll模型和select模型的每次轮询整个fd集合的开销。

 

转载于:https://www.cnblogs.com/xzxl/p/8575745.html

你可能感兴趣的文章
ios证书
查看>>
sql like N'%...%' 在C#里的写法
查看>>
HTML css样式
查看>>
价值观的选择
查看>>
【多线程】的简单理解&进程 and【你的电脑是几核的?】
查看>>
Find Integer
查看>>
开始学习C++
查看>>
让外界可以访问电脑上的网站的几种方式——花生壳,域名,IIS(待)
查看>>
# 2017-2018-1 20155224 《信息安全系统设计基础》第七周学习总结
查看>>
web-view——微信小程序嵌入H5
查看>>
Makefile所有内嵌函数
查看>>
深入理解JVM之JVM内存区域与内存分配
查看>>
Python List
查看>>
《系统分析与设计方法》 练习计算投资回收分析
查看>>
常用的数据批注
查看>>
pwntools使用简介2
查看>>
Java反射
查看>>
ant 使用指南
查看>>
[杨小米私房菜]炒青菜不出水的关键三步--豆豉鲮鱼油麦菜
查看>>
mysql日志文件目录
查看>>