提示:程式碼基於epoll的ET模式 【當然它預設是LT模式,還有EPOLLONESHOT】 epoll是最高效的一種模式,內部採用了複雜的二叉樹【最頭暈的AVL樹和紅黑TREE,特別是插入,刪除,查詢比線性表高效,作業系統核心使用了大量的資料結構和演算法,自己去看系統導論就清楚了】
讀者務必瞭解網路卡工作原理【做過網路卡晶片與常規的嵌入式晶片通訊驅動最好容易理解,藉助電子廠商的datasheet來擼理解會深刻一些】,硬體中斷原理,TCP/IP協議,TCP/UDP的區別,連線與關閉狀態轉移,TCP/UDP API【socket】,瞭解常規的線性表實現和樹的資料結構。
理解linux裝置檔案和硬體的關係,知道程式的常規排程演算法【以便於理解阻塞,非阻塞,非同步,輪詢】
理解IO流和緩衝IO【比如對檔案描述符的操作】
IO模型知識
以上知識理論便以理解以下程式碼【不然真跟碼農一樣抄著程式碼嘍^_^】
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <sys/epoll.h>
#include <libgen.h>
#define MAX_EVENT_NUMBER 1024
#define TCP_BUFFER_SIZE 512
#define UDP_BUFFER_SIZE 1024
int setnonblock(int fd)
{
int old_option = fcntl(fd,F_GETFL);
int new_option = old_option|O_NONBLOCK;
fcntl(fd,F_SETFL,new_option);
return old_option;
}
void addfd(int epollfd,int fd)
{
struct epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN|EPOLLET;
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
setnonblock(fd);
}
int main(int argc,char *argv[])
{
if(argc<2){
printf("useage:%s ip and port\n",basename(argv[0]));
return 0;
}
const char *ip = argv[1];
int port = atoi(argv[2]);
struct sockaddr_in address;
bzero(&address,sizeof(&address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
inet_pton(AF_INET,ip,&address.sin_addr);
int sockfd = socket(PF_INET,SOCK_STREAM,0);
assert(sockfd>0);
int ret = bind(sockfd,(struct sockaddr*)&address,sizeof(address));
assert(ret!=-1);
ret = listen(sockfd,1024);
bzero(&address,sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
inet_pton(AF_INET,ip,&address.sin_addr);
int udpfd = socket(PF_INET,SOCK_DGRAM,0);
assert(udpfd>0);
ret = bind(udpfd,(struct sockaddr*)&address,sizeof(address));
assert(ret!=-1);
struct epoll_event events[MAX_EVENT_NUMBER];
int epollfd = epoll_create(1024);
addfd(epollfd,sockfd);
addfd(epollfd,udpfd);
while(1){
int number = epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);
if(number <0){
printf("epoll fail\n");
break;
}
int i;
for(i=0;i<number;i++){
int fd = events[i].data.fd;
if(fd == sockfd){
struct sockaddr_in client;
socklen_t client_len = sizeof(client);
int connfd = accept(sockfd,(struct sockaddr*)&client,&client_len);
printf("client come in\n");
addfd(epollfd,connfd);
}
else if(fd == udpfd){
char buf[UDP_BUFFER_SIZE];
memset(buf,'\0',UDP_BUFFER_SIZE);
struct sockaddr_in client;
socklen_t client_len = sizeof(client);
ret = recvfrom(udpfd,buf,UDP_BUFFER_SIZE,0,(struct sockaddr*)&client,&client_len);
printf("收到udp客戶端的資料:%s\n",buf);
if(ret >0){
sendto(udpfd,buf,UDP_BUFFER_SIZE,0,(struct sockaddr*)&client,client_len);
}
}
else if(events[i].events & EPOLLIN){
char buf[TCP_BUFFER_SIZE];
while(1){
memset(buf,'\0',TCP_BUFFER_SIZE);
ret = recv(fd,buf,TCP_BUFFER_SIZE-1,0);
if(ret < 0){
if((errno == EAGAIN)||(errno == EWOULDBLOCK)){
break;
}
close(fd);
break;
}
else if(ret ==0){
close(fd);
}else{
printf("收到tcp客戶端的資料:%s\n",buf);
send(fd,buf,TCP_BUFFER_SIZE,0);
struct sockaddr_in client;
socklen_t client_len = sizeof(client);
getpeername(fd,(struct sockaddr*)&client,&client_len);
char ip[INET_ADDRSTRLEN];
printf("your ip is %s,port is %d\n",inet_ntop(AF_INET,&client.sin_addr,ip,INET_ADDRSTRLEN),ntohs(client.sin_port));
//sendto(udpfd,buf,TCP_BUFFER_SIZE,0,(struct sockaddr*)&client,client_len);
}
}
}else{
printf("其它事件\n");
}
}
}
close(sockfd);
return 0;
}