目录
三、epoll_server:水平触发LT + 多触发读read
四、epoll_server:边沿触发ET + 非阻塞IO循环(read)
一、代码结构
1、客户端
clie_fd = socket(AF_INET, STREAM, 0);
connect(clie_fd, );
write();
read();
close();
2、服务端
serv_fd = socket(AF_INET, STREAM, 0);
bind();
listen(serv_fd, );
read();
write();
close();
二、敲代码:服务器-客户端
1、服务端
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <string.h>
#define serv_ip "127.0.0.1"
#define serv_port 6666
int main()
{
int serv_fd, clie_fd;
struct sockaddr_in serv_addr, clie_addr; //设置服务器地址
socklen_t clie_addr_len;
char buf[BUFSIZ];
char quitbuf[20];
int n = 0, i = 0;
char clie_ipbuf[15];
serv_fd = socket(AF_INET, SOCK_STREAM, 0); //创建socket
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(serv_port);
inet_pton(AF_INET, serv_ip, &serv_addr.sin_addr.s_addr);
//serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(serv_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); //绑定端口和ip到服务器套接字
listen(serv_fd, 128); //设置最大同时连接数
clie_addr_len = sizeof(clie_addr);
clie_fd = accept(serv_fd, (struct sockaddr *)&clie_addr, &clie_addr_len); //阻塞
printf("client ip: %s, client port: %d\n", inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_ipbuf, (socklen_t)sizeof(clie_ipbuf)), ntohs(clie_addr.sin_port));
while(1)
{
//读取
memset(buf, 0, sizeof(buf));
n = read(clie_fd, buf, sizeof(buf));
if(n < 0)
{
perror("read error");
exit(1);
}
//处理
printf("%s\n", buf);
for(i=0; i<n; i++)
{
buf[i] = toupper(buf[i]);
}
//回写
write(clie_fd, buf, n);
}
close(serv_fd);
close(clie_fd);
printf("server close...\n");
return 0;
}
2、客户端
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <string.h>
#define serv_ip "127.0.0.1"
#define serv_port 6666
int main()
{
int clie_fd;
struct sockaddr_in serv_addr; //设置服务器地址
socklen_t serv_addr_len;
char buf[BUFSIZ];
int n = 0;
clie_fd = socket(AF_INET, SOCK_STREAM, 0); //创建socket
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(serv_port);
//inet_pton(AF_INET, clie_ip, &clie_addr.sin_addr.s_addr);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //
serv_addr_len = sizeof(serv_addr);
connect(clie_fd, (struct sockaddr *)&serv_addr, serv_addr_len); //连接
read(clie_fd, buf, sizeof(buf));
printf("%s\n", buf);
while(1)
{
//从屏幕读
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin); //fgts()读一行 hello ---> "hello\n\0"
buf[strlen(buf)-1] = 0; //将\n ---> \0
if(strcmp(buf, "quit") == 0) {break;}
//写
write(clie_fd, buf, strlen(buf));
//读取
n = read(clie_fd, buf, sizeof(buf));
//打印处理
printf("%s\n", buf);
}
close(clie_fd);
printf("client close...\n");
return 0;
}
三、epoll_server:水平触发LT + 多触发读read
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#define serv_ip "127.0.0.1"
#define serv_port 6666
#define OPEN_MAX 1000
void perr_exit(const char *arg)
{
printf("%s", arg);
exit(1);
}
int main()
{
int lfd, cfd, epfd; //文件描述符
struct sockaddr_in serv_addr, clie_addr; //地址
socklen_t clie_addr_len;
char clie_ipbuf[16];
char buf[BUFSIZ];
int n = 0, i = 0, res = 0, nready = 0 ;
struct epoll_event evt, evts[OPEN_MAX]; //事件
lfd = socket(AF_INET, SOCK_STREAM, 0); //创建socket
int opt = 1;
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); //端口复用,不受2MSL时间影响
serv_addr.sin_family = AF_INET; //server地址
serv_addr.sin_port = htons(serv_port);
inet_pton(AF_INET, serv_ip, &serv_addr.sin_addr.s_addr);
//serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
res = bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); //绑定端口和ip到服务器套接字
if(res == -1) { perr_exit("bind error\n"); }
listen(lfd, 128); //设置最大同时连接数
epfd = epoll_create(OPEN_MAX); //创建 EPOLL 模型,epfd 指向红黑树树根
if(epfd == -1) { perr_exit("epoll_create error\n"); }
evt.events = EPOLLIN; //文件描述符的事件
evt.data.fd = lfd;
res = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &evt); //添加 listen 事件
if(res == -1) { perr_exit("epoll_ctl_add error\n"); }
while(1)
{
nready = epoll_wait(epfd, evts, OPEN_MAX, -1); //内核阻塞
if(nready == -1) { perr_exit("epoll_wait error\n"); }
for(i=0; i<nready; i++)
{
if(!(evts[i].events & EPOLLIN)) { continue; } //过滤非读事件
if(evts[i].data.fd == lfd) //监听事件
{
clie_addr_len = sizeof(clie_addr);
cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len); //非阻塞
//打印
printf("connected to client: %s, %d\n", inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_ipbuf, (socklen_t)sizeof(clie_ipbuf)), ntohs(clie_addr.sin_port));
//回写
sprintf(buf, "connected to server: %s, %d", serv_ip, serv_port);
write(cfd, buf, strlen(buf));
evt.events = EPOLLIN;
evt.data.fd = cfd;
res = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &evt); //添加事件
if(res == -1) { perr_exit("epoll_ctl_add error\n"); }
}
else //读事件
{
cfd = evts[i].data.fd; //获取文件描述符
memset(buf, 0, sizeof(buf));
n = read(cfd, buf, sizeof(buf)); //读文件
if(n < 0) //出错
{
perr_exit("read error\n");
close(cfd);
}
else if(n == 0) //客户端断开
{
//break;
printf("disconnected to client: %s, %d\n", inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_ipbuf, (socklen_t)sizeof(clie_ipbuf)), ntohs(clie_addr.sin_port));
res = epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL); //事件删除
if(res == -1) { perr_exit("epoll_ctl_del error\n"); }
close(cfd); //文件关闭
}
//处理,对应不同的终端,可以执行不同的程序,可以fork子进程
if(buf[0]=='0'){
printf("terminal 0\n");
}
printf("%s\n", buf);
for(i=0; i<n; i++)
{
buf[i] = toupper(buf[i]);
}
//写
write(cfd, buf, n);
}
}
}
close(lfd);
printf("server close...\n");
return 0;
}
//ps -aux | grep server
//cat /proc/sys/fs/file-max //查看一个进程可以打开的文件描述符上限 93881
//sudo vi /etc/security/limits.conf //在文件尾写下如下设置,soft 软限制,hard 硬限制
/* <type> <item> <value>
* soft nofile 65536
* hard nofile 100000
*/
四、epoll_server:边沿触发ET + 非阻塞IO循环(read)
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <fcntl.h>
#define serv_ip "127.0.0.1"
#define serv_port 6666
#define OPEN_MAX 1000
#define MAX_LINE 5
void perr_exit(const char *arg)
{
printf("%s", arg);
exit(1);
}
int main()
{
int lfd, cfd, epfd; //文件描述符
struct sockaddr_in serv_addr, clie_addr; //地址
socklen_t clie_addr_len;
char clie_ipbuf[16];
char buf[BUFSIZ];
int n = 0, i = 0, res = 0, nready = 0, flag;
struct epoll_event evt, evts[OPEN_MAX]; //事件
lfd = socket(AF_INET, SOCK_STREAM, 0); //创建socket
int opt = 1;
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); //端口复用,不受2MSL时间影响
serv_addr.sin_family = AF_INET; //server地址
serv_addr.sin_port = htons(serv_port);
inet_pton(AF_INET, serv_ip, &serv_addr.sin_addr.s_addr);
//serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
res = bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); //绑定端口和ip到服务器套接字
if(res == -1) { perr_exit("bind error\n"); }
listen(lfd, 128); //设置最大同时连接数
epfd = epoll_create(OPEN_MAX); //创建 EPOLL 模型,epfd 指向红黑树树根
if(epfd == -1) { perr_exit("epoll_create error\n"); }
//evt.events = EPOLLIN; //文件描述符的事件,默认为水平触发
evt.events = EPOLLIN | EPOLLET; //边沿触发
evt.data.fd = lfd;
res = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &evt); //添加 listen 事件
if(res == -1) { perr_exit("epoll_ctl_add error\n"); }
while(1)
{
nready = epoll_wait(epfd, evts, OPEN_MAX, -1); //内核阻塞
if(nready == -1) { perr_exit("epoll_wait error\n"); }
for(i=0; i<nready; i++)
{
if(!(evts[i].events & EPOLLIN)) { continue; } //过滤非读事件
if(evts[i].data.fd == lfd) //监听事件
{
clie_addr_len = sizeof(clie_addr);
cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len); //非阻塞
//打印
printf("connected to client: %s, %d\n", inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_ipbuf, (socklen_t)sizeof(clie_ipbuf)), ntohs(clie_addr.sin_port));
//回写
sprintf(buf, "connected to server: %s, %d", serv_ip, serv_port);
write(cfd, buf, strlen(buf));
//修改 cfd 为非阻塞读
flag = fcntl(cfd, F_GETFL); //获得flag
flag |= O_NONBLOCK;
fcntl(cfd, F_SETFL, flag); //设置文件为非阻塞
evt.events = EPOLLIN;
evt.data.fd = cfd;
res = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &evt); //添加事件
if(res == -1) { perr_exit("epoll_ctl_add error\n"); }
}
else //读事件
{
cfd = evts[i].data.fd; //获取文件描述符
memset(buf, 0, sizeof(buf));
n = read(cfd, buf, MAX_LINE); //读文件。 若用 readn(),阻塞和不阻塞由 cfd 的 flag 设置,若该处
if(n < 0) { perr_exit("read error\n"); }
else if(n == 0) //客户端断开
{
//break;
printf("disconnected to clie nt: %s, %d\n", inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_ipbuf, (socklen_t)sizeof(clie_ipbuf)), ntohs(clie_addr.sin_port));
res = epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL); //事件删除
if(res == -1) { perr_exit("epoll_ctl_del error\n"); }
close(cfd); //文件关闭
}
//处理
printf("%s\n", buf);
/*
for(i=0; i<n; i++)
{
buf[i] = toupper(buf[i]);
}
//写
write(cfd, buf, n);
*/
}
}
}
close(lfd);
printf("server close...\n");
return 0;
}