Linux 网络编程八(epoll应用--大并发处理)
http://www.cnblogs.com/zhanggaofeng/p/5901316.html
//头文件 pub.h #ifndef _vsucess#define _vsucess#ifdef __cplusplus extern "C" {#endif //服务器创建socket int server_socket(int port);//设置非阻塞 int setnonblock(int st);//接收客户端socket int server_accept(int st);//关闭socket int close_socket(int st);//接收消息 int socket_recv(int st);//连接服务器 int connect_server(char *ipaddr,int port);//发送消息 int socket_send(int st);//将sockaddr_in转化成IP地址 int sockaddr_toa(const struct sockaddr_in * addr, char * ipaddr);#ifdef __cplusplus } #endif#endif
//辅助方法--pub.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h>//htons()函数头文件 #include <netinet/in.h>//inet_addr()头文件 #include <fcntl.h> #include "pub.h"#define MAXBUF 1024//创建socket int socket_create() {int st = socket(AF_INET, SOCK_STREAM, 0);if (st == -1){printf("create socket failed ! error message :%s\n", strerror(errno));return -1;}return st; }//设置服务端socket地址重用 int socket_reuseaddr(int st) {int on = 1;if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1){printf("setsockopt reuseaddr failed ! error message :%s\n",strerror(errno));//close socket close_socket(st);return -1;}return 0; }//服务器绑定--监听端口号 int socket_bind(int st, int port) {struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));//typeaddr.sin_family = AF_INET;//portaddr.sin_port = htons(port);//ipaddr.sin_addr.s_addr = htonl(INADDR_ANY);//bind ip addressif (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1){printf("bind failed ! error message :%s\n", strerror(errno));//close socket close_socket(st);return -1;}//listenif (listen(st, 20) == -1){printf("listen failed ! error message :%s\n", strerror(errno));//close socket close_socket(st);return -1;}return 0; }//服务器创建socket int server_socket(int port) {if (port < 0){printf("function server_socket param not correct !\n");return -1;}//create socketint st = socket_create();if (st < 0){return -1;}//reuseaddrif (socket_reuseaddr(st) < 0){return -1;}//bind and listenif (socket_bind(st, port) < 0){return -1;}return st; }//连接服务器 int connect_server(char *ipaddr,int port) {if(port<0||ipaddr==NULL){printf("function connect_server param not correct !\n");return -1;}int st=socket_create();if(st<0){return -1;}//conect serverstruct sockaddr_in addr;memset(&addr,0,sizeof(addr));addr.sin_family=AF_INET;addr.sin_port=htons(port);addr.sin_addr.s_addr=inet_addr(ipaddr);if(connect(st,(struct sockaddr *)&addr,sizeof(addr))==-1){printf("connect failed ! error message :%s\n",strerror(errno));return -1;}return st; }//设置非阻塞 int setnonblock(int st) {if (st < 0){printf("function setnonblock param not correct !\n");//close socket close_socket(st);return -1;}int opts = fcntl(st, F_GETFL);if (opts < 0){printf("func fcntl failed ! error message :%s\n", strerror(errno));return -1;}opts = opts | O_NONBLOCK;if (fcntl(st, F_SETFL, opts) < 0){printf("func fcntl failed ! error message :%s\n", strerror(errno));return -1;}return opts; }//接收客户端socket int server_accept(int st) {if (st < 0){printf("function accept_clientsocket param not correct !\n");return -1;}struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));socklen_t len = sizeof(addr);int client_st = accept(st, (struct sockaddr *) &addr, &len);if (client_st < 0){printf("accept client failed ! error message :%s\n", strerror(errno));return -1;} else{char ipaddr[20] = { 0 };sockaddr_toa(&addr, ipaddr);printf("accept by %s\n", ipaddr);}return client_st; }//关闭socket int close_socket(int st) {if (st < 0){printf("function close_socket param not correct !\n");return -1;}close(st);return 0; }//将sockaddr_in转化成IP地址 int sockaddr_toa(const struct sockaddr_in * addr, char * ipaddr) {if (addr == NULL || ipaddr == NULL){return -1;}unsigned char *p = (unsigned char *) &(addr->sin_addr.s_addr);sprintf(ipaddr, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);return 0; }//接收消息 int socket_recv(int st) {if (st < 0){printf("function socket_recv param not correct !\n");return -1;}char buf[MAXBUF] = { 0 };int rc=0;rc=recv(st,buf,sizeof(buf),0);if(rc==0){printf("client is close ! \n");return -1;}else if(rc<0){/** recv错误信息:Connection reset by peer* 错误原因:服务端给客户端发送数据,但是客户端没有接收,直接关闭,那么就会报错* 如果客户端接受了数据,再关闭,也不会报错,rc==0.*/printf("recv failed ! error message :%s \n",strerror(errno));return -1;}printf("%s",buf);//send message/*memset(buf,0,sizeof(buf));strcpy(buf,"i am server , i have recved !\n");if(send(st,buf,strlen(buf),0)<0){printf("send failed ! error message :%s \n",strerror(errno));return -1;}*/return 0; }//发送消息 int socket_send(int st) {char buf[MAXBUF]={0};while(1){//read from keyboardread(STDIN_FILENO,buf,sizeof(buf));if(buf[0]=='0'){break;}if(send(st,buf,strlen(buf),0)<0){printf("send failed ! error message :%s \n",strerror(errno));return -1;}memset(buf,0,sizeof(buf));}return 0; }
//网络编程服务端 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h>//htons()函数头文件 #include <netinet/in.h>//inet_addr()头文件 #include <fcntl.h> #include <sys/epoll.h> #include "pub.h"#define MAXSOCKET 20int main(int arg, char *args[]) {if (arg < 2){printf("please print one param!\n");return -1;}//create server socketint listen_st = server_socket(atoi(args[1]));if (listen_st < 0){return -1;}/** 声明epoll_event结构体变量ev,变量ev用于注册事件,* 数组events用于回传需要处理的事件*/struct epoll_event ev, events[100];//生成用于处理accept的epoll专用文件描述符int epfd = epoll_create(MAXSOCKET);//把socket设置成非阻塞方式 setnonblock(listen_st);//设置需要放到epoll池里的文件描述符ev.data.fd = listen_st;//设置这个文件描述符需要epoll监控的事件/** EPOLLIN代表文件描述符读事件*accept,recv都是读事件*/ev.events = EPOLLIN | EPOLLERR | EPOLLHUP;/** 注册epoll事件* 函数epoll_ctl中&ev参数表示需要epoll监视的listen_st这个socket中的一些事件*/epoll_ctl(epfd, EPOLL_CTL_ADD, listen_st, &ev);while (1){/** 等待epoll池中的socket发生事件,这里一般设置为阻塞的* events这个参数的类型是epoll_event类型的数组* 如果epoll池中的一个或者多个socket发生事件,* epoll_wait就会返回,参数events中存放了发生事件的socket和这个socket所发生的事件* 这里强调一点,epoll池存放的是一个个socket,不是一个个socket事件* 一个socket可能有多个事件,epoll_wait返回的是有消息的socket的数目* 如果epoll_wait返回事件数组后,下面的程序代码却没有处理当前socket发生的事件* 那么epoll_wait将不会再次阻塞,而是直接返回,参数events里面的就是刚才那个socket没有被处理的事件*/int nfds = epoll_wait(epfd, events, MAXSOCKET, -1);if (nfds == -1){printf("epoll_wait failed ! error message :%s \n", strerror(errno));break;}int i = 0;for (; i < nfds; i++){if (events[i].data.fd < 0)continue;if (events[i].data.fd == listen_st){//接收客户端socketint client_st = server_accept(listen_st);/** 监测到一个用户的socket连接到服务器listen_st绑定的端口**/if (client_st < 0){continue;}//设置客户端socket非阻塞 setnonblock(client_st);//将客户端socket加入到epoll池中struct epoll_event client_ev;client_ev.data.fd = client_st;client_ev.events = EPOLLIN | EPOLLERR | EPOLLHUP;epoll_ctl(epfd, EPOLL_CTL_ADD, client_st, &client_ev);/** 注释:当epoll池中listen_st这个服务器socket有消息的时候* 只可能是来自客户端的连接消息* recv,send使用的都是客户端的socket,不会向listen_st发送消息的*/continue;}//客户端有事件到达if (events[i].events & EPOLLIN){//表示服务器这边的client_st接收到消息if (socket_recv(events[i].data.fd) < 0){close_socket(events[i].data.fd);//接收数据出错或者客户端已经关闭events[i].data.fd = -1;/*这里continue是因为客户端socket已经被关闭了,* 但是这个socket可能还有其他的事件,会继续执行其他的事件,* 但是这个socket已经被设置成-1* 所以后面的close_socket()函数都会报错*/continue;}/** 此处不能continue,因为每个socket都可能有多个事件同时发送到服务器端* 这也是下面语句用if而不是if-else的原因,*/}//客户端有事件到达if (events[i].events & EPOLLERR){printf("EPOLLERR\n");//返回出错事件,关闭socket,清理epoll池,当关闭socket并且events[i].data.fd=-1,epoll会自动将该socket从池中清除 close_socket(events[i].data.fd);events[i].data.fd = -1;continue;}//客户端有事件到达if (events[i].events & EPOLLHUP){printf("EPOLLHUP\n");//返回挂起事件,关闭socket,清理epoll池 close_socket(events[i].data.fd);events[i].data.fd = -1;continue;}}}//close epoll close(epfd);//close server socket close_socket(listen_st);return 0; }
//网络编程客户端 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h>//htons()函数头文件 #include <netinet/in.h>//inet_addr()头文件 #include <fcntl.h> #include <sys/epoll.h> #include "pub.h"int main(int arg,char *args[]) {if(arg<2){printf("please print two param !\n");}//端口号int port=atoi(args[2]);//服务端IP地址char ipaddr[30]={0};strcpy(ipaddr,args[1]);//connect serverint st=connect_server(ipaddr,port);//send message//发送消息-- socket_send(st);//close socket close(st);return 0; }
.SUFFIXES:.c .o CC=gcc SRCS1=epoll_client.c\pub.c SRCS2=epoll_server.c\pub.c OBJS1=$(SRCS1:.c=.o) OBJS2=$(SRCS2:.c=.o) EXEC1=mclient EXEC2=mserverstart:$(OBJS1) $(OBJS2)$(CC) -o $(EXEC1) $(OBJS1)$(CC) -o $(EXEC2) $(OBJS2)@echo "-------ok-----------" .c.o:$(CC) -Wall -g -o $@ -c $< clean:rm -f $(OBJS1)rm -f $(EXEC1)rm -f $(OBJS2)rm -f $(EXEC2)
Linux 网络编程八(epoll应用--大并发处理)相关推荐
- 【Linux网络编程】epoll进阶之水平模式和边沿模式
------------->[Linux系统编程/网络编程](学习目录汇总) <-------------- 文章目录 1.epoll的事件模型 1.1 ET(边沿模式)的设置 1.2 基 ...
- Linux网络编程7——epoll反应堆模型
学习视频链接 16-epoll反应堆main逻辑_bilibili_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1iJ411S7UA?p=81& ...
- Linux网络编程---I/O复用模型之epoll
https://blog.csdn.net/men_wen/article/details/53456474 Linux网络编程-I/O复用模型之epoll 1. epoll模型简介 epoll是Li ...
- linux网络编程(三)select、poll和epoll
linux网络编程(三)select.poll和epoll 一.为什么会有多路I/O转接服务器? 二.select 三.poll 三.epoll 一.为什么会有多路I/O转接服务器? 为什么会有多路I ...
- 详情讲述Linux网络编程关注的问题丨epoll原理丨reactor模型丨三次挥手丨四次握手丨多线程丨单线程丨C/C++Linux丨C++后端开发
90分钟搞懂linux网络编程关注的问题 1. 三次挥手,四次握手 2. epoll实现原理剖析 3. reactor模型封装 单线程.多线程以及多进程 视频讲解如下,点击观看: 详情讲述Linux网 ...
- Linux网络编程(六)-高并发服务器03-I/O多路复用03:epoll【红黑树;根节点为监听节点】【无宏FD_SETSIZE限制;不需每次都将要监听的文件描述符从应用层拷贝到内核;不需遍历树】
一.epoll概述 epoll的本质是一个[红黑树].监听结点为根节点. 大量并发,少量活跃效率高. epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并 ...
- 【网络编程】epoll 笔记
一.最大连接数 1.select select在单进程中最多同时监听1024个fd:要想实现百万并发需要一千个进程,并且性能会很差.内存消耗巨大.所以select只适用于连接数在一千个以下的场景. 2 ...
- Linux网络编程——黑马程序员笔记
01P-复习-Linux网络编程 02P-信号量生产者复习 03P-协议 协议: 一组规则. 04P-7层模型和4层模型及代表协议 分层模型结构: OSI七层模型: 物.数.网.传.会.表.应TCP/ ...
- Linux网络编程基础
2019独角兽企业重金招聘Python工程师标准>>> (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍 客户端和服务端 网络程序和普通的程序有一个最大的 ...
最新文章
- Halcon 点云拟合平面并获取单位法向量及位姿
- AI一分钟 | 阿里联合蚂蚁金服95亿美元收购饿了么;西湖大学正式获批成立
- Python 程序设计(第二版)董付国_清华大学出版社_习题答案与分析【针对8.4及其之前的】
- JDK 9/10/11:Java字符串上+ =带来的副作用
- socket 编程入门教程(一)TCP server 端:2、socket与文件描述符
- 看咒语,知情节?他们用《哈利·波特》让AI学习剧透
- 富士通大数据架构解决方案闪耀存储峰会
- XML-RPC 实现C++和C#交互
- 批处理取系统前一天时间并取备分文件日期为前一天的复制到本地
- 循序渐进 OSPF的详细剖析(二)
- Oracle 定时任务详解(dbms_scheduler)
- Microsoft Edge无法打开测试平台的解决方法
- w ndows热键,Window 10 优雅的快捷键
- 狼的处世十大哲理(想养狼的人必应)
- 洗地扫地机一体机好用吗、洗扫一体洗地机选购必看
- DLNA介绍(包括 UPnP)
- CSCW领域的“老”词和“新”词
- 各证件号码(身份证、护照、军官证、驾驶证、港澳台湾通行证、户口簿)正则表达式校验 完整正确
- surface pro 开发java_微软 Surface Pro、Studio、Laptop 全线更新
- 黄荣奎:如何快速、便捷开发小程序
热门文章
- 独立线性度 最佳直线
- 1034. 二哥的金链
- 深入理解softmax函数
- 内核态与用户态【转载】
- VS2005 there is no source code available for the current location 解决方案
- [转载]SQL Plus 一些使用技巧
- poj 2528_2
- CLI下的网页浏览器之二——Lynx
- 世界 Web 2.0 网站评奖揭晓
- java 如何去掉http debug日志_你居然还去服务器上捞日志,搭个日志收集系统难道不香吗?...