I/O复用:一种预先告知内核的能力,使得内核一旦发现进程指定的一个或多个I/O条件就绪,它就通知进程。
同步I/O:导致请求的进程阻塞,直到I/O操作完成。
异步I/O:不导致请求进程阻塞。
I/O复用模型(select、poll):
5种I/O模型比较:
/* According to POSIX.1-2001, POSIX.1-2008 */
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
#include <sys/select.h>
int pselect(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, const struct timespec *timeout,
const sigset_t *sigmask);
/* Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
pselect(): _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600
*/
select客户端:
#include "../Gnet.h"
void do_client(int connfd)
{
char buf[MAX_LINE];
int maxfd;
fd_set rset;
int n;
int closewr;
closewr = 0;
while(1)
{
FD_ZERO(&rset);
if(closewr == 0)
FD_SET(STDIN_FILENO, &rset);
FD_SET(connfd, &rset);
maxfd = STDIN_FILENO > connfd ? STDIN_FILENO : connfd;
if(select(maxfd+1, &rset, NULL, NULL, NULL) < 0)
perr_exit("select error.");
if(FD_ISSET(connfd, &rset))
{
if((n = Read(connfd, buf, MAX_LINE)) == 0)
{
if(closewr == 1)
return;
else
perr_exit("server terminated.");
}
Write(STDOUT_FILENO, buf, n);
}
if(FD_ISSET(STDIN_FILENO, &rset))
{
if((n = Read(STDIN_FILENO, buf, MAX_LINE)) == 0)
{
shutdown(connfd, SHUT_WR);
FD_CLR(STDIN_FILENO, &rset);
closewr = 1;
}
else
Write(connfd, buf, n);
}
}
}
int main(int argc, const char* argv[])
{
int connfd;
struct sockaddr_in server_addr;
if(argc < 2)
perr_exit("usage : client <IPaddress>");
connfd = Socket(AF_INET, SOCK_STREAM, 0);
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
inet_pton(AF_INET, argv[1], &server_addr.sin_addr);
Connect(connfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
do_client(connfd);
return 0;
}
select服务器:
#include "../Gnet.h"
int main(int argc, const char* argv[])
{
int lfd, connfd;
int maxfd;
int client[FD_SETSIZE], maxi, findi;
int nready;
int nread;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len;
fd_set allset, rset;
char buf[MAX_LINE];
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(SERVER_PORT);
lfd = Socket(AF_INET, SOCK_STREAM, 0);
Bind(lfd, (const struct sockaddr*)&server_addr, sizeof(server_addr));
Listen(lfd, LISTENQ);
maxfd = lfd;
maxi = -1;
for(int i=0; i<FD_SETSIZE; ++i)
client[i] = -1;
FD_ZERO(&allset);
FD_SET(lfd, &allset);
printf("waiting for connecting.\n");
while(1)
{
rset = allset;
nready = select(maxfd+1, &rset, NULL, NULL, NULL);
if(nready < 0)
perr_exit("select error.");
if(FD_ISSET(lfd, &rset))
{
client_addr_len = sizeof(client_addr);
connfd = Accept(lfd, (struct sockaddr*)&client_addr, &client_addr_len);
for(findi = 0; findi < FD_SETSIZE; ++findi)
{
if(client[findi] < 0)
{
client[findi] = connfd;
break;
}
}
if(findi == FD_SETSIZE)
Close(connfd);
else
{
FD_SET(connfd, &allset);
if(connfd > maxfd)
maxfd = connfd;
if(findi > maxi)
maxi = findi;
if(--nready <= 0)
continue;
}
}
for(int i=0; i <= maxi; ++i)
{
if(client[i] < 0)
continue;
if(FD_ISSET(client[i], &rset))
{
if((nread = Read(client[i], buf, MAX_LINE)) == 0)
{
Close(client[i]);
FD_CLR(client[i], &allset);
client[i] = -1;
}
else
Write(client[i], buf, nread);
if(--nready <= 0)
break;
}
}
}
return 0;
}
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <signal.h>
#include <poll.h>
int ppoll(struct pollfd *fds, nfds_t nfds,
const struct timespec *tmo_p, const sigset_t *sigmask);
poll服务器:
#include "../Gnet.h"
int main(int argc, const char* argv[])
{
int lfd, connfd;
int maxi, findi;
int nready;
int nread;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len;
char buf[MAX_LINE];
struct pollfd client[OPEN_MAX];
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(SERVER_PORT);
lfd = Socket(AF_INET, SOCK_STREAM, 0);
Bind(lfd, (const struct sockaddr*)&server_addr, sizeof(server_addr));
Listen(lfd, LISTENQ);
client[0].fd = lfd;
client[0].events = POLLRDNORM;
for(int i=1; i<OPEN_MAX; ++i)
client[i].fd = -1;
maxi = 0;
printf("waiting for connecting.\n");
while(1)
{
nready = poll(client, maxi+1, -1);
if(nready < 0)
perr_exit("poll error.");
if(client[0].revents & POLLRDNORM)
{
client_addr_len = sizeof(client_addr);
connfd = Accept(lfd, (struct sockaddr*)&client_addr, &client_addr_len);
for(findi=1; findi<OPEN_MAX; ++findi)
{
if(client[findi].fd < 0)
{
client[findi].fd = connfd;
break;
}
}
if(findi == OPEN_MAX)
Close(connfd);
else
{
if(findi > maxi)
maxi = findi;
client[findi].events = POLLRDNORM;
if(--nready <= 0)
continue;
}
}
for(int i =1; i <= maxi; ++i)
{
if(client[i].fd < 0)
continue;
if(client[i].revents & (POLLRDNORM | POLLERR))
{
if((nread = Read(client[i].fd, buf, MAX_LINE)) < 0)
{
client[i].fd = -1;
printf("read error.");
}
else if(nread == 0)
{
Close(client[i].fd);
client[i].fd = -1;
}
else
Write(client[i].fd, buf, nread);
if(--nready <= 0)
break;
}
}
}
return 0;
}
github:https://github.com/gongluck/CodeBase/tree/master/notes/unpv13-notes