自测之Lesson16:并发通信
知识点:三个多路并发模型(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:并发通信的更多相关文章
- JMeter压测上对于并发的认识误区
1.误区 在JMeter压测过程中,我们通常认为1s内100的并发量(即:QPS为100)的设置如下: 此时,没有再添加额外的控制器.上述中的参数设置解释:Number of Threads(user ...
- 并发通信Manage,队列, 互斥锁
目录 Manage 队列 先入先出 互斥锁 Manage 进程间的通信是被限制的 from multiprocessing import Process a = 1 def func(): glob ...
- python 39 socketserver 模块并发通信
socketserver模块 socketserver模块实现一个服务端与多个客户端通信.是在socket的基础上进行了一层封装,底层还是调用的socket. socketserver干了两件事: 1 ...
- python设置socket的超时时间(可能使用locust压测千级并发的时候要用到,先记录在此)
在使用urllib或者urllib2时,有可能会等半天资源都下载不下来,可以通过设置socket的超时时间,来控制下载内容时的等待时间. 如下python代码 import socket timeou ...
- 将socket通信变成并发的方式
一 利用multiprocessing模块,开启多进程,实现socket通信并发 1. 开启子进程的两种方式 import time import random from multiprocessin ...
- Asp.net 性能监控之压测接口“卡住” 分析
问题描述:web api项目接口压测.前期并发100,500没出现问题,平均耗时也在几百毫秒.当并发1000时候,停留等待许久,看现象是jemeter卡住,没返回,时间过了许久,才正常. 解决过程: ...
- tomcat8.5配置高并发
最近部署的tomcat应用,有一天压测的时候,测试一致反馈下载不了,结果查看日志才发现如下错误: INFO: Maximum number of threads (200) created for c ...
- epoll在socket通信中的应用
当服务器需要服务多个客户时,需要使用并发通信,实现并发通信有以下几种方法: 1.在服务器中fork子进程来为每个客户服务 具体可参考http://www.cnblogs.com/ggjucheng/ ...
- android ipc通信机制之之三,进程通讯方式。
IPC通讯方式的优缺点: IPC通讯方式的对比 名称 优点 缺点 适用场景 Bundle 简单易用 只能传输Bundle支持的数据类型 四大组件的进程通信 文件共享 简单易用 不适合高并发场景,并无法 ...
随机推荐
- chromium之message_pump_win之一
写了22篇博文,终于到这里了———— MessagePumpWin!!! MessagePumpWin这个类还是挺复杂的,可以分成好几部分.接下来分块分析 从介绍看,MessagePumpWin 是M ...
- Linux——查看
查看当前系统版本: lsb_release -a #没有装:yum install lsb 查看当前运行端口: netstat -atunlp #没有装:yum install net-tools - ...
- linux中删除文件内空白行的几种方法。
linux中删除文件内空白行的几种方法 有时你可能需要在 Linux 中删除某个文件中的空行.如果是的,你可以使用下面方法中的其中一个.有很多方法可以做到,但我在这里只是列举一些简单的方法. 你可能已 ...
- python环境Anaconda的安装
本人最开始的的环境为:win10 + 32位的python 最近想学习一下爬虫,听说某些库需要64的python才能实现(本人也是小白,只是平时喜欢折腾,这里是听说,暂时没有能力解释),无奈之下只好卸 ...
- 第一代到第四代多址技术:从FDMA、TDMA、CDMA到OFDMA
做通信物理层有关的内容研究已经有很长一段时间了.一直没有怎么总结,今天借着秋招,来总结一波. 本文所讲的是多址技术,日常常见的有时分多址.频分多址.码分多址,对应TDMA.FDMA.CDMA. 那么什 ...
- JS 原型总结
参考: (从内存角度)简单类型与复杂类型及原型链
- flume搭建新手测试环境
硬件环境: 腾讯云,两台服务器8G 双核 软件环境: flume1.8.jdk1.8,centos6 第一次搭建也是各种找文件,只知道flume是日志抓取服务,也听说了非常稳定强大的服务,正好公司需要 ...
- Hive的DML操作
1. Load 在将数据加载到表中时,Hive 不会进行任何转换.加载操作是将数据文件移动到与 Hive表对应的位置的纯复制/移动操作. 语法结构: load data [local] inpath ...
- 第七篇:gcc和arm-linux-gcc常用选项
目录 一.gcc和arm-linux-gcc的常用选项 二.从.c文件到可执行文件过程 一.gcc和arm-linux-gcc的常用选项 常用选型 -v 查看gcc编译器的版本,显示gcc执行时的详细 ...
- Noip 2011 Day 1 & Day 2
Day 1 >>> T1 >> 水题一道 . 我们只需要 for 一遍 , 由于地毯是从下往上铺的 , 我们只需要记录该位置最上面的地毯的编号 , 每一次在当前地 ...