知识点:三个多路并发模型(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. centOS上的基础文件操作

    文件及文件夹的基础操作: 1: 新建     (1)在当前目录新建一个文件夹   dir foldername     (2)  新建文件:     当前目录  vi newFile.text     ...

  2. php的基础知识(四)

    14.数组: 索引数组: 下标就是数字开始的. $arr = ['a','b','c',1,2,3]; 关联数组: $arr = [ 'a' => 'b', 'c' => 'd'; 'e' ...

  3. 大数据学习--day07(冒泡排序、Arrays工具类、方法可变参数)

    冒泡排序.Arrays工具类.方法可变参数 冒泡排序之前写过,略 Arrays工具类 二分法查询 public static int binarySearch(int[] a,int key) 头信息 ...

  4. 3.1 wifi网卡RT3070在S3C2440的移植和使用

    学习目标:熟悉RT3070在S3C2440的移植和使用,以及其中的相关工具的安装和使用: 一.配置内核选择WIFI驱动 1. 将usb wifi插到电脑,在ubuntu使用命令:# lsusb 查看w ...

  5. 批量改变图片的尺寸大小 python opencv

    我目标文件夹下有一大批图片,我要把它转变为指定尺寸大小的图片,用pthon和opencv实现的. 以上为原图片. import cv2 import os # 按指定图像大小调整尺寸 def resi ...

  6. 变分自编码器(Variational auto-encoder,VAE)

    参考: https://www.cnblogs.com/huangshiyu13/p/6209016.html https://zhuanlan.zhihu.com/p/25401928 https: ...

  7. 常用代码c#

    当使用 HttpContext.Current用到不是当前线程会出null的情况,可使用 System.Web.HttpRuntime.AppDomainAppPath获取程序的根路 string p ...

  8. 线上CPU飚高(死循环,死锁...)

    之前排除服务器内存暴增的问题,在此看到一篇类似的文章,做个类似的记录. 1.top基本使用 top 命令运行图: 第一行:基本信息 第二行:任务信息 第三行:CPU使用情况 第四行:物理内存使用情况 ...

  9. MVC PartialView 方式实现点击加载更多

    <table id="MovieListing"> </table><div> <button id="btnShowMore& ...

  10. DevExpress 操作gridcontrol

    XtraGrid的关键类就是:GridControl和GridView.GridControl本身不显示数据,数据都是显示在GridView/CardView/XXXXView中.GridContro ...