linux 網路程式設計:epoll 的例項
在前面已經經過了PPC、TPC、select之類( TPC就是使用程序處理data,TPC就是使用執行緒處理 ),前面兩個的缺點大家應該都是知道的是吧,對於select( 其實poll和他差不多 ),缺點是能同時連線的fd是在是不多,在linux中一般是1024/2048,對於很大的伺服器來說是不夠的!當然我們可以自己修改其值!但是效率上就會下降!
對於改進poll的epoll來說:支援一個程序開啟大數目的socket描述符,也就是說與本機的記憶體是有關係的!( 一般伺服器的都是很大的! )
下面是我的小PC機上的顯示:
391658
達到了391658個,那麼對於伺服器而言,顯然,嘿嘿嘿~~~
epoll的基礎知識吧大家在網上到處都能找到,不就是epoll_creat 、epoll_ctl、epoll_wait 3函式!大家自己搜去,我也是在學習。。。
此處主要是貼上自己的測試的一些垃圾程式碼,與大家共勉!呵呵呵~
哦,忘了要注意一下:
epoll_ctl epoll的事件註冊函式,其events引數可以是以下巨集的集合:
EPOLLIN: 表示對應的檔案描述符可以讀(包括對端SOCKET正常關閉);
EPOLLOUT: 表示對應的檔案描述符可以寫;
EPOLLPRI: 表示對應的檔案描述符有緊急的資料可讀(這裡應該表示有帶外資料到來);
EPOLLERR: 表示對應的檔案描述符發生錯誤;寫已關閉socket pipe broken
EPOLLHUP: 表示對應的檔案描述符被結束通話;譬如收到RST包。在註冊事件的時候這個事件是預設新增。
EPOLLRDHUP: 表示對應的檔案描述符對端socket關閉事件,主要應用於ET模式下。
在水平觸發模式下,如果對端socket關閉,則會一直觸發epollin事件,驅動去處理client socket。
在邊沿觸發模式下,如果client首先發送協議然後shutdown寫端。則會觸發epollin事件。但是如果處理程式只進行一次recv操作時,根據recv收取到得資料長度來判讀後邊是
否還有需要處理的協議時,將丟失客戶端關閉事件。
EPOLLET: 將EPOLL設為邊緣觸發(Edge Triggered)模式,這是相對於水平觸發(Level Triggered)來說的。
EPOLLONESHOT:只監聽一次事件,當監聽完這次事件之後,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到EPOLL佇列裡
server端:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> /* socket類定義需要*/ #include <sys/epoll.h> /* epoll標頭檔案 */ #include <fcntl.h> /* nonblocking需要 */ #include <sys/resource.h> /* 設定最大的連線數需要setrlimit */ #define MAXEPOLL 10000 /* 對於伺服器來說,這個值可以很大的! */ #define MAXLINE 1024 #define PORT 6000 #define MAXBACK 1000 //!> 設定非阻塞 //!> int setnonblocking( int fd ) { if( fcntl( fd, F_SETFL, fcntl( fd, F_GETFD, 0 )|O_NONBLOCK ) == -1 ) { printf("Set blocking error : %d\n", errno); return -1; } return 0; } int main( int argc, char ** argv ) { int listen_fd; int conn_fd; int epoll_fd; int nread; int cur_fds; //!> 當前已經存在的數量 int wait_fds; //!> epoll_wait 的返回值 int i; struct sockaddr_in servaddr; struct sockaddr_in cliaddr; struct epoll_event ev; struct epoll_event evs[MAXEPOLL]; struct rlimit rlt; //!> 設定連線數所需 char buf[MAXLINE]; socklen_t len = sizeof( struct sockaddr_in ); //!> 設定每個程序允許開啟的最大檔案數 //!> 每個主機是不一樣的哦,一般伺服器應該很大吧! //!> rlt.rlim_max = rlt.rlim_cur = MAXEPOLL; if( setrlimit( RLIMIT_NOFILE, &rlt ) == -1 ) { printf("Setrlimit Error : %d\n", errno); exit( EXIT_FAILURE ); } //!> server 套介面 //!> bzero( &servaddr, sizeof( servaddr ) ); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl( INADDR_ANY ); servaddr.sin_port = htons( PORT ); //!> 建立套接字 if( ( listen_fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { printf("Socket Error...\n" , errno ); exit( EXIT_FAILURE ); } //!> 設定非阻塞模式 //!> if( setnonblocking( listen_fd ) == -1 ) { printf("Setnonblocking Error : %d\n", errno); exit( EXIT_FAILURE ); } //!> 繫結 //!> if( bind( listen_fd, ( struct sockaddr *)&servaddr, sizeof( struct sockaddr ) ) == -1 ) { printf("Bind Error : %d\n", errno); exit( EXIT_FAILURE ); } //!> 監聽 //!> if( listen( listen_fd, MAXBACK ) == -1 ) { printf("Listen Error : %d\n", errno); exit( EXIT_FAILURE ); } //!> 建立epoll //!> epoll_fd = epoll_create( MAXEPOLL ); //!> create ev.events = EPOLLIN | EPOLLET; //!> accept Read! ev.data.fd = listen_fd; //!> 將listen_fd 加入 if( epoll_ctl( epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev ) < 0 ) { printf("Epoll Error : %d\n", errno); exit( EXIT_FAILURE ); } cur_fds = 1; while( 1 ) { if( ( wait_fds = epoll_wait( epoll_fd, evs, cur_fds, -1 ) ) == -1 ) { printf( "Epoll Wait Error : %d\n", errno ); exit( EXIT_FAILURE ); } for( i = 0; i < wait_fds; i++ ) { if( evs[i].data.fd == listen_fd && cur_fds < MAXEPOLL ) //!> if是監聽埠有事 { if( ( conn_fd = accept( listen_fd, (struct sockaddr *)&cliaddr, &len ) ) == -1 ) { printf("Accept Error : %d\n", errno); exit( EXIT_FAILURE ); } printf( "Server get from client !\n"/*, inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port */); ev.events = EPOLLIN | EPOLLET; //!> accept Read! ev.data.fd = conn_fd; //!> 將conn_fd 加入 if( epoll_ctl( epoll_fd, EPOLL_CTL_ADD, conn_fd, &ev ) < 0 ) { printf("Epoll Error : %d\n", errno); exit( EXIT_FAILURE ); } ++cur_fds; continue; } //!> 下面處理資料 //!> nread = read( evs[i].data.fd, buf, sizeof( buf ) ); if( nread <= 0 ) //!> 結束後者出錯 { close( evs[i].data.fd ); epoll_ctl( epoll_fd, EPOLL_CTL_DEL, evs[i].data.fd, &ev ); //!> 刪除計入的fd --cur_fds; //!> 減少一個唄! continue; } write( evs[i].data.fd, buf, nread ); //!> 回寫 } } close( listen_fd ); return 0; }
對於client:
由於本人比較懶,所以就使用上一次的select的client吧,一樣的,呵呵:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/select.h>
#define MAXLINE 1024
#define SERV_PORT 6000
//!> 注意輸入是由stdin,接受是由server傳送過來
//!> 所以在client端也是需要select進行處理的
void send_and_recv( int connfd )
{
FILE * fp = stdin;
int lens;
char send[MAXLINE];
char recv[MAXLINE];
fd_set rset;
FD_ZERO( &rset );
int maxfd = ( fileno( fp ) > connfd ? fileno( fp ) : connfd + 1 );
//!> 輸入和輸出的最大值
int n;
while( 1 )
{
FD_SET( fileno( fp ), &rset );
FD_SET( connfd, &rset ); //!> 注意不要把rset看作是簡單的一個變數
//!> 注意它其實是可以包含一組套接字的哦,
//!> 相當於是封裝的陣列!每次都要是新的哦!
if( select( maxfd, &rset, NULL, NULL, NULL ) == -1 )
{
printf("Client Select Error..\n");
exit(EXIT_FAILURE );
}
//!> if 連線口有資訊
if( FD_ISSET( connfd, &rset ) ) //!> if 連線埠有資訊
{
printf( "client get from server ...\n" );
memset( recv, 0, sizeof( recv ) );
n = read( connfd, recv, MAXLINE );
if( n == 0 )
{
printf("Recv ok...\n");
break;
}
else if( n == -1 )
{
printf("Recv error...\n");
break;
}
else
{
lens = strlen( recv );
recv[lens] = '\0';
//!> 寫到stdout
write( STDOUT_FILENO, recv, MAXLINE );
printf("\n");
}
}
//!> if 有stdin輸入
if( FD_ISSET( fileno( fp ), &rset ) ) //!> if 有輸入
{
//!> printf("client stdin ...\n");
memset( send, 0, sizeof( send ) );
if( fgets( send, MAXLINE, fp ) == NULL )
{
printf("End...\n");
exit( EXIT_FAILURE );
}
else
{
//!>if( str )
lens = strlen( send );
send[lens-1] = '\0'; //!> 減一的原因是不要回車字元
//!> 經驗值:這一步非常重要的哦!!!!!!!!
if( strcmp( send, "q" ) == 0 )
{
printf( "Bye..\n" );
return;
}
printf("Client send : %s\n", send);
write( connfd, send, strlen( send ) );
}
}
}
}
int main( int argc, char ** argv )
{
//!> char * SERV_IP = "10.30.97.188";
char buf[MAXLINE];
int connfd;
struct sockaddr_in servaddr;
if( argc != 2 )
{
printf("Input server ip !\n");
exit( EXIT_FAILURE );
}
//!> 建立套接字
if( ( connfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )
{
printf("Socket Error...\n" , errno );
exit( EXIT_FAILURE );
}
//!> 套接字資訊
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
//!> 連結server
if( connect( connfd, ( struct sockaddr * )&servaddr, sizeof( servaddr ) ) < 0 )
{
printf("Connect error..\n");
exit(EXIT_FAILURE);
}
/*else
{
printf("Connet ok..\n");
}*/
//!>
//!> send and recv
send_and_recv( connfd );
//!>
close( connfd );
printf("Exit\n");
return 0;
}
編譯執行:
gcc -o server server.c
gcc -o client client.c
./server
./client
END
相關推薦
linux 網路程式設計:epoll 的例項
在前面已經經過了PPC、TPC、select之類( TPC就是使用程序處理data,TPC就是使用執行緒處理 ),前面兩個的缺點大家應該都是知道的是吧,對於select( 其實poll和他差不多 ),缺點是能同時連線的fd是在是不多,在linux中一般是102
Linux網路程式設計:TCP客戶/伺服器模型及基本socket函式
TCP客戶/伺服器模型 TCP連線的分組交換 在使用socket API的時候應該清楚應用程式和TCP協議棧是如何互動的: 呼叫connect()會發出SYN段(SYN是TCP報文段頭部的一個標誌位,置為1) 阻塞的read()函式返回0就表明收到了FIN段 客戶端呼叫c
Linux網路程式設計:socket程式設計簡介、網路位元組序及相關函式
Socket(套接字) socket可以看成是使用者程序與核心網路協議棧的程式設計介面(API函式)。 socket不僅可以用於本機的程序間通訊,還可以用於網路上不同主機的程序間通訊。 IPv4套接字地址結構 IPv4套接字地址結構通常也稱為“網際套接字地址結構”,它以
網路程式設計:epoll、accept觸發模式及阻塞方式的選擇
select(),poll()模型都是水平觸發模式,訊號驅動IO是邊緣觸發模式,epoll()模型即支援水平觸發,也支援邊緣觸發,預設是水平觸發 從表象看epoll的效能最好,但是在連線數少,並且連線都十分活躍的情況下,select和poll的效能可能比ep
Linux網路程式設計--使用epoll模型同時處理tcp和udp服務
在實際工作中,伺服器需要同時監聽和處理tcp和udp的套接字,同時監聽N多的埠。根據bind系統呼叫來講,一個socket只能監聽一個埠,因此要建立多個socket並繫結到各個埠上。當然同一個埠可以同時繫結tcp和udp的socket,但是要建立兩個socket
Linux網路程式設計:原始套接字的魔力【上】
原文:http://blog.chinaunix.net/uid-23069658-id-3280895.html 基於原始套接字程式設計 在開發面向連線的TCP和麵向無連線的UDP程式時,我們所關心的核心問題在於資料收發層面,資料的傳輸特性由TCP或UDP來
Linux網路程式設計 -- select/epoll得知socket有資料可讀,如何判斷資料全部被讀取完畢?
http://blog.csdn.net/ldd909/article/details/6168077 補充一點
Linux網路程式設計:使用select函式實現socket 收發資料
所謂的回射是指:客戶端A向服務端B傳送資料,服務端B接收到資料之後,再將接收到的資料傳送回客戶端B。所謂的迭代伺服器,
Linux網路程式設計——原始套接字例項:MAC 頭部報文分析
通過《Linux網路程式設計——原始套接字程式設計》得知,我們可以通過原始套接字以及 recvfrom( ) 可以獲取鏈路層的資料包,那我們接收的鏈路層資料包到底長什麼樣的呢? MAC 頭部(有線區域網) 注意:CRC、PAD 在組包時可以忽略 鏈路層資料包的其中一
Linux 網路程式設計——原始套接字例項:MAC 地址掃描器
如果 A (192.168.1.1 )向 B (192.168.1.2 )傳送一個數據包,那麼需要的條件有 ip、port、使用的協議(TCP/UDP)之外還需要 MAC 地址,因為在乙太網資料包中 MAC 地址是必須要有的。那麼怎樣才能知道對方的 MAC 地址?答案是:它通
Linux 網路程式設計——原始套接字例項:傳送 UDP 資料包
乙太網(Ethernet)報文格式(MAC頭部報文格式): IP 報文格式: UDP 報文格式: 校驗和函式: /******************************************************* 功能:
Linux網路程式設計--epoll 模型原理詳解以及例項
1.簡介 Linux I/O多路複用技術在比較多的TCP網路伺服器中有使用,即比較多的用到select函式。Linux 2.6核心中有提高網路I/O效能的新方法,即epoll 。 epoll是什麼?按照man手冊的說法是為處理大批量控制代碼而作了改進
Linux學習之網路程式設計(epoll的用法)
言之者無罪,聞之者足以戒。 - “詩序” epoll相關的函式包含在標頭檔案<sys/epoll.h> epoll是Linux核心為處理大批量控制代碼而作了改進的poll,是Linux下多路複用IO介面select/poll的增強版本,它能顯著減少程式在大量併發連線中只有少量活躍
嵌入式Linux網路程式設計,I/O多路複用,epoll()示例,epoll()客戶端,epoll()伺服器,單鏈表
文章目錄 1,I/O多路複用 epoll()示例 1.1,epoll()---net.h 1.2,epoll()---client.c 1.3,epoll()---sever.c 1.4,epoll()---linklist.h
Linux----網路程式設計(IO複用之epoll系統呼叫函式)
伺服器端epoll.c #include <stdio.h> #include <string.h> #include <stdlib.h> #include <assert.h> #include <unistd.h&
《Linux網路程式設計》: 無連線和麵向連線協議的區別
網路程式設計中最基本的概念就是面向連線(connection-oriented)和無連線(connectionless)協議。儘管本質上來說,兩者之間的區別並不難理解,但對那些剛剛開始進行網路程式設計的人來說,卻是個很容易混淆的問題。這個問題與上下文有些關聯:很顯然,如果兩臺計算機要進行通訊,就必須
《Linux網路程式設計》: 網路協議入門
我們每天使用網際網路,你是否想過,它是如何實現的? 全世界幾十億臺電腦,連線在一起,兩兩通訊。北京的某一塊網絡卡送出訊號,深圳的另一塊網絡卡居然就收到了,兩者實際上根本不知道對方的物理位置,你不覺得這是很神奇的事情嗎? 為了使各種不同的計算機之間可以互聯,ARPANet指定了一套計算
Linux網路程式設計(一):一個簡單的socket程式
伺服器: /* *tcp_server.c */ #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include
Linux-C網路程式設計之epoll函式
上文中說到如果從100的不同的地方取外賣,那麼epoll相當於一部手機,當外賣到達後,送貨員可以通知你,從而達到每去必得,少走很多路。 它是如何實現這些作用的呢? epoll的功能 epoll是select/poll的強化版,同是多路複用的函式,epoll
linux 高併發網路程式設計之epoll詳解
前言 I/O多路複用有很多種實現。在linux上,2.4核心前主要是select和poll,自Linux 2.6核心正式引入epoll以來,epoll已經成為了目前實現高效能網路伺服器的必備技術。儘管他們的使用方法不盡相同,但是本質上卻沒有什麼區別。本文將重點探