2013-02-03 wcdj
1 UNIX下可用的五种I/O模型
(1) 阻塞式I/O
(2) 非阻塞式/O
(3) I/O复用(select和poll)
(4) 信号驱动式I/O(SIGIO)
(5) 异步I/O(POSIX的aio_系列函数)
2 I/O复用select
client代码
// client_v2.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#define SERV_PORT 30008
#define SA struct sockaddr
#define MAXLINE 4096
#define max(a, b) ( (a) > (b) ? (a) : (b) )
static int read_cnt;
static char *read_ptr;
static char read_buf[MAXLINE];
static ssize_t my_read(int fd, char *ptr)
{
if (read_cnt <= 0)
{
again:
if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0)
{
if (errno == EINTR)
goto again;
return -1;
}
else if (read_cnt == 0)
return 0;
read_ptr = read_buf;
}
--read_cnt;
*ptr = *read_ptr++;
return 1;
}
ssize_t readlinebuf(void **vptrptr)
{
if (read_cnt)
*vptrptr = read_ptr;
return read_cnt;
}
ssize_t readline(int fd, void *vptr, size_t maxlen)
{
ssize_t n, rc;
char c, *ptr;
ptr = (char *)vptr;
for (n = 1; n < maxlen; ++n)
{
if ( (rc = my_read(fd, &c)) == 1)
{
*ptr++ = c;
if (c == '\n')
break;// newline is stored, like fgets()
}
else if (rc == 0)
{
*ptr = 0;
return (n-1);// EOF, n - 1 bytes were read
}
else
return -1;// error, errno set by read()
}
*ptr = 0;
return (n);
}
ssize_t writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = (const char*)vptr;
nleft = n;
while (nleft > n)
{
if ( (nwritten = write(fd, ptr, nleft)) <= 0 )
{
if (nwritten < 0 && errno == EINTR)
nwritten = 0;// and call write() again
else
return -1;// error
}
nleft -= nwritten;
ptr += nwritten;
}
return n;
}
void str_cli(FILE *fp, int sockfd)
{
int maxfdp1;
int stdineof = 0;
fd_set rset;
char buf[MAXLINE];
int n;
FD_ZERO(&rset);
for ( ; ; )
{
if (stdineof == 0)
{
FD_SET(fileno(fp), &rset);
}
FD_SET(sockfd, &rset);
maxfdp1 = max(fileno(fp), sockfd) + 1;
select(maxfdp1, &rset, NULL, NULL, NULL);
// socket is readable
if (FD_ISSET(sockfd, &rset))
{
printf("socket is readable\n");
if ( (n = read(sockfd, buf, MAXLINE)) == 0 )
{
if (stdineof == 1)
{
printf("normal tremination\n");
return;// normal termination
}
else
{
printf("str_cli: server terminated prematurely\n");
exit(1);
}
}
write(fileno(stdout), buf, n);
}
// input is readable
if (FD_ISSET(fileno(fp), &rset))
{
printf("input is readable\n");
if ( (n = read(fileno(fp), buf, MAXLINE)) == 0 )
{
printf("input read 0\n");
stdineof = 1;
shutdown(sockfd, SHUT_WR);// send FIN
FD_CLR(fileno(fp), &rset);// turn off the bit for fileno(fp) in rset
continue;
}
buf[n] = '\0';
printf("input read:%s", buf);
//int iret = writen(sockfd, buf, n);
int iret = send(sockfd, buf, n, 0);
printf("socket send:%d\n", iret);
}
}
}
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if (argc != 2)
{
printf("usage: tcpcli <IPaddress>\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&servaddr, 0x0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
if (-1 == connect(sockfd, (SA *)&servaddr, sizeof(servaddr)))
{
printf("connect err:%s\n", strerror(errno));
exit(1);
}
str_cli(stdin, sockfd);
exit(0);
}
server代码
// server_v2.cpp
// 使用单进程和select的TCP服务器
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#define SERV_PORT 30008
#define SA struct sockaddr
#define MAXLINE 4096
#define max(a, b) ( (a) > (b) ? (a) : (b) )
#define LISTENQ 1024
ssize_t writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = (const char*)vptr;
nleft = n;
while (nleft > n)
{
if ( (nwritten = write(fd, ptr, nleft)) <= 0 )
{
if (nwritten < 0 && errno == EINTR)
nwritten = 0;// and call write() again
else
return -1;// error
}
nleft -= nwritten;
ptr += nwritten;
}
return n;
}
int
main(int argc, char **argv)
{
int i;
int maxi;
int maxfd;
int listenfd;
int connfd;
int sockfd;
int nready, client[FD_SETSIZE];
ssize_t n;
fd_set rset, allset;
char buf[MAXLINE];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&servaddr, 0x0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
bind(listenfd, (SA *)&servaddr, sizeof(servaddr));
listen(listenfd, LISTENQ);
maxfd = listenfd; // initialize
maxi = -1; // index into client[] array
for (i = 0; i < FD_SETSIZE; ++i)
{
client[i] = -1;// -1 indicates available entry
}
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
for ( ; ; )
{
rset = allset;// structure assignment
nready = select(maxfd+1, &rset, NULL, NULL, NULL);
printf("select ready:%d\n", nready);
// new client connection
if (FD_ISSET(listenfd, &rset))
{
printf("new client connection\n");
clilen = sizeof(cliaddr);
connfd = accept(listenfd, (SA *)&cliaddr, &clilen);
for(i = 0; i < FD_SETSIZE; ++i)
{
if (client[i] < 0)
{
client[i] = connfd;// save descriptor
break;
}
}
if (i == FD_SETSIZE)
{
printf("too many clients\n");
exit(1);
}
FD_SET(connfd, &allset);// add new descriptor to set
if (connfd > maxfd)
{
maxfd = connfd;// for select
}
if (i > maxi)
{
maxi = i;// max index in client[] array
}
if (--nready <= 0)
{
printf("listen, no more readable descriptors\n");
continue;// no more readable descriptors
}
}
// check all clients for data
printf("check all clients for data\n");
for (i = 0; i<= maxi; ++i)
{
if ( (sockfd = client[i]) < 0 )
{
continue;
}
if (FD_ISSET(sockfd, &rset))
{
if ( (n = read(sockfd, buf, MAXLINE)) == 0 )
{
// connection closed by client
printf("connection closed by client\n");
close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -1;
}
else
{
buf[n] = '\0';
printf("get info:%s", buf);
writen(sockfd, buf, n);
}
if (--nready <= 0)
break;// no more readable descriptors
}
}
}
}