一、性能查看的方法:
1、top 查看CPU使用情况;
2、free -m 查看内存情况;
二、I/O复用的基本概念
三、select
1、缺点:
(1)最大只能监听1024个fd;
(2)select能够监听到事件的发生,但是不知道 是哪一个fd,需要循环判断
导致效率不高;
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/fcntl.h>
// 初始化服务端的监听端口。
int initserver(int port);
int main(int argc,char *argv[])
{
if (argc != 2)
{
printf("usage: ./tcpselect port\n"); return -1;
}
// 初始化服务端用于监听的socket。
int listensock = initserver(atoi(argv[1]));
printf("listensock=%d\n",listensock);
if (listensock < 0)
{
printf("initserver() failed.\n"); return -1;
}
fd_set readfdset; // 读事件的集合,包括监听socket和客户端连接上来的socket。
int maxfd; // readfdset中socket的最大值。
// 初始化结构体,把listensock添加到集合中。
FD_ZERO(&readfdset);
FD_SET(listensock,&readfdset);
maxfd = listensock;
while (1)
{
// 调用select函数时,会改变socket集合的内容,所以要把socket集合保存下来,传一个临时的给select。
fd_set tmpfdset = readfdset;
int infds = select(maxfd+1,&tmpfdset,NULL,NULL,NULL);
// printf("select infds=%d\n",infds);
// 返回失败。
if (infds < 0)
{
printf("select() failed.\n"); perror("select()"); break;
}
// 超时,在本程序中,select函数最后一个参数为空,不存在超时的情况,但以下代码还是留着。
if (infds == 0)
{
printf("select() timeout.\n"); continue;
}
// 检查有事情发生的socket,包括监听和客户端连接的socket。
// 这里是客户端的socket事件,每次都要遍历整个集合,因为可能有多个socket有事件。
for (int eventfd=0; eventfd <= maxfd; eventfd++)
{
if (FD_ISSET(eventfd,&tmpfdset)<=0) continue;
if (eventfd==listensock)
{
// 如果发生事件的是listensock,表示有新的客户端连上来。
struct sockaddr_in client;
socklen_t len = sizeof(client);
int clientsock = accept(listensock,(struct sockaddr*)&client,&len);
if (clientsock < 0)
{
printf("accept() failed.\n"); continue;
}
printf ("client(socket=%d) connected ok.\n",clientsock);
// 把新的客户端socket加入集合。
FD_SET(clientsock,&readfdset);
if (maxfd < clientsock) maxfd = clientsock;
continue;
}
else
{
// 客户端有数据过来或客户端的socket连接被断开。
char buffer[1024];
memset(buffer,0,sizeof(buffer));
// 读取客户端的数据。
ssize_t isize=read(eventfd,buffer,sizeof(buffer));
// 发生了错误或socket被对方关闭。
if (isize <=0)
{
printf("client(eventfd=%d) disconnected.\n",eventfd);
close(eventfd); // 关闭客户端的socket。
FD_CLR(eventfd,&readfdset); // 从集合中移去客户端的socket。
// 重新计算maxfd的值,注意,只有当eventfd==maxfd时才需要计算。
if (eventfd == maxfd)
{
for (int ii=maxfd;ii>0;ii--)
{
if (FD_ISSET(ii,&readfdset))
{
maxfd = ii; break;
}
}
printf("maxfd=%d\n",maxfd);
}
continue;
}
printf("recv(eventfd=%d,size=%d):%s\n",eventfd,isize,buffer);
// 把收到的报文发回给客户端。
write(eventfd,buffer,strlen(buffer));
}
}
}
return 0;
}
// 初始化服务端的监听端口。
int initserver(int port)
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if (sock < 0)
{
printf("socket() failed.\n"); return -1;
}
// Linux如下
int opt = 1; unsigned int len = sizeof(opt);
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,len);
setsockopt(sock,SOL_SOCKET,SO_KEEPALIVE,&opt,len);
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
if (bind(sock,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0 )
{
printf("bind() failed.\n"); close(sock); return -1;
}
if (listen(sock,5) != 0 )
{
printf("listen() failed.\n"); close(sock); return -1;
}
return sock;
}
四、poll
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/fcntl.h>
// ulimit -n
#define MAXNFDS 1024
// 初始化服务端的监听端口。
int initserver(int port);
int main(int argc,char *argv[])
{
if (argc != 2)
{
printf("usage: ./tcppoll port\n"); return -1;
}
// 初始化服务端用于监听的socket。
int listensock = initserver(atoi(argv[1]));
printf("listensock=%d\n",listensock);
if (listensock < 0)
{
printf("initserver() failed.\n"); return -1;
}
int maxfd; // fds数组中需要监视的socket的大小。
struct pollfd fds[MAXNFDS]; // fds存放需要监视的socket。
for (int ii=0;ii<MAXNFDS;ii++) fds[ii].fd=-1; // 初始化数组,把全部的fd设置为-1。
// 把listensock添加到数组中。
fds[listensock].fd=listensock;
fds[listensock].events=POLLIN; // 有数据可读事件,包括新客户端的连接、客户端socket有数据可读和客户端socket断开三种情况。
maxfd=listensock;
while (1)
{
int infds = poll(fds,maxfd+1,5000);
// printf("poll infds=%d\n",infds);
// 返回失败。
if (infds < 0)
{
printf("poll() failed.\n"); perror("poll():"); break;
}
// 超时。
if (infds == 0)
{
printf("poll() timeout.\n"); continue;
}
// 检查有事情发生的socket,包括监听和客户端连接的socket。
// 这里是客户端的socket事件,每次都要遍历整个集合,因为可能有多个socket有事件。
for (int eventfd=0; eventfd <= maxfd; eventfd++)
{
if (fds[eventfd].fd<0) continue;
if ((fds[eventfd].revents&POLLIN)==0) continue;
fds[eventfd].revents=0; // 先把revents清空。
if (eventfd==listensock)
{
// 如果发生事件的是listensock,表示有新的客户端连上来。
struct sockaddr_in client;
socklen_t len = sizeof(client);
int clientsock = accept(listensock,(struct sockaddr*)&client,&len);
if (clientsock < 0)
{
printf("accept() failed.\n"); continue;
}
printf ("client(socket=%d) connected ok.\n",clientsock);
if (clientsock>MAXNFDS)
{
printf("clientsock(%d)>MAXNFDS(%d)\n",clientsock,MAXNFDS); close(clientsock); continue;
}
fds[clientsock].fd=clientsock;
fds[clientsock].events=POLLIN;
fds[clientsock].revents=0;
if (maxfd < clientsock) maxfd = clientsock;
printf("maxfd=%d\n",maxfd);
continue;
}
else
{
// 客户端有数据过来或客户端的socket连接被断开。
char buffer[1024];
memset(buffer,0,sizeof(buffer));
// 读取客户端的数据。
ssize_t isize=read(eventfd,buffer,sizeof(buffer));
// 发生了错误或socket被对方关闭。
if (isize <=0)
{
printf("client(eventfd=%d) disconnected.\n",eventfd);
close(eventfd); // 关闭客户端的socket。
fds[eventfd].fd=-1;
// 重新计算maxfd的值,注意,只有当eventfd==maxfd时才需要计算。
if (eventfd == maxfd)
{
for (int ii=maxfd;ii>0;ii--)
{
if ( fds[ii].fd != -1)
{
maxfd = ii; break;
}
}
printf("maxfd=%d\n",maxfd);
}
continue;
}
printf("recv(eventfd=%d,size=%d):%s\n",eventfd,isize,buffer);
// 把收到的报文发回给客户端。
write(eventfd,buffer,strlen(buffer));
}
}
}
return 0;
}
// 初始化服务端的监听端口。
int initserver(int port)
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if (sock < 0)
{
printf("socket() failed.\n"); return -1;
}
// Linux如下
int opt = 1; unsigned int len = sizeof(opt);
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,len);
setsockopt(sock,SOL_SOCKET,SO_KEEPALIVE,&opt,len);
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
if (bind(sock,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0 )
{
printf("bind() failed.\n"); close(sock); return -1;
}
if (listen(sock,5) != 0 )
{
printf("listen() failed.\n"); close(sock); return -1;
}
return sock;
}
五、epoll
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/types.h>
#define MAXEVENTS 100
// 把socket设置为非阻塞的方式。
int setnonblocking(int sockfd);
// 初始化服务端的监听端口。
int initserver(int port);
int main(int argc,char *argv[])
{
if (argc != 2)
{
printf("usage:./tcpepoll port\n"); return -1;
}
// 初始化服务端用于监听的socket。
int listensock = initserver(atoi(argv[1]));
printf("listensock=%d\n",listensock);
if (listensock < 0)
{
printf("initserver() failed.\n"); return -1;
}
int epollfd;
char buffer[1024];
memset(buffer,0,sizeof(buffer));
// 创建一个描述符
epollfd = epoll_create(1);
// 添加监听描述符事件
struct epoll_event ev;
ev.data.fd = listensock;
ev.events = EPOLLIN;
epoll_ctl(epollfd,EPOLL_CTL_ADD,listensock,&ev);
while (1)
{
struct epoll_event events[MAXEVENTS]; // 存放有事件发生的结构数组。
// 等待监视的socket有事件发生。
int infds = epoll_wait(epollfd,events,MAXEVENTS,-1);
// printf("epoll_wait infds=%d\n",infds);
// 返回失败。
if (infds < 0)
{
printf("epoll_wait() failed.\n"); perror("epoll_wait()"); break;
}
// 超时。
if (infds == 0)
{
printf("epoll_wait() timeout.\n"); continue;
}
// 遍历有事件发生的结构数组。
for (int ii=0;ii<infds;ii++)
{
if ((events[ii].data.fd == listensock) &&(events[ii].events & EPOLLIN))
{
// 如果发生事件的是listensock,表示有新的客户端连上来。
struct sockaddr_in client;
socklen_t len = sizeof(client);
int clientsock = accept(listensock,(struct sockaddr*)&client,&len);
if (clientsock < 0)
{
printf("accept() failed.\n"); continue;
}
// 把新的客户端添加到epoll中。
memset(&ev,0,sizeof(struct epoll_event));
ev.data.fd = clientsock;
ev.events = EPOLLIN;
epoll_ctl(epollfd,EPOLL_CTL_ADD,clientsock,&ev);
printf ("client(socket=%d) connected ok.\n",clientsock);
continue;
}
else if (events[ii].events & EPOLLIN)
{
// 客户端有数据过来或客户端的socket连接被断开。
char buffer[1024];
memset(buffer,0,sizeof(buffer));
// 读取客户端的数据。
ssize_t isize=read(events[ii].data.fd,buffer,sizeof(buffer));
// 发生了错误或socket被对方关闭。
if (isize <=0)
{
printf("client(eventfd=%d) disconnected.\n",events[ii].data.fd);
// 把已断开的客户端从epoll中删除。
memset(&ev,0,sizeof(struct epoll_event));
ev.events = EPOLLIN;
ev.data.fd = events[ii].data.fd;
epoll_ctl(epollfd,EPOLL_CTL_DEL,events[ii].data.fd,&ev);
close(events[ii].data.fd);
continue;
}
printf("recv(eventfd=%d,size=%d):%s\n",events[ii].data.fd,isize,buffer);
// 把收到的报文发回给客户端。
write(events[ii].data.fd,buffer,strlen(buffer));
}
}
}
close(epollfd);
return 0;
}
// 初始化服务端的监听端口。
int initserver(int port)
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if (sock < 0)
{
printf("socket() failed.\n"); return -1;
}
// Linux如下
int opt = 1; unsigned int len = sizeof(opt);
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,len);
setsockopt(sock,SOL_SOCKET,SO_KEEPALIVE,&opt,len);
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
if (bind(sock,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0 )
{
printf("bind() failed.\n"); close(sock); return -1;
}
if (listen(sock,5) != 0 )
{
printf("listen() failed.\n"); close(sock); return -1;
}
return sock;
}
// 把socket设置为非阻塞的方式。
int setnonblocking(int sockfd)
{
if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1) return -1;
return 0;
}