epoll实现的net_echo程序

// net_echo.cpp
// 写一个程序,支持同时打开10w个文件句柄,申请1G共享内存,是一个tcp echo的server,采用select或epoll管理多连接
#include < sys / socket.h >
#include
< sys / resource.h >
#include
< stdio.h >
#include
< sys / epoll.h >
#include
< arpa / inet.h >
#include
< strings.h >
#include
< unistd.h >
#include
< fcntl.h >
#include
< errno.h >
#include
< sys / shm.h >
#include
< string .h >
#include
< time.h >

#define  SHM_MAX 1000000000UL   // 共享内存大小
#define  SHM_KEY 7896    // 共享内存申请时的key
#define  SERV_PORT 4466    // 服务端口号
#define  MAX_RLIMIT 100000   // 最大访问量
#define  LISTENQ  5     // 监听队列长度
#define  MAX_LINE 128    // 缓存长度
const   char   * local_addr = " 127.0.0.1 " ; // 绑定服务地址

struct  access_info{   // 记录客户访问信息
 time_t a_time;   // 客户访问时间
 in_addr_t a_ip;   // 客户ip
  int  a_errno;   // 是否访问成功,成功为0,否则为其错误号
};

bool  setnonblocking( int  fd);  // 设置fd为非阻塞模式
bool  set_fd_limit(unsigned  int  max);  // 设置系统允许的进程所能打开的文件描述符的最大值

int  main( int  argc, char   ** argv)
{
    
int  listenfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); // 建立serv socket
     if (listenfd < 0 ){
        perror(
" create socket failed! " );
        
return   - 1 ;
    }

    
struct  sockaddr_in servaddr,clientaddr;
    bzero(
& servaddr, sizeof (servaddr));
    servaddr.sin_family
= AF_INET;
    servaddr.sin_port
= htons(SERV_PORT);
    inet_aton(local_addr,
& (servaddr.sin_addr));
    
if (bind(listenfd,(sockaddr  * ) & servaddr, sizeof (servaddr)) < 0 )   // 绑定本机地址
    {
        perror(
" bind error! " );
        
return   - 1 ;
    }

 
 
if ( ! set_fd_limit(MAX_RLIMIT)){
  perror(
" setrlimit failed! " );
  
return   - 1 ;
 }
       
 
struct  epoll_event ev,events[ 20 ];
 
int  epfd = epoll_create(MAX_RLIMIT);   // epoll_create
  if ( ! setnonblocking(listenfd)) return   - 1 ;
 ev.data.fd
= listenfd;
 ev.events
= EPOLLIN | EPOLLET;
 epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,
& ev); // 把listenfd加入到epoll监听队列
 
 
int  shm_id = shmget(SHM_KEY,SHM_MAX,IPC_CREAT | 0600 );  // 申请共享内存
  if (shm_id ==- 1 ){
  perror(
" shmget " );
  
return   - 2 ;
 }
 
struct  access_info  client_info, * pos;   // 客户信息
  int   * head;
 head
= ( int   * )shmat(shm_id, 0 , 0 );
 
if ( int (head) ==- 1 ){
  perror(
" shmat " );
  
return   - 1 ;
 }
 
* head = 1 ;       // 服务器在运行状态,若该值变为0,则关闭服务器
  if ( * (head + 1 ) != 1 ){     //  head+1服务器是否第一次运行,head+2共享内存存储的信息数量 
   * (head + 1 ) = 1 ;              //   ___________  
   * (head + 2 ) = 0 ;    // head-->|___ 0/1___|  服务器的运行状态 
 }                             //    |___ 0/1___|  共享内存是否使用过,是为1,否则为0
                               
//    |___  n____|   共享内存存储信息数量  0~SHM_MAX/(3*4*8)-1
 pos = ( struct  access_info  * )(head) + 1 +* (head + 2 );  // 记录信息的开始位置
 
 listen(listenfd,LISTENQ);  
