#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/epoll.h>
//多线程边沿触发非阻塞epoll--终极无敌版
typedef struct abc{
int lfd;
int cfd;
int epfd;
struct epoll_event *event_msg_st;
}pthread_msg_st;
void listen_fun(void *arg){//监听线程
pthread_detach(pthread_self());
pthread_msg_st *st=(pthread_msg_st *)arg;
st->cfd=accept(st->lfd,NULL,NULL);
int flag=fcntl(st->cfd,F_GETFL);
fcntl(st->cfd,F_SETFL,flag|O_NONBLOCK);//cfd设置为非阻塞
st->event_msg_st->events=EPOLLIN|EPOLLET;
st->event_msg_st->data.fd=st->cfd;
epoll_ctl(st->epfd,EPOLL_CTL_ADD,st->cfd,st->event_msg_st);
printf("客户机连接成功\n");
}
void connect_fun(void *arg){//通信线程
pthread_detach(pthread_self());
pthread_msg_st *st=(pthread_msg_st *)arg;
char buff[5]={0};
while(1){
memset(buff,0,sizeof(buff));
int ret=recv(st->cfd,buff,sizeof(buff),0);
if(ret==-1){
if(errno==EAGAIN){//错误码为EAGAIN时,说明非阻塞读取数据读完了
putchar('\n');
break;
}else{
perror("recv error");
break;
}
}else if(ret==0){
printf("断开连接\n");
epoll_ctl(st->epfd,EPOLL_CTL_DEL,st->cfd,NULL);
close(st->cfd);
break;
}
write(1,buff,ret);
}
}
int main(int argc, const char *argv[]){
int lfd=socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in bind_st={AF_INET,htons(6666),inet_addr("192.168.250.100")};
bind(lfd,(struct sockaddr*)&bind_st,sizeof(bind_st));
listen(lfd,5);
int i=0,cfd=0,epfd=0,num=0;
pthread_t tid1=0,tid2=0;
pthread_msg_st msg_st={0};
int size=2000;
struct epoll_event *event_s=(struct epoll_event *)malloc(sizeof(struct epoll_event)*size);
memset(event_s,0,size*sizeof(struct epoll_event));
struct epoll_event event_msg_st={0};
event_msg_st.events=EPOLLIN|EPOLLET;//读事件,边沿触发
event_msg_st.data.fd=lfd;//事件触发后,遍历wait结构体数组的.data.fd可以确认套接字类型
epfd=epoll_create(1);
epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&event_msg_st);
printf("服务器启动\n");
while(1){
num=epoll_wait(epfd,event_s,size,-1);
for(i=0;i<num;i++){
if(event_s[i].data.fd==lfd){//监听套接字读事件
msg_st.lfd=event_s[i].data.fd;
msg_st.epfd=epfd;
msg_st.event_msg_st=&event_msg_st;
pthread_create(&tid1,NULL,(void*)listen_fun,(void*)&msg_st);
}else{//通信套接字读事件
msg_st.cfd=event_s[i].data.fd;
msg_st.epfd=epfd;
msg_st.event_msg_st=&event_msg_st;
pthread_create(&tid2,NULL,(void*)connect_fun,(void*)&msg_st);
}
}
}
free(event_s);
close(lfd);
return 0;
}
注意:1.编译时需要加 -lpthread
2.端口号与IP地址自行修改
3.有些头文件不是必须的,写上也没事
4.加油!