1. 程式人生 > 其它 >tcp 回射伺服器 客戶端非阻塞方式的實現

tcp 回射伺服器 客戶端非阻塞方式的實現

技術標籤: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。