程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> epoll邊緣觸發(epoll et) 源代碼例子

epoll邊緣觸發(epoll et) 源代碼例子

編輯:關於C語言

    在利用epoll編寫網絡應用程序,特別是服務器的時候。為了得到最優的效果,一般采用邊緣觸發(epoll ET)的方式。由於邊緣觸發,epoll_wait只有在套接字狀態發生變化的時候才會返回。所以要對套接字(socket)進行循環accept,read,write;直到套接字的緩沖區空(read,accept)或者填滿(write)為止。當read返回的字節數小於要讀的字節數,或者返回EAGAIN的時候,認為緩存區為空了。由於網絡上已經有很多epollet如何處理epollin事件的例子,所以下面是本人只提供測試如何處理epollout事件的代碼。 /*
============================================================================
Name                : epoll_test.c
Author            :
Version         :
Copyright     : Your copyright notice
Description : epoll et example(echo) 此echo服務器對輸入的內容復制了REPEAT_NUM20000次),然後返回給客戶端
                 用於測試epollout事件如何觸發。
============================================================================
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>

#define EPOLL_SIZE 10
#define EVENT_ARR 20
#define BACK_QUEUE 10
#define PORT 18001
#define BUF_SIZE 16
#define REPEAT_NUM 20000
#define OUT_BUF_SIZE 32*REPEAT_NUM

int g_srv_fd;

//由於有可能不能一次write所有的內容,所以需要全局變量保存內容的長度,內容輸出到那裡,
//在監聽到epollout事件後繼續上一次的發送
char g_out_buf[OUT_BUF_SIZE];//保存輸出的內容
int g_out_buf_offset;                //保存輸出到那裡
int g_out_buf_len;                     //保存輸出內容的長度
int g_has_write_buf;        //保存是否要寫輸出內容

void setnonblocking(int sockFd) {
  int opt;

  opt = fcntl(sockFd, F_GETFL);
  if (opt < 0) {
    printf("fcntl(F_GETFL) fail.");
    exit(-1);
  }
  opt |= O_NONBLOCK;
  if (fcntl(sockFd, F_SETFL, opt) < 0) {
    printf("fcntl(F_SETFL) fail.");
    exit(-1);
  }
}

void handle_sig(int signum) {
  close(g_srv_fd);
  fprintf(stderr, "receiv sig int");
  sleep(5);
  exit(0);
}


int write_out_buf(int fd, char *out_buf,int buf_len,int offset)
{
  int snd_len = write(fd, out_buf+offset, buf_len-offset);
  int tmp_len;
  if (snd_len==(buf_len-offset)){
    do{
      tmp_len = write(fd, out_buf+offset+snd_len, buf_len-offset-snd_len);
      if (tmp_len>0 && tmp_len<(buf_len-offset-snd_len)){
        snd_len += tmp_len;
        break;
      }
      if(tmp_len == -1){
        break;
      }
      snd_len += tmp_len;
    }while(tmp_len>0);
  }
  if (((snd_len==-1||tmp_len==-1)    && errno ==EAGAIN) || snd_len<buf_len-offset){
    fprintf(stderr, "snd receiv eagin or snd_len<out_len\r\n");
  }
  fprintf(stderr, "snd ret:%d\r\n", snd_len);
  return snd_len;

}
//
void process_write(int fd, char *in_buf,int buf_len)
{
  char *p_out_buf=g_out_buf;
  int tmp_offset;
  int i;
  for(i=0; i<REPEAT_NUM;i++){
    tmp_offset+=snprintf(p_out_buf+tmp_offset,OUT_BUF_SIZE-tmp_offset,"%d:%s\r\n", i,in_buf);
  }
  g_out_buf_len =tmp_offset;
  g_out_buf_offset = 0;
  g_has_write_buf = 0;
  g_out_buf_offset +=write_out_buf(fd, g_out_buf, g_out_buf_len,g_out_buf_offset);
  fprintf(stderr, "write_out_buf len:%d wret:%d\r\n", g_out_buf_len, g_out_buf_offset);
  if (g_out_buf_offset<g_out_buf_len){
    g_has_write_buf=1;
  }
}


int main() {

  int serverFd;

  serverFd = socket(AF_INET, SOCK_STREAM, 0);
  g_srv_fd = serverFd;

  setnonblocking(serverFd);
  signal(SIGPIPE, SIG_IGN);
  signal(SIGINT, handle_sig);

  int epFd = epoll_create(EPOLL_SIZE);
  struct epoll_event ev, evs[EVENT_ARR];
  ev.data.fd = serverFd;
  ev.events = EPOLLIN | EPOLLET;
  epoll_ctl(epFd, EPOLL_CTL_ADD, serverFd, &ev);

  struct sockaddr_in serverAddr;
  socklen_t serverLen = sizeof(struct sockaddr_in);
  serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  serverAddr.sin_port = htons(PORT);
  if (bind(serverFd, (struct sockaddr *) &serverAddr, serverLen)) {
    printf("bind() fail.\n");
    exit(-1);
  }

  if (listen(serverFd, BACK_QUEUE)) {
    printf("Listen fail.\n");
    exit(-1);
  }

  int clientFd;
  struct sockaddr_in clientAddr;
  socklen_t clientLen;
  char buf[BUF_SIZE];

  int i = 0;
  while (1) {

    int nfds = epoll_wait(epFd, evs, EVENT_ARR, -1);
    for (i = 0; i < nfds; i++) {
      if (evs[i].data.fd == serverFd && (evs[i].events & EPOLLIN)) {
        //epollet需要循環對監聽的套接字accept,直到返回EAGAIN
        do {
          if ((clientFd = accept(serverFd,
              (struct sockaddr *) &clientAddr, &clientLen)) < 0) {
            printf("accept fail.\n");
            break;
          }
          printf("Connect from %s:%d\n", inet_ntoa(clientAddr.sin_addr),
              htons(clientAddr.sin_port));
          setnonblocking(clientFd);
          ev.data.fd = clientFd;
          //注意,為了效率,這裡直接對EPOLLIN,EPOLLOUT事件監聽
          ev.events = EPOLLIN | EPOLLET | EPOLLOUT;
          //ev.events = EPOLLIN;
          epoll_ctl(epFd, EPOLL_CTL_ADD, clientFd, &ev);
        }while(clientFd>0);
      } else if (evs[i].events & EPOLLIN) {
        fprintf(stderr, "epollin event fd:%d\n", clientFd);
        if ((clientFd = evs[i].data.fd) > 0) {
          //epollet需要對套接字循環的讀,直到len < BUF_SIZE,或者len<=0返回
          int len = read(clientFd, buf, BUF_SIZE);
          fprintf(stderr, "read fd:%d len:%d\n", clientFd, len);
          if (len == BUF_SIZE) {
            do {
              /*
              if (write(clientFd, buf, len) < 0) {
                fprintf(stderr, "write fail!\n");
                //break;
              }
              */
              process_write(clientFd, buf, len);
              if (len < BUF_SIZE) {
                fprintf(stderr, "len <bufsize %d<%d\n", len,
                    BUF_SIZE);
                break;
              }
              len = read(clientFd, buf, BUF_SIZE);
              fprintf(stderr, "read2 fd:%d len:%d\n", clientFd, len);
            } while (len > 0);
            if (len == 0) {
              epoll_ctl(epFd, EPOLL_CTL_DEL, clientFd, &ev);
              close(clientFd);
              evs[i].data.fd = -1;
              fprintf(stderr, "close fd:%d\n", clientFd);
            } else if (len == -1 && errno != EAGAIN) {
              fprintf(stderr, " fd:%d\n", clientFd);
              epoll_ctl(epFd, EPOLL_CTL_DEL, clientFd, &ev);
              close(clientFd);
              evs[i].data.fd = -1;
            }
          } else if (len > 0 && len < BUF_SIZE) {
            process_write(clientFd, buf, len);
          } else if (len == 0 || (len == -1 && errno != EAGAIN)) {
            epoll_ctl(epFd, EPOLL_CTL_DEL, clientFd, &ev);
            close(clientFd);
            evs[i].data.fd = -1;
            fprintf(stderr, "close fd:%d\n", clientFd);
          }

        }
      } else if(evs[i].events & EPOLLOUT){
        //監聽到epollout時間,說明發送緩沖去可以寫,那繼續上一次的寫操作
        clientFd = evs[i].data.fd;
        fprintf(stderr, "receive epoll out fd:%d\n", clientFd);
        if(g_has_write_buf){
          g_out_buf_offset +=write_out_buf(clientFd, g_out_buf, g_out_buf_len,g_out_buf_offset);
          fprintf(stderr, "write len :%d writed:%d\n",g_out_buf_len,g_out_buf_offset);
          if(g_out_buf_offset==g_out_buf_len){
            g_has_write_buf = 0;
          }
        }
      }else {
        printf("other event.\n");
      }
    }
  }

  return 0;
}
下面是測試的perl代碼epoll_test.pl) #!/usr/bin/perl

