使用select多路复用技术的非阻塞模型

select多路复用通常具有很好的跨平台性,也能提供不错的并发性能,但是在通常情况下有最大监听文件描述符的限制(通常1024),如果不需要达到C10K这种前端高性能服务器的要求,采用select+nonblocking的方式能降低编程的难度


用到的接口
FD_SETSIZE;
FD_SET(<#fd#>, <#fdsetp#>);
FD_ISSET(<#fd#>, <#fdsetp#>);
FD_ZERO(<#fdsetp#>);
select(<#(int)__nfds#>, <#(fd_set*)__readfds#>, <#(fd_set*)__writefds#>, <#(fd_set*)__exceptfds#>, <#(struct timeval*)__timeout#>)


//select-nonblocking
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <errno.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/select.h>#define MAX_READ_SIZE  20*1024*1024
#define MAX_WRITE_SIZE 20*1024*1024
#define MAX_CHUNCK_SIZE 2*1024*1024
#define KEEP_ALIVE 0/*** a simple server using select * feilengcui008@gmail.com**/void run();int main(int argc, char *argv[])
{run();return 0;
}//struct for storing read-write buffer
struct fd_state {char buffer[MAX_READ_SIZE];char write_buffer[MAX_WRITE_SIZE];int writing;size_t read_len;size_t written_len;size_t write_upto_len;
};//alloc fd_state
struct fd_state *alloc_fd_state(void)
{struct fd_state *p = (struct fd_state *) malloc(sizeof(struct fd_state));if (!p) return NULL;p->read_len = p->writing = p->written_len = p->write_upto_len = 0;return p;
};//free fd_state pointer
void free_fd_state(struct fd_state *p)
{free(p);
}//handle error
void error_exit()
{perror("errors happen");exit(EXIT_FAILURE);
}
//set sock non-blocking
void made_nonblock(int fd)
{if(fcntl(fd, F_SETFL, O_NONBLOCK)==-1){error_exit();}
}//handle read event
int do_read(int fd,struct fd_state *state)
{char buf[MAX_CHUNCK_SIZE];int i;ssize_t result;while (1){result = recv(fd, buf, sizeof(buf), 0);/*printf("readlen:%d\n",(int)result);fflush(stdout);*/if (result<=0)break;//read buf to fd_state.buffer until "\n"for (int j = 0; j < result; ++j) {if (state->read_len< sizeof(state->buffer))state->buffer[state->read_len++] = buf[j];/*read until "\n"todo:handle the read buffer and set the write bufferif (buf[j]=='\n'){state->writing = 1;state->write_upto_len = state->read_len;break;}*/}}state->writing = 1;state->write_upto_len = state->read_len;//write(1, state->buffer, state->read_len);//nothing readif (result==0){return 1;}if (result<0){//nonblockingif (errno== EAGAIN)return 0;elsereturn -1;}return 0;
}int do_write(int fd,struct fd_state *state)
{//we borrow the readbuffer just for test//we should have a write buffer to store our http response bodyssize_t result;while (state->written_len<state->write_upto_len){result = send(fd, state->buffer+state->written_len, state->write_upto_len-state->written_len, 0);if (result<=0){break;}state->written_len+=result;}if (state->written_len==state->read_len)state->written_len = state->write_upto_len = state->read_len = 0;state->writing = 0;if (result<0){if (errno== EAGAIN)return 0;elsereturn -1;}if (result==0)return 1;return 0;
}void do_select(int serverfd)
{//set init fd_statestruct fd_state *state[FD_SETSIZE];for (int i = 0; i < FD_SETSIZE; ++i) {state[i] = NULL;}fd_set read_set,write_set,error_set;FD_ZERO(&read_set);FD_ZERO(&write_set);FD_ZERO(&error_set);//main loopwhile(1){int maxfd = serverfd;FD_ZERO(&read_set);FD_ZERO(&write_set);FD_ZERO(&error_set);FD_SET(serverfd, &read_set);//server accept operation just alloc the fd_state struct//do_read and do_write just change the fd_state's state//here we add server sock and client socks to fd_set//we don not pass the readset/writeset to do_read or do_write func//so we add read write event here//when use poll,we can direct change the state in do_read and do_write use the events statusfor (int i = 0; i < FD_SETSIZE; ++i) {if (state[i]){if (i>maxfd){maxfd = i;}FD_SET(i, &read_set);if (state[i]->writing){FD_SET(i, &write_set);}}}//block until event happensif(select(maxfd+1, &read_set, &write_set, &error_set, NULL)<0){error_exit();}// if server becomes readable then accept the client sock// and alloc the client sock stateif(FD_ISSET(serverfd, &read_set)){struct sockaddr_storage ss;socklen_t slen = sizeof(ss);int client = accept(serverfd, (struct sockaddr *)&ss, &slen);if(client<0){error_exit();}else if(client> FD_SETSIZE){close(client);}else{made_nonblock(client);state[client] = alloc_fd_state();}}//handle the fd_setfor (int j = 0; j < maxfd + 1; ++j) {int flag = 0;if (j==serverfd) continue;//handle readif (FD_ISSET(j, &read_set)) flag = do_read(j,state[j]);//handle writeif (flag==0&& FD_ISSET(j, &write_set)){flag = do_write(j, state[j]);//no matter what flag is,we close after write opsif(!KEEP_ALIVE){free_fd_state(state[j]);state[j] = NULL;close(j);}}//handle errorif (flag){free_fd_state(state[j]);state[j] = NULL;close(j);}}}
}int create_server_socket()
{int serverfd = socket(AF_INET, SOCK_STREAM, 0);if(serverfd<0){error_exit();}int reuse = 1;if (setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))<0){error_exit();}return serverfd;
}void run()
{int serverfd = create_server_socket();made_nonblock(serverfd);//server sockaddrstruct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_addr.s_addr = 0;sin.sin_port = htons(8000);if(bind(serverfd, (struct sockaddr *)&sin, sizeof(sin))<0){error_exit();}if(listen(serverfd, 50)<0){error_exit();}do_select(serverfd);
}

Linux网络编程之select相关推荐

  1. Linux网络编程之IP地址转换为无符号整数的方法

    Linux网络编程之IP地址转换为无符号整数的方法,代码如下:(没考虑异常输入) #include <stdio.h> #include <string.h> #include ...

  2. Linux网络编程之sockaddr与sockaddr_in,sockaddr_un结构体详细讲解

    Linux网络编程之sockaddr与sockaddr_in,sockaddr_un结构体详细讲解 (1)sockaddr struct sockaddr { unsigned  short  sa_ ...

  3. Linux网络编程之TCP状态转移

    Linux网络编程之TCP状态转移 一.TCP状态转移时序 二.半关闭及shutdown函数 一.TCP状态转移时序 TCP状态转移图: netstat -apn | grep client 查看客户 ...

  4. linux网络编程之socket编程(六)

    经过一个国庆长假,又有一段时间没有写博文了,今天继续对linux网络编程进行学习,如今的北京又全面进入雾霾天气了,让我突然想到了一句名句:"真爱生活,珍惜生命",好了,言归正传. ...

  5. 嵌入式linux ntpd命令,嵌入式Linux网络编程之:实验内容——NTP协议实现

    本文引用地址:http://www.eepw.com.cn/article/257114.htm 10.4实验内容--NTP协议实现 1.实验目的 通过实现NTP协议的练习,进一步掌握Linux网络编 ...

  6. linux ioctl网络参数设置,Linux 网络编程之ioctl函数

    1.介绍 Linux网络程序与内核交互的方法是通过ioctl来实现的,ioctl与网络协议栈进行交互,可得到网络接口的信息,网卡设备的映射属性和配置网络接口.并且还能够查看,修改,删除ARP高速缓存的 ...

  7. linux网络编程之socket(十一):套接字I/O超时设置方法和用select实现超时

    一.使用alarm 函数设置超时 C++ Code  1 2 3 4 5 6 7 8 9 10 11 12 13   void handler( int sig) { } signal(SIGALRM ...

  8. linux网络编程之用select函数实现io复用(基于TCP)引发的思考

    1.基本概念 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程.IO多路复用适用如下场合: (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/ ...

  9. linux网络编程之用select方法实现io复用(基于udp)

    1.基本概念 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程.IO多路复用适用如下场合: (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/ ...

最新文章

  1. 爱立信:用什么保持全球老大的地位?
  2. C#中实现视频播放器窗体程序(附源码下载)
  3. lintcode :Partition List 链表划分
  4. 第16讲:ODBCJDBC简介
  5. 集成CDI和WebSockets
  6. 使用ESP定律_手工脱壳
  7. linux ls 目录结构,linux 系统目录结构 ls命令 文件类型 alias命令
  8. 行存、列存,堆表、AO表性能对比 - 阿里云HDB for PostgreSQL最佳实践
  9. 零基础如何学习C语言
  10. 管理者必须要精通的六项管理技能
  11. 百度下拉词目前用软件工具可以刷出来吗?
  12. 怎样在html中插入ppt,PPT怎么插入网页中的视频
  13. 机器人码垛手持式编程_工业机器人的码垛编程方法
  14. 粮食在计算机中的应用,计算机监控系统在粮食仓储中的应用
  15. 分享一个空手反套白狼的骚操作
  16. 机器学习中特征选择概述
  17. Excel VBA代码示例
  18. MacBook Pro完整卸载及安装激活VMware Fusion13.0.0教程
  19. 数学建模(五):图与网络模型
  20. 1152:最大数max(x,y,z)

热门文章

  1. 配置IIS服务器提供APP文件下载
  2. SmartCode 使用常见问题
  3. bzoj 4921: [Lydsy六月月赛]互质序列
  4. 利用OracleCommandBuilder实现 datatable与数据库的增删改
  5. Spring Boot 入门例子 Hello world - TerryHe 博客园
  6. ExtJs组件之间的相互访问,访问机制
  7. 提高网站首页载入速度的常用方法
  8. 实锤!沙特新规,出货箱单必须显示条形码,发票必须盖章!
  9. 一看就明白的爬虫入门讲解-基础理论篇(下篇)
  10. 【产品】密码明文显示的必要性和解决方案