tcp 回射伺服器 客戶端非阻塞方式的實現
阿新 • • 發佈:2020-12-26
技術標籤:c++
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。