use IO::Socket;
my $host = "127.0.0.1";
my $port = 18001;
my $socket = IO::Socket::INET->new("$host:$port") or die "create socket error $@";
my $msg_out = "1234567890";
print $socket $msg_out;
print "now send over, go to sleep\n";
sleep(10);
my $msg_in;
while (1){
        read $socket, $msg_in, 10240;

        print "receiv msg $msg_in\n";
}
print "5 second gonesend another line\n";
#print $socket $msg_out;

while (1)
{
        sleep(1);
}
運行效果 epollin event fd:5
read fd:5 len:0
close fd:5
Connect from 127.0.0.1:48552
accept fail.
epollin event fd:-1
read fd:5 len:10
snd receiv eagin or snd_len<out_len
snd ret:65536
write_out_buf len:408890 wret:65536
receive epoll out fd:5
snd receiv eagin or snd_len<out_len
snd ret:131072
write len :408890 writed:196608
receive epoll out fd:5
snd receiv eagin or snd_len<out_len
snd ret:65536
write len :408890 writed:262144
receive epoll out fd:5
snd receiv eagin or snd_len<out_len
snd ret:49152
write len :408890 writed:311296
receive epoll out fd:5
snd receiv eagin or snd_len<out_len
snd ret:49152
write len :408890 writed:360448
receive epoll out fd:5
snd ret:48442
write len :408890 writed:408890
epollin event fd:5
read fd:5 len:0
close fd:5

本文出自 “我的it生活-linux|arm” 博客,請務必保留此出處http://herojames.blog.51cto.com/851340/303279

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved