知识点:三个多路并发模型(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. 深入理解bit_or和bit_and,bit_count

    bit_or:二进制数按位或,bit_and:二进制数按位与,bit_count:统计二进制数1个个数 下面以一个例子来说明用法:示例要实现的功能就是计算每月有几天有访问,先把示例摘录在这里.1234 ...

  2. Tomcat问题之 启动 Cannot find /usr/local/tomcat/bin/setclasspath.sh

    在linux启动startup命令报Cannot find /usr/local/tomcat/bin/setclasspath.sh  使用: unset CATALINA_HOME命令得以解决   ...

  3. 06JavaScript变量

    JavaScript 变量 变量是用于存储信息的"容器". var x=5; var y=6; var z=x+y; 就像代数那样 x=5 y=6 z=x+y 在代数中,我们使用字 ...

  4. ie 8在打印网页的时候打印预览是空白的

    win 7专业版系统中的ie 8在打印网页的时候打印预览是空白的,打印出来也是空白的,但是用别的浏览器打印没有问题 根据您的描述,该问题主要是由于保护模式下%Temp%\Low不正常工作引起的. 建议 ...

  5. python的基本知识

    1. python的简介    python的创始⼈人为吉多·范罗苏姆(Guido van Rossum).1989年年的圣诞节期间,吉多· 范罗苏姆为了了在阿姆斯特丹丹打发时间,决⼼心开发⼀个新的脚 ...

  6. python应用:selenium之爬取天眼查信息

    inform_table.py # -*-coding:utf8-*- from selenium import webdriver from selenium.webdriver.common.pr ...

  7. 『Python基础-3』变量、定义变量、变量类型、关键字Python基础-3』变量、定义变量、变量类型、关键字

    『Python基础-3』变量.定义变量.变量类型.关键字 目录: 1.Python变量.变量的命名 2.变量的类型(Python数据类型) 3.Python关键字 1. Python 变量.变量的命名 ...

  8. java的编码格式

    几种常见的编码格式 为什么要编码 不知道大家有没有想过一个问题,那就是为什么要编码?我们能不能不编码?要回答这个问题必须要回到计算机是如何表示我们人类能够理解的符号的,这些符号也就是我们人类使用的语言 ...

  9. Java:xxx is not an enclosing class

    1. 错误原因 该错误一般出现在对内部类进行实例化时,例如 public class A{ public class B{ } } 此时B是A的内部类,如果我们要使用如下语句实例化一个B类的对象: A ...

  10. android 几个工具方法

    集合几个工具方法,方便以后使用. 1.获取手机 分辨率屏幕: public static void printScreenInfor(Context context){ DisplayMetrics ...