知识点:三个多路并发模型(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. Linux 学习第四天

    Linux学习第四天 一.常用命令 1.tar  (压缩.解压) A.添加压缩包  tar czvf 压缩包名称.tar.gz 源文件 B.添加压缩包  tar cjvf 压缩包名称.tar.bz2 ...

  2. 关于vue中mockjs的使用

    使用vue的时候,后台可能不能及时作出接口,那么就需要我们前端自己模拟数据,使用mockjs可以进行模拟数据. 首先安装mockjs,cnpm install mockjs --save-dev: 其 ...

  3. 前端String转json

    1.data = eval("("+data+")");2.JSON.parse(data);

  4. Css Sprite(雪碧图、精灵图)<图像拼合技术>

    一.精灵图使用场景: 二.Css Sprite(优点) 减少图片的字节. 减少网页的http请求,从而大大的提高页面的性能. 解决了网页设计师在图片命名上的困扰,只需对一张集合的图片上命名就可以了,不 ...

  5. (八)netty的SSL renegotiation攻击漏洞

    为了满足安全规范,从http改造成https(见(四)启用HTTPS),然而启用https后就可以高枕无忧了吗?绿盟告诉你:当然不,TLS Client-initiated 重协商攻击(CVE-201 ...

  6. git小技巧之分支、关联远程仓库、回滚、解决.gitignore不生效等

    1.分支管理 新建并切换分支:git checkout -b <name>新建本地分支并关联到远程分支git checkout -b myRelease origin/Release合并某 ...

  7. 说一说MySQL的锁机制

    锁概述 MySQL的锁机制,就是数据库为了保证数据的一致性而设计的面对并发场景的一种规则. 最显著的特点是不同的存储引擎支持不同的锁机制,InnoDB支持行锁和表锁,MyISAM支持表锁. 表锁就是把 ...

  8. HTTPS相关知识以及在golang中的应用

    最近简单学习了HTTPS,并在golang中实践了一下,现在把学到的知识记录下来,方便以后查看,如果有幸能帮到有需要的人就更好了,如果有错误欢迎留言指出. 一些简单的概念,可以自行百度百科 HTTPS ...

  9. mysql库地址

    https://dev.mysql.com/downloads/connector/

  10. Flume直接对接SaprkStreaming的两种方式

    一.flume对接sparkStreaming的两种方式: Push推送的方式 Poll拉取的方式 第一种Push方式: 代码如下: package cn.itcast.spark.day5 impo ...