服务器的并发本质上都一样,都是将创建连接、收发数据的功能分配给子线程;父线程仅负责while循环下进行检测
父进程
1、完成服务器的设置;
2、创建epoll树和监听的epoll实例;
3、完成while循环,并把参数传递给子进程。
int main()
{
//完成线程锁的初始化
pthread_mutex_init(&mutex,NULL);
// 创建监听的lfd
int lfd = socket(AF_INET, SOCK_STREAM, 0);
// 定义服务器性质,并储存在sockaddr_in addr中;绑定。
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9999);
addr.sin_addr.s_addr = INADDR_ANY;
bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
//开启监听
listen(lfd, 128);
//创建epoll树
int epfd = epoll_create(100);
//上锁
pthread_mutex_lock(&mutex);
//创建epoll实例以及实例的数组
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = lfd;
int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
struct epoll_event evs[1024];
int size = sizeof(evs) / sizeof(struct epoll_event);
//开始循环
while(1)
{
int num = epoll_wait(epfd, evs, size, -1);
for(int i = 0; i < num; i++)
{
int curfd = evs[i].data.fd;
if(curfd == lfd)
{
Info info;
info.lfd = lfd;
info.epfd = epfd;
info.ev.events = EPOLLIN | EPOLLET;
info.ev.data.fd = curfd;
pthread_t tid;
pthread_create(&tid,NULL,acceptConn,&info);
pthread_detach(tid);
}
else
{
Info info;
info.lfd = lfd;
info.epfd = epfd;
info.ev.events = EPOLLIN | EPOLLET;
info.ev.data.fd = curfd;
pthread_t tid;
pthread_create(&tid,NULL,Communication,&info);
pthread_detach(tid);
}
}
}
子进程
监听子进程
typedef struct Info{
int epfd;
int lfd;
struct epoll_event ev;
}
---------------------------------------------------------------------------------------------
void* acceptConn(void *arg)
{
Info* tmp = (Info*) arg;
int fd = tmp->ev.data.fd;//取出监听的fd
int cfd = accept(fd , NULL, NULL);
tmp.ev.events = EPOLLIN | EPOLLET;
tmp.ev.data.fd = cfd;
//获得flag信息并修改
int flag = fcntl(cfd, F_GETFL);
flag |= O_NONBLOCK;
fcntl(cfd, F_SETFL, flag);
pthread_mutex_lock(&mutex);
int ret = epoll_ctl(tmp->epfd, EPOLL_CTL_ADD, cfd, &(tmp->ev));//该ev已经在外面写好了,并且随着info传进来了,可以直接使用
pthread_mutex_unlock(&mutex);
}
读写子线程
void Communication(void *argv){
// 处理通信的文件描述符
// 接收数据
int* info = (int*) argv;
char buf[1024];
memset(buf, 0, sizeof(buf));
while(1)
{
int len = recv((info ->ev).data.fd, buf, sizeof(buf), 0);
if(len == -1)
{
if(errno = EAGAIN) //头文件error.h
{
printf("数据读取完毕");
send(info->lfd,buf,strlen(buf)+1,0);
epoll_ctl(info->epfd, EPOLL_CTL_DEL,(info>ev).data.fd, NULL);
close((info ->ev).data.fd);
break;
}
else
{
perror("recv error");
break;
//如果使用exit会退出全部线程
}
}
}
}
注意:当打印buff里面的字符串时,如果结尾处没有"/0"容易导致末尾处的一个字节的乱码。