nonblock_select.cpp
/* tcpcli01.c */
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "common.h"
#include <algorithm>
#include <fcntl.h>
const int MAXLINE = 4096;
void
str_cli(FILE *fp, int sockfd)
{
int maxfdp1,val,stdineof;
ssize_t n,nwritten;
fd_set rset,wset;
char to[MAXLINE],fr[MAXLINE];
char *toiptr,*tooptr,*friptr,*froptr;
val = fcntl(sockfd,F_GETFL,0);
fcntl(sockfd,F_SETFL,val|O_NONBLOCK);
val = fcntl(STDIN_FILENO,F_GETFL,0);
fcntl(STDIN_FILENO,F_SETFL,val|O_NONBLOCK);
val = fcntl(STDOUT_FILENO,F_GETFL,0);
fcntl(STDOUT_FILENO,F_SETFL,val|O_NONBLOCK);
toiptr = tooptr = to;
friptr = froptr = fr;
stdineof = 0;
maxfdp1 = std::max(std::max(STDIN_FILENO,STDOUT_FILENO),sockfd) + 1;
for(;;){
FD_ZERO(&rset);
FD_ZERO(&wset);
if(stdineof == 0&& toiptr < &to[MAXLINE])
FD_SET(STDIN_FILENO,&rset);
if(friptr < &fr[MAXLINE])
FD_SET(sockfd,&rset);
if(tooptr != toiptr)
FD_SET(sockfd,&wset);
if(froptr != friptr)
FD_SET(STDOUT_FILENO,&wset);
select(maxfdp1,&rset,&wset,NULL,NULL);
if(FD_ISSET(STDIN_FILENO,&rset)){
printf("STDIN_FILENO can be read\n");
if((n = read(STDIN_FILENO,toiptr,&to[MAXLINE]-toiptr))<0)
{
if(errno != EWOULDBLOCK)
{
printf("read error on stdin\n");
exit(1);
}
}
else if(n == 0)
{
stdineof = 1;
if(tooptr == toiptr)
shutdown(sockfd,SHUT_WR);
}
else
{
printf("put send content into to buffer\n");
toiptr += n;
FD_SET(sockfd,&wset);
}
}
if(FD_ISSET(sockfd,&rset)){
printf("server sock can be read\n");
if((n = read(sockfd,friptr,&fr[MAXLINE]-friptr)) < 0)
{
if(errno != EWOULDBLOCK)
{
printf("read error on socket\n");
exit(1);
}
}
else if(n == 0)
{
if(stdineof)
return;
else
{
exit(1);
}
}
else
{
friptr += n;
FD_SET(STDOUT_FILENO,&wset);
}
}
if(FD_ISSET(STDOUT_FILENO,&wset) && ((n=friptr-froptr) >0 ))
{
if((nwritten = write(STDOUT_FILENO,froptr,n)) < 0)
{
if(errno != EWOULDBLOCK)
{
printf("write error to stdout\n");
exit(1);
}
}
else
{
printf("write to stdout success\n");
froptr += nwritten;
if(froptr == friptr)
{
froptr = friptr = fr;
}
}
}
if(FD_ISSET(sockfd,&wset) && ((n = toiptr -tooptr) > 0 ))
{
printf("server sock can be write\n");
if((nwritten = write(sockfd,tooptr,n)) < 0){
printf("write to server sock failed\n");
if(errno != EWOULDBLOCK)
{
printf("write error to socket\n");
exit(1);
}
}
else
{
printf("write to server sock successfully\n");
tooptr += nwritten;
if(tooptr == toiptr){
toiptr = tooptr = to;
if(stdineof)
shutdown(sockfd,SHUT_WR);
}
}
}
}
}
int
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if(argc != 2)
{
printf("usage: tcpcli <IPaddress> ");
exit(0);
}
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
exit(1);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9877);
if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) < 0)
{
perror("inet_pton");
exit(1);
}
if(connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
{
perror("connect");
exit(1);
}
str_cli(stdin, sockfd); /* do it all */
exit(0);
}
select 和 poll的是否阻塞和socket是否阻塞無關
select 和 poll 可以實現異步通知,內核告訴我哪些文件描述符可以寫可以讀,這個時候我們再去寫,再去讀。
阻塞的write就會影響其他的讀或寫,因爲它會盡力把所有內容寫入。
而非阻塞write則是能寫多少寫多少,直接返回,不影響其他的讀或寫。然後下一次再根據select的結果再去write。