linux下I/O複用與epoll實際使用(二)
上一節《linux下I/O複用與epoll實際使用(一)》主要講解了epoll的原理,這一節結合socket的程式設計,詳解select與epoll程式設計示例。 一、socket程式設計 在TCP/IP協議中“IP地址+TCP或者UDP埠號”唯一標識網路通訊中的一個程序,"IP+埠號"就稱為socket。在TCP協議中,建立連線的兩個程序各自有一個socket來標識,name兩個socket組成的socjet pair就唯一標識一個連線。 預備知識: 網路位元組序:記憶體中多位元組資料相對於記憶體地址有大端小端之分,磁碟檔案中的多位元組資料相對於檔案中的偏移地址也有大端小端之分。網路資料流同樣有大端小端之分,所以傳送主機通常將傳送緩衝區中的資料按記憶體地址從低到高的順序發出,接收主機把從網路上接收到的位元組按記憶體從低到高的順序儲存,因此網路資料流的地址應該規定:**先發出的資料是低地址,後發出的資料是高地址。**TCP/IP協議規定網路資料流應該採用大端位元組序,即低地址高位元組。所以傳送主機和接收主機是小段位元組序的在傳送和接收之前需要做位元組序的轉換。
為了使網路程式具有可移植性可以呼叫以下函式進行網路位元組數的轉換。
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
socket地址資料型別及相關函式 sockaddr資料結構
tcpsocket 實現 實現模型: 1.伺服器端 socket -> bind -> listen -> accept(阻塞,三次握手)-> send。 2.客戶端 socket -> connect(阻塞,三次握手)-> rcv。 上一節我們已經比較過select與epoll的優缺點。 現在直接上select和epoll的示例程式碼: 程式碼在(一)的基礎上修改的 server端
#include<stdio.h> #include<arpa/inet.h> #include<unistd.h> #include<stdlib.h> #include<sys/socket.h> #include<string.h> #include<errno.h> #include<signal.h> #include<fcntl.h> #include<stdbool.h> #include<sys/epoll.h> #define _SELECT int gSetNonblocking(int fd) { int old_option=fcntl(fd,F_GETFL); int new_option=old_option|O_NONBLOCK; fcntl(fd,F_SETFL,new_option); return new_option; } /*往 epoll 描述符新增套接字*/ void gAddfd(int epollfd,int fd,bool oneshoot) { struct epoll_event event; event.data.fd=fd; event.events=EPOLLIN; int ret; if(oneshoot) { event.events=event.events|EPOLLONESHOT; } ret=epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event); gSetNonblocking(fd); } void gDelfd(int epollfd,int fd,bool oneshoot) { struct epoll_event event; event.data.fd=fd; event.events=EPOLLIN; int ret; if(oneshoot) { event.events=event.events|EPOLLONESHOT; } ret=epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event); if(ret!=0) { if(errno==EEXIST) { epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&event); } } } void gCtrlfd(int epollfd,int fd,bool oneshoot) { struct epoll_event event; event.data.fd=fd; event.events=EPOLLIN; int ret; if(oneshoot) { event.events=event.events|EPOLLONESHOT; } ret=epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event); if(ret!=0) { if(errno==EEXIST) { epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&event); } } } int main(int argc,char *argv[]) { int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) write(STDERR_FILENO,"socket error",14); struct sockaddr_in addr; memset(&addr,0,sizeof(addr)); addr.sin_family=AF_INET; addr.sin_port=htons(atoi(argv[1])); addr.sin_addr.s_addr=INADDR_ANY; bind(sock,(struct sockaddr *)&addr,sizeof(addr)); listen(sock,32767); signal(SIGPIPE,SIG_IGN); // signal(SIGINT,sig); #ifdef _SELECT int maxfd=0,i=0; int array[4096]; int ret; fd_set rfds; fd_set wfds; array[0]=sock; int array_size=sizeof(array)/sizeof(array[0]); for(i=1;i<array_size;i++) { array[i]=-1; } #else int epollfd; epollfd=epoll_create(20); gAddfd(epollfd,sock,false); int connfd[20]={-1}; int number; int i,j; int sockfd; int count=0; struct epoll_event event[512]; #endif while(1) { #ifdef _SELECT FD_ZERO(&rfds); FD_ZERO(&wfds); for(i=0;i<array_size;i++) { if(array[i]>0) { FD_SET(array[i],&rfds); FD_SET(array[i],&wfds); if(array[i]>maxfd) { maxfd=array[i]; } } } ret=select(maxfd+1,&rfds,&wfds,NULL,NULL); if(ret>0) { int j; for(j=0;j<array_size;j++) { if(j==0 && FD_ISSET(array[j],&rfds)) { struct sockaddr_in client; socklen_t len=sizeof(client); int new_sock=accept(sock,(struct sockaddr *)&client,&len); if(new_sock<0) { perror("accept"); continue; } else { printf("get a new client %s\n",inet_ntoa(client.sin_addr)); fflush(stdout); int k=1; for(;k<array_size;++k) { if(array[k]<0) { array[k]=new_sock; if(new_sock > maxfd) maxfd=new_sock; break; } } if(k==array_size) { close(new_sock); } } }else if(j!=0 && FD_ISSET(array[j],&rfds)) { char buf[1024]; ssize_t s=read(array[j],buf,sizeof(buf)-1); if(s>0) { buf[s]='\0'; printf("clientsay#%s",buf); if(FD_ISSET(array[j],&wfds)) { char *msg="HTTP/1.0 200 OK <\r\n\r\n<html><h1>welecom to select world!!!</h1></html>\r\n"; write(array[j],msg,strlen(msg)); } } else if(0==s) { printf("client quit\n"); close(array[j]); array[j]=-1; } else { perror("read"); close(array[j]); array[j]=-1; } } } } #else number=epoll_wait(epollfd,event,512,-1); if(number < 0 && errno !=EINTR) { printf("epoll failure\n"); break; } for(i=0;i<number;i++) { sockfd=event[i].data.fd; printf("sockfd=%d,sock=%d,connfd=%d",sockfd,sock,connfd[count]); if(sockfd==sock && (event[i].events & EPOLLIN)) { struct sockaddr_in cliaddr; socklen_t clilen=sizeof(struct sockaddr_in); if(connfd[count]==-1) { connfd[count]=accept(sock,(struct sockaddr *)&cliaddr,&clilen); }else { for(j=0;j<20;j++) { if(connfd[j]==-1) { count=j; } } connfd[count]=accept(sock,(struct sockaddr *)&cliaddr,&clilen); } if(connfd[count]<0) { printf("errno is -> %d:%s\n",errno,strerror(errno)); continue; } /*設定連線套接字EPOLLONESHOT*/ gAddfd(epollfd,connfd[count],false); count++; if(count>19) { for(j=0;j<count;j++) { if(connfd[j]==-1) { count=j; } } /*reserve*/ } printf("client connect\n"); }else { for(j=0;j<=count;j++) { if(sockfd==connfd[j] && (event[i].events & EPOLLIN)) { printf("Start sleep(10) ...\n"); sleep(10); char text[512]; int ret=recv(connfd[j],text,512,0); while(recv>0) { if(ret>0) { text[ret]='\0'; printf("Recv(%d):%s\n",ret,text); } else if(ret==0) { printf("Client close socket\n"); gDelfd(epollfd,connfd[j],false); close(connfd[j]); connfd[j]=-1; break; } else if(errno == EWOULDBLOCK) { printf("wouldblock\n"); break; } else if(errno == EPIPE) { printf("Broken pipe\n"); break; } ret=recv(connfd[j],text,512,0); } } } } } #endif } }
client端
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/un.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/epoll.h>
#include <fcntl.h>
#define path "tempfile.socket"
int sock;
void sig(int sig)
{
char buf[512];
memset(buf, 0, sizeof(buf));
sprintf(buf, "close client %d",sock);
write(sock, buf, strlen(buf));
// sleep(3);
// exit(-1);
close(sock);
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("Usage:./exec [port]\n");
exit(-1);
}
signal(SIGINT, sig);
sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[1]));
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
connect(sock, (struct sockaddr *)&addr, sizeof(addr));
int old = fcntl(sock, F_GETFL);
int newoption = old | O_NONBLOCK;
fcntl(sock, F_SETFL, newoption);
char buf[512];
memset(buf, 0, sizeof(buf));
int epollfd = epoll_create(5);
struct epoll_event event[128];
struct epoll_event e1,e2;
e1.data.fd = sock;
e1.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &e1);
e2.data.fd = STDIN_FILENO;
e2.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &e2);
int nR;
int ret;
int i;
while (1)
{
int number = epoll_wait(epollfd, event, 128, -1);
if (number <= 0)
{
printf("epoll_wait error\n");
return 0;
}
for(i = 0; i < number; ++i)
{
if (event[i].data.fd == sock && (event[i].events & EPOLLIN))
{
memset(buf, 0, sizeof(buf));
nR = read(sock, buf, 512);
if (nR == 0)
{
close(sock);
break;
}
write(STDOUT_FILENO, buf, nR);
}
else if (event[i].data.fd == STDIN_FILENO && (event[i].events & EPOLLIN))
{
printf("please input string:");
fflush(stdout);
memset(buf, 0, sizeof(buf));
nR = read(STDIN_FILENO, buf, 512);
if (nR <= 0)
{
printf("errno[%d]:%s\n", errno, strerror(errno));
}
ret = write(sock, buf, nR);
if (ret == 0 && errno == EINTR)
{
printf("write sock error\n");
exit(-1);
}
else if (ret < 0)
{
printf("write sock ret < 0\n");
exit(-1);
}
printf("Send [%d]byte\n", ret);
}
}
}
close(sock);
return 0;
}
執行結果: server
client