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

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

实现代码:

#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <sys/epoll.h>
#include <string.h> #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 <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h> #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集合的开销。

自测之Lesson16:并发通信的更多相关文章

  1. JMeter压测上对于并发的认识误区

    1.误区 在JMeter压测过程中,我们通常认为1s内100的并发量(即:QPS为100)的设置如下: 此时,没有再添加额外的控制器.上述中的参数设置解释:Number of Threads(user ...

  2. 并发通信Manage,队列, 互斥锁

    目录 Manage 队列  先入先出 互斥锁 Manage 进程间的通信是被限制的 from multiprocessing import Process a = 1 def func(): glob ...

  3. python 39 socketserver 模块并发通信

    socketserver模块 socketserver模块实现一个服务端与多个客户端通信.是在socket的基础上进行了一层封装,底层还是调用的socket. socketserver干了两件事: 1 ...

  4. python设置socket的超时时间(可能使用locust压测千级并发的时候要用到,先记录在此)

    在使用urllib或者urllib2时,有可能会等半天资源都下载不下来,可以通过设置socket的超时时间,来控制下载内容时的等待时间. 如下python代码 import socket timeou ...

  5. 将socket通信变成并发的方式

    一 利用multiprocessing模块,开启多进程,实现socket通信并发 1. 开启子进程的两种方式 import time import random from multiprocessin ...

  6. Asp.net 性能监控之压测接口“卡住” 分析

    问题描述:web api项目接口压测.前期并发100,500没出现问题,平均耗时也在几百毫秒.当并发1000时候,停留等待许久,看现象是jemeter卡住,没返回,时间过了许久,才正常. 解决过程: ...

  7. tomcat8.5配置高并发

    最近部署的tomcat应用,有一天压测的时候,测试一致反馈下载不了,结果查看日志才发现如下错误: INFO: Maximum number of threads (200) created for c ...

  8. epoll在socket通信中的应用

    当服务器需要服务多个客户时,需要使用并发通信,实现并发通信有以下几种方法: 1.在服务器中fork子进程来为每个客户服务  具体可参考http://www.cnblogs.com/ggjucheng/ ...

  9. android ipc通信机制之之三,进程通讯方式。

    IPC通讯方式的优缺点: IPC通讯方式的对比 名称 优点 缺点 适用场景 Bundle 简单易用 只能传输Bundle支持的数据类型 四大组件的进程通信 文件共享 简单易用 不适合高并发场景,并无法 ...

随机推荐

  1. 利用MFC Picture Control控件 加载bmp,png

    1.在资源视图,选择PictureControl,并且在属性中把Type设置为Bitmap. 2.加载PNG CStatic* pWnd = (CStatic*)GetDlgItem(IDC_PIC) ...

  2. .net core 基于Claim登录验证

    网站,首先需要安全,实现安全就必须使用登录验证,.net core 基于Claim登录验证就很简单使用. Claim是什么,可以理解为你的身份证的中的名字,性别等等的每一条信息,然后Claim组成一个 ...

  3. mac 无法写入设备的最后一个块 格式化

    硬盘,U盘,装在硬盘盒通过USB连接到电脑.但是无法格式化硬盘 失败的页面显示: 正在卸载磁盘 无法写入设备的最后一个块 操作失败 建议您这样做: 1.切换进Windows系统,或者找一台安装有Win ...

  4. 第四模块MySQL50题作业,以及由作业引申出来的一些高端玩法

    一.表关系 先参照如下表结构创建7张表格,并创建相关约束                 班级表:class       学生表:student       cid caption grade_id ...

  5. 导入/导出excel和PHPExcel基本使用

    * PHPExcel基本使用 * PS:文章如果有误,请及时指出,给予修改 * 项目中导入PHPExcel * 可以去网上下载 github composer 都可以 * 为了方便下载,我将压缩包添加 ...

  6. 使用Scala开发Apache Kafka的TOP 20大好用实践

    本文作者是一位软件工程师,他对20位开发人员和数据科学家使用Apache Kafka的方式进行了最大限度得深入研究,最终将生产实践环节需要注意的问题总结为本文所列的20条建议. Apache Kafk ...

  7. 从Oracle导出数据并导入到Hive

    1.配置源和目标的数据连接 源(oracle): 目标(Hive 2.1.1),需要事先将hive的驱动程序导入HHDI的lib目录中. Hive2.1.1需要的jar包如下:可根据自身情况更换had ...

  8. 分治与递归-找k个临近中位数的数

    问题描述:给定由n个互不相同的数组成的集合S以及正整数k≤n,试设计一个O(n)时间算法找出S中最接近S的中位数的k个数. 算法描述: 用线性时间选择实现的算法找到中位数 S’=除去中位数外的S S& ...

  9. 推荐 的FPGA设计经验(3) 物理实现和时间闭环优化

    Optimizing Physical Implementation and Timing Closure Planning Physical Implementation When planning ...

  10. Codecraft-18 and Codeforces Round #458:D,Bash and a Tough Math Puzzle

    题目传送门 题目大意:Bash喜欢对数列进行操作.第一种操作是询问l~r区间内的gcd值是否几乎为x,几乎为表示能否至多修改一个数达到.第二种操作是将ai修改为x.总共Q个询问,N个数. Soluti ...