// 监听客户端请求
  int  nfds,i,connfd,sockfd,n;
 socklen_t len;
 
char  line[MAX_LINE];
 
while ( * head)
 {
  nfds
= epoll_wait(epfd,events, 20 , 500 );  // 检测活跃连接
   for (i = 0 ;i < nfds;i ++ )
  {
   
if (events[i].data.fd == listenfd)     // 有新连接到来
   {
    len
= sizeof (clientaddr);
    connfd
= accept(listenfd,(sockaddr  * ) & clientaddr, & len);
    
    client_info.a_time
= time(NULL);      // 注册客户信息
    client_info.a_ip = clientaddr.sin_addr.s_addr;
    client_info.a_errno
= 0 ;
    
    
if (connfd < 0 ){
     perror(
" connfd<0! " );
     client_info.a_errno
= errno;
     
continue ;
    }
    
    memcpy(pos,
& client_info, sizeof (client_info));
    pos
++ ;          // 共享内存指针后移,并把信息数量加1
     if (( * (head + 2 )) ++> 4 * SHM_MAX / ( 3 * 8 * 4 * 5 ))   // 共享内存剩余不足1/5时发出警告信息
     fprintf(stderr, " Warning:share memory is being not enough/n Left:%d/n " ,SHM_MAX -* (head + 2 ) * 3 * 4 * 8 );
     
    
    
if ( ! setnonblocking(connfd)) continue ;
    ev.data.fd
= connfd;
    ev.events
= EPOLLIN | EPOLLET;
    epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,
& ev); // 新连接加入epoll_wait
   }
   
else   if (events[i].events & EPOLLIN)    // 连接可读
   {
    
if ((sockfd = events[i].data.fd) < 0 ) continue ;
    
while ((n = read(sockfd,line,MAX_LINE)) == MAX_LINE)
     write(sockfd,line,n);
    
if (n < 0 )
    {
     
if  (errno  ==  ECONNRESET) {
      events[i].data.fd 
=   - 1 ;
      epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL);
      close(sockfd);
      
continue ;
     }
     
else   continue ;   // 恰好读完MAX_LINE后无数据
    }
    
else   if (n == 0 ){       // 客户端关闭连接
     epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL);
     close(sockfd);
     events[i].data.fd 
=   - 1 ;
     
continue ;
    }
    
else  write(sockfd,line,n);
   }
/*    else if(events[i].events&EPOLLOUT)
   {
    sockfd=events[i].data.fd;
    if(sockfd<0)continue;
    write(sockfd,line,n);
    ev.data.fd=sockfd;
    ev.events=EPOLLIN|EPOLLET;
    epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
   }
 
*/  }
    }
 shmdt(head);  
// 卸载共享内存
 close(epfd);
 close(listenfd);
 
return   0 ;
}

bool  setnonblocking( int  sock)
{
    
int  opts;
    opts
= fcntl(sock,F_GETFL);
    
if (opts < 0 )
 {
  perror(
" fcntl(sock,GETFL) " );
  
return   false ;
    }
 opts 
=  opts | O_NONBLOCK;
 
if (fcntl(sock,F_SETFL,opts) < 0 )
 {
  perror(
" fcntl(sock,SETFL,opts) " );
  
return   false ;
    }
 
return   true ;
}

bool  set_fd_limit(unsigned  int  max)
{
    
struct  rlimit rlim,rlim_new;
    
if  (getrlimit(RLIMIT_NOFILE,  & rlim) != 0 )
        
return   false ;
    
if (rlim.rlim_cur >= max)  return   true ;

    
if (rlim.rlim_max == RLIM_INFINITY || rlim.rlim_max >= max){
        rlim_new.rlim_max 
=  rlim.rlim_max;
        rlim_new.rlim_cur 
=  max;
    }
    
else {
        
if (geteuid() != 0 ){errno = 1 ; return   false ; }
  rlim_new.rlim_max
= rlim_new.rlim_cur = max;
    }
 
if  (setrlimit(RLIMIT_NOFILE,  & rlim_new) != 0 ) { /*  failed. try raising just to the old max  */
  
int  err = errno;
  setrlimit(RLIMIT_NOFILE, 
& rlim);
  errno
= err;
  
return   false ;
    }
 
return   true ;
}

