epoll ET模式伺服器和客戶端原始碼例子
關於epoll替代select作為高效能伺服器的事件通知機制的資料相當多,我就不在這裡班門弄斧了,有興趣的同學可以參考末尾的文獻連結。
這裡說明如下:
1.epoll是linux下高併發伺服器的完美方案,因為是基於事件觸發的,所以比select快的不只是一個數量級。
2.單執行緒epoll,觸發量可達到15000,參見文獻[4]
3.高效能server要使用非阻塞方式。
使用ET模型的時候,一定要注意,每次收到有效通知,然後讀取資料的時候,務必每次讀取乾淨(讀到出錯為止)。當再次呼叫check(sockfd)的時候才能正確返回。 目前使用的epoll模型大多都是ET模式,socket都要設定為非阻塞的。 網上找了很多原始碼例子,但是大多不理想,下面是我根據網上資料修改嘗試出的一個例子,供大家參考。後續更進一步地研究可以參考nginx,memcache,Apache traffic server這類開原始碼。epoll伺服器端
//compile: g++ -g epoll_server.cpp -o epoll_server //run: ./epoll_server // #include <sys/socket.h> #include <sys/epoll.h> #include <sys/sendfile.h> #include <sys/wait.h> #include <sys/stat.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <fcntl.h> #include <errno.h> #define MAX_EVENTS 10 #define LISTENQ 20 #define PORT 5000 //8080 //設定socket連線為非阻塞模式 void setnonblocking (int fd) { int opts; opts = fcntl (fd, F_GETFL); if (opts < 0) { perror ("fcntl(F_GETFL)\n"); exit (1); } opts = (opts | O_NONBLOCK); if (fcntl (fd, F_SETFL, opts) < 0) { perror ("fcntl(F_SETFL)\n"); exit (1); } } int main () { struct epoll_event ev, events[MAX_EVENTS]; int listenfd, connfd, nfds, epfd, sockfd, i, nread, n; struct sockaddr_in local, remote; socklen_t addrlen; char buf[BUFSIZ]; //建立listen socket if ((listenfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) { perror ("sockfd\n"); exit (1); } setnonblocking (listenfd); bzero (&local, sizeof (local)); local.sin_family = AF_INET; local.sin_addr.s_addr = htonl (INADDR_ANY);; local.sin_port = htons (PORT); if (bind (listenfd, (struct sockaddr *) &local, sizeof (local)) < 0) { perror ("bind\n"); exit (1); } listen (listenfd, LISTENQ); epfd = epoll_create (MAX_EVENTS); if (epfd == -1) { perror ("epoll_create"); exit (EXIT_FAILURE); } ev.events = EPOLLIN; ev.data.fd = listenfd; if (epoll_ctl (epfd, EPOLL_CTL_ADD, listenfd, &ev) == -1) { perror ("epoll_ctl: listen_sock"); exit (EXIT_FAILURE); } for (;;) { nfds = epoll_wait (epfd, events, MAX_EVENTS, -1); if (nfds == -1) { perror ("epoll_wait error"); exit (EXIT_FAILURE); } for (i = 0; i < nfds; ++i) { sockfd = events[i].data.fd; if (sockfd == listenfd) { while ((connfd = accept (listenfd, (struct sockaddr *) &remote, &addrlen)) > 0) { char *ipaddr = inet_ntoa (remote.sin_addr); printf("accept a connection from [%s]\n", ipaddr); setnonblocking (connfd); //設定連線socket為非阻塞 ev.events = EPOLLIN | EPOLLET; //邊沿觸發要求套接字為非阻塞模式;水平觸發可以是阻塞或非阻塞模式 ev.data.fd = connfd; if (epoll_ctl (epfd, EPOLL_CTL_ADD, connfd, &ev) == -1) { perror ("epoll_ctl: add"); exit (EXIT_FAILURE); } } if (connfd == -1) { if (errno != EAGAIN && errno != ECONNABORTED && errno != EPROTO && errno != EINTR) perror ("accept"); } continue; } if (events[i].events & EPOLLIN) { n = 0; while ((nread = read (sockfd, buf + n, BUFSIZ - 1)) > 0) { n += nread; } if (nread == -1 && errno != EAGAIN) { perror ("read error"); } printf("recv from client data [%s]\n", buf); ev.data.fd = sockfd; ev.events = events[i].events | EPOLLOUT; if (epoll_ctl (epfd, EPOLL_CTL_MOD, sockfd, &ev) == -1) { perror ("epoll_ctl: mod"); } } if (events[i].events & EPOLLOUT) { snprintf (buf, sizeof(buf), "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\nHello World", 11); int nwrite, data_size = strlen (buf); n = data_size; while (n > 0) { nwrite = write (sockfd, buf + data_size - n, n); if (nwrite < n) { if (nwrite == -1 && errno != EAGAIN) { perror ("write error"); } break; } n -= nwrite; } printf("send to client data [%s]\n", buf); close (sockfd); events[i].data.fd = -1; } } } close (epfd); close (listenfd); return 0; }
epoll客戶端
//compile: g++ -g epoll_client.cpp -o epoll_client
//run: ./epoll_client
//
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
using namespace std;
#define PORT 5000
int main(int argc, char* argv[])
{
int sockfd, on = 1;
char buffer[512] = {0};
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
cout << "create socket fail" << endl;
return -1;
}
cout << "succeed to create client socket fd " << sockfd << endl;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
cout << "set socket reuse by etsockopt" << endl;
servaddr.sin_port = htons((short)PORT);
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //此處更改epoll伺服器地址
if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
cout << "connect error" << endl;
return -1;
}
cout << "succeed to connect epoll server " << endl;
char target[] = "The Author: [email protected]";
memcpy(buffer, target, strlen(target));
int wlen = send(sockfd, buffer, strlen(buffer), 0);
if(wlen <= 0)
cout << " send data to server fail " << strerror(errno) << endl;
cout << "send data to server on success, data: [" << buffer << "]"<< endl;
memset(buffer, 0, sizeof(buffer));
int rlen = recv(sockfd, buffer, sizeof(buffer), 0);
if(rlen <= 0)
cout << " receive data from server fail " << strerror(errno) << endl;
cout << "receive data from server on success, data: [" << buffer << "]" << endl;
return 0;
}
大家可以在上述框架上進一步修改.下面是執行圖
下面的參考文獻按照我認為的優先順序遞減排列
相關推薦
epoll ET模式伺服器和客戶端原始碼例子
關於epoll替代select作為高效能伺服器的事件通知機制的資料相當多,我就不在這裡班門弄斧了,有興趣的同學可以參考末尾的文獻連結。 這裡說明如下: 1.epoll是linux下高併發伺服器的完美方案,因為是基於事件觸發的,所以比select快的不只是一個數量級。 2.單
FTP伺服器和客戶端原始碼編寫問題(ftp server client source)
其實FTP也就是普通的Socket程式,只是需要按照FTP協議(RFC959, 1635?可能我記錯了)去做,也就是每個訊息有固定的結構的,比如頭3個位元組必須是200,201,300,400之類的數字表示操作結果。 寫FTP協議的程式主要要明白的一個關鍵問題是雙socket,一個control socke
golang thrift 原始碼分析,伺服器和客戶端究竟是如何工作的
首先編寫thrift檔案(rpcserver.thrift),執行thrift --gen go rpcserver.thrift,生成程式碼 namespace go rpc service RpcService { string SayHi(1: s
netty原始碼解解析(4.0)-20 ChannelHandler: 自己實現一個自定義協議的伺服器和客戶端
本章不會直接分析Netty原始碼,而是通過使用Netty的能力實現一個自定義協議的伺服器和客戶端。通過這樣的實踐,可以更深刻地理解Netty的相關程式碼,同時可以瞭解,在設計實現自定義協議的過程中需要解決的一些關鍵問題。 本週章涉及到的程式碼可以從github上下載: https://git
socket程式設計回射伺服器和客戶端
//回射伺服器 #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet
網路程式設計(二)——伺服器和客戶端資訊的獲取
目錄 1、字串IP地址和二進位制IP地址結構的轉換 2.套接字檔案描述符的判定 3、IP地址與域名之間的相互轉換 4、協議名稱處理函式 1、字串IP地址和二進位制IP地址結構的轉換 #include <sys/socket.h> #inclu
第一個Netty程式——構建和執行Echo伺服器和客戶端
在構建之前,需要安裝開發環境:JDK和Apache Maven以及IDE。 pom檔案: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-ins
Java thrift伺服器和客戶端建立例項
首先環境介紹一下: 1.IntelliJ IDEA 2017.1 2.thrift-0.9.3 相信大家在看我這篇文章的時候已經對thrift通訊框架已有所調研,這裡就不再贅述了,直接進入正題: <1>建立HelloWorld.thrift namespace jav
Spring Boot2.0 Oauth2 伺服器和客戶端配置及原理
一、應用場景 為了理解OAuth的適用場合,讓我舉一個假設的例子。 有一個"雲沖印"的網站,可以將使用者儲存在Google的照片,沖印出來。使用者為了使用該服務,必須讓"雲沖印"讀取自己儲存在Google上的照片。 問題是隻有得到使用者的授權,Google才會同意"雲沖印"讀取這些
教你如何構建非同步伺服器和客戶端的 Kotlin 框架 Ktor
Ktor 是一個使用 Kotlin 以最小的成本快速建立 Web 應用程式的框架。 Ktor 是一個用於在連線系統(connected systems)中構建非同步伺服器和客戶端的 Kotlin 框架。它由 Kotlin 團隊建立,因此,它充分利用了 Kotlin 的語言特性,為開發者提供出色的體驗和執
【Redis】Redis在Ubuntu中的伺服器和客戶端操作
伺服器端 伺服器端的命令為: redis-server 可以使用help檢視幫助文件 redis-server --help 個人習慣 ps aux | grep redis # 檢視redis伺服器程序 sudo kill -9
ROS學習筆記18 (編寫簡單的伺服器和客戶端 (C++))
1 編寫Service節點 這裡,我們將建立一個簡單的service節點("add_two_ints_server"),該節點將接收到兩個整型數字,並返回它們的和。 進入先前你在catkin workspace教程中所建立的beginner_tutorials包所在的目錄
ROS學習筆記19 (編寫簡單的伺服器和客戶端 (Python))
1 編寫服務端節點 我們會建立服務端節點 ("add_two_ints_server") ,節點接收兩個整型數字,並返回和 進入beginner_tutorials包 $ roscd beginner_tutorials 確保你確保已經在之前建立好AddTwoInts
(4)編寫簡單的伺服器和客戶端
目錄 編寫Service節點 程式碼 程式碼解釋 編寫Client節點 程式碼 程式碼解釋 編譯節點 編譯節點
Linux Epoll ET模式EPOLLOUT和EPOLLIN觸發時刻
ET模式稱為邊緣觸發模式,顧名思義,不到邊緣情況,是死都不會觸發的。 EPOLLOUT事件: EPOLLOUT事件只有在連線時觸發一次,表示可寫,其他時候想要觸發,那你要先準備好下面條件: 1.某次write,寫滿了傳送緩衝區,返回錯誤碼為EAGAIN。 2.對端讀取了
Socket通訊實現伺服器和客戶端對話
廣域網和區域網 介紹socket通訊前我們先介紹一下廣域網與區域網的概念。區域網簡稱LAN,是指在某一區域幾臺計算機組成的計算機組,區域網是封閉的,區域網經常採用共享通道,即共用同一條電纜。廣域網簡稱WAN,是一種跨越大的,地域性的地區性網路集合廣域網包含大大小
網路程式設計 伺服器和客戶端之間流的傳遞
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; /* 實現TCP客戶端,連線到伺服器 和伺服器
C#實現伺服器和客戶端之間通訊
TCP 套接字程式設計 伺服器端實現步驟: 1、使用Socket類建立套接字。 2、利用Bind方法將建立的套接字繫結到指定的地址結構。 3、利用Listen方法設定套接字為監聽模式,使得伺服器進入被動開啟狀態。 4、接受客戶端的連線請求。 5、接收、應答客戶端的資料請求
python實現tcp伺服器和客戶端(socket)
python實現tcp伺服器和客戶端(socket) 1.socket模組 socket是什麼 socket最初是為了同一主機上的應用程式建立的,使得一個程式與另外一個程式之間可以通訊,也就是所謂的程序間通訊,有兩種型別的socket:基於檔案和麵向網路的。
用node.js模擬伺服器和客戶端
伺服器 程式碼 var net = require("net") var server = net.createServer(); server.listen(12306,"127.0.0.1") server.on("listening",function(){ consol