/*----------------------------------------------------------------*/         

                 

 

// net_echo_shutdown.cpp
// 启动该进程时,关闭net_echo服务进程

#include
< sys / shm.h >
#include
< stdio.h >
#include
< unistd.h >
#include
< errno.h >
#define  SHM_MAX 1000000000UL   // 共享内存大小
#define  SHM_KEY 7896    // 共享内存申请时的key

#ifndef IPC_ALLOC
#define  IPC_ALLOC IPC_CREAT
#endif


int  main( int  argc, char   ** argv)
{
 
if (geteuid() != 0 ){
  errno
= 1 ;
  perror(
" net_echo_shutdown: " );
  
return   - 1 ;
 }
 
int  shmid;
 
if ((shmid = shmget(SHM_KEY,SHM_MAX,IPC_ALLOC | 0600 )) ==- 1 )
 {
  perror(
" shmget() " );
  
return   - 1 ;
 }
 
int   * head = ( int   * )shmat(shmid, 0 , 0 );
 
if ( int (head) ==- 1 ){
  perror(
" shmat() " );
  
return   - 1 ;
 }
 
if ( * head != 1 ){      // 服务器并未运行
  fprintf(stderr, " Net_echo server is not running/n " );
  
return   - 1 ;
 }
 
* head = 0 ;    // 设置关闭标志
 printf( " Shutdown the echo server/n " );
 sleep(
2 );
 shmdt(head);
 
return   0 ;
}

 /******************************************************************/

// print_shm.cpp
// 读取并打印共享内存信息
#include < stdio.h >
#include
< sys / shm.h >
#include
< unistd.h >
#include
< errno.h >
#include
< time.h >
#include
< sys / socket.h >
#include
< arpa / inet.h >
#include
< string .h >
#define  SHM_KEY 7896
#define  SHM_MAX 1000000000UL
#ifndef IPC_ALLOC
#define  IPC_ALLOC IPC_CREAT
#endif

struct  access_info{   // 记录客户访问信息
 time_t a_time;   // 客户访问时间
 in_addr_t a_ip;   // 客户ip
  int  a_errno;   // 是否访问成功,成功为0,否则为其错误号
};

int  main( int  argc, char   ** argv)
{
 
if (geteuid() != 0 ){
  errno
= 1 ;
  perror(
" print_shm: " );
  
return   - 1 ;
 }
 
int  shmid = shmget(SHM_KEY,SHM_MAX,IPC_ALLOC | 0600 );
 
if (shmid ==- 1 )
 {
  perror(
" shmget() " );
  
return   - 1 ;
 }
 
int   * head = ( int   * )shmat(shmid, 0 , 0 );
 
if ( int (head) ==- 1 ){
  perror(
" shmat() " );
  
return   - 1 ;
 }
 
if ( * (head + 1 ) != 1 ){
  fprintf(stderr,
" SHM have not be used!/n " );
  
return   - 1 ;
 }
 
struct  access_info  * pos = (access_info  * )(head) + 1 ;
 
for ( int  i = 0 ;i <* (head + 2 );i ++ ,pos ++ )
  printf(
" %-15s%-10s%20s%s " ,inet_ntoa( * (in_addr  * ) & (pos -> a_ip)),pos -> a_errno == 0 ? " Success! " : " Failed: " ,
   pos
-> a_errno == 0 ? "" :strerror(pos -> a_errno),ctime( & pos -> a_time));
 
 shmdt(head);
 
return   0 ;
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新时代的系统架构设计师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值