2012-10-22 42 views
-2

我在c中構建代理服務器,我試圖瞭解select()函數。我完成了代碼,以便從客戶端建立連接,然後提取Web地址,以便可以建立另一個連接以連接到實際的Web服務器。該頁面然後由代理接收並返回給客戶端。在代理服務器中選擇()

據我所知,select()將允許這處理多個客戶端請求,但我不明白它是如何幫助(或更確切地說如何實現)到Web服務器的第二個連接。根據我的理解,我將不再需要一個while循環來繼續從Web服務器接收數據並將其傳回客戶端。

我是否需要爲Web服務器連接設置第二個文件描述符?如果我正在處理兩個或更多客戶端請求,我如何確保它們正確連接到Web服務器的正確連接?無論如何,我將不勝感激任何幫助。我已經完成了網絡教程和其他一些在線的教程,但幾天之後,我仍然沒有把這個想法包括在內。

void error(const char *msg) 
{ 
    perror(msg); 
    exit(1); 
} 

void *get_in_addr(struct sockaddr *sa) 
{ 
    if (sa->sa_family == AF_INET) 
     return &(((struct sockaddr_in*)sa)->sin_addr); 

    return &(((struct sockaddr_in6*)sa)->sin6_addr); 
} 

string getHostString(const char *buf); 

int main(int argc, char *argv[]) 
{ 
    int sockfd, newsockfd, portno; 
    int fdmax; //maximum file descriptor number 
    socklen_t clilen; 
    char buffer[256]; 
    struct sockaddr_storage remoteaddr; //client address 
    char clientIP[INET6_ADDRSTRLEN]; 

    //fd_set readfds, writefds, exceptfds; 
    fd_set masterfds, readfds; 
    struct timeval timeout; 
    int rc; 

    /*Set time limit. */ 
    timeout.tv_sec = 3; 
    timeout.tv_usec = 0; 

    /*Create a descriptor set containing the sockets */ 
    FD_ZERO(&readfds); 
    FD_ZERO(&masterfds); 
    /*FD_ZERO(&writefds); 
    FD_ZERO(&exceptfds); 
    FD_SET(newsockfd, &readfds); 

    rc = select(sizeof(readfds)*8, &readfds, NULL, NULL, &timeout); 
    if (rc==-1){ 
     perror("select failed"); 
     return -1; 
    }*/ 

    struct sockaddr_in serv_addr, cli_addr; 
    int n; 
    if (argc < 2) { 
     fprintf(stderr,"ERROR, no port provided\n"); 
     exit(1); 
    } 

    sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    if (sockfd < 0) 
     error("ERROR opening socket"); 

    bzero((char *) &serv_addr, sizeof(serv_addr)); 
    portno = atoi(argv[1]); 
    serv_addr.sin_family = AF_INET; 
    serv_addr.sin_addr.s_addr = INADDR_ANY; 
    serv_addr.sin_port = htons(portno); 

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 
      error("ERROR on binding"); 

    if((listen(sockfd,5)) == -1) 
     error("Server-listen() error!!!"); 
    printf("Server-listen() is OK...\n"); 


    FD_SET(sockfd, &masterfds); 

    //keep track of the biggest file descriptor so far 
    fdmax = sockfd; //so far it's this one 

    for(;;){ 

     readfds = masterfds; 

     if(select(fdmax+1, &readfds, NULL, NULL, NULL) == -1){ 
      error("Server-select() error !"); 
     } 
     printf("Server-select() is OK...\n"); 

     for(int i = 0; i <= fdmax; i++){ 
      printf("i: %d sockfd %d\n", i, sockfd); 
      if(FD_ISSET(i, &readfds)){ 
       if(i == sockfd){ //sockfd is the listener 
        //following handles new connections 
        clilen = sizeof(cli_addr); 
        if((newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr, &clilen)) == -1) 
         error("Server-accept() error!!!"); 
        else{ 
         printf("Server-accept() is OK...\n"); 
         FD_SET(newsockfd, &masterfds); //add to master set 
         if(newsockfd > fdmax) 
          fdmax = newsockfd; 
         printf("selectserver: New connection from %s on " 
           "socket %d\n", 
           inet_ntop(remoteaddr.ss_family, 
            get_in_addr((struct sockaddr*)&cli_addr), 
            clientIP, INET6_ADDRSTRLEN), 
           newsockfd); 
        } 
       } 
       else{ 
        //handle data from a client 
        //Here is where the FIRST read occurs 
        bzero(buffer,256); 
        //n = read(newsockfd,buffer,255); 
        n = read(i,buffer,255); 
        if (n < 0) error("ERROR reading from socket"); 
        printf("Here is the message: \n\n%s\n\n",buffer); 

        //string hoststring(buffer); 
        string hoststring(getHostString(buffer)); 

        int html_port = 80; 
        int html_socket; 

        printf("Prior to struct addrinfo\n"); 

        struct addrinfo hints, *res; 

        memset(&hints, 0, sizeof hints); 
        hints.ai_family = AF_UNSPEC; 
        hints.ai_socktype = SOCK_STREAM; 
        hints.ai_flags = AI_PASSIVE; 

        printf("Prior to getaddrinfo()\n"); 

        char *address = new char[hoststring.size() +1]; 
        address[hoststring.size()] = 0; 
        memcpy(address, hoststring.c_str(), hoststring.size()); 

        //getaddrinfo(token, (char *)html_port, &hints, &res); 
        getaddrinfo(address, "80", &hints, &res); 

        printf("We are past getaddrinfo()\n"); 

        //if (html_socket = socket(PF_INET, SOCK_STREAM, 0) < 0){ 
        if ((html_socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0){ 
         printf("socket connection error\n"); 
        } 

        printf("We are past socket()\n"); 

        //char* address; 
        //address = new char[256]; 
        //strncpy(address, "www.cs.ucr.edu", sizeof("www.cs.ucr.edu")); 


        printf("address: %s\n", address);  
        struct hostent * host; 
        if ((host = gethostbyname(address)) == NULL){ 
         printf("Problem with gethostbyname()\n"); 
        } 

        printf("We are past gethostbyname() and about to connect.\n"); 

        if (connect(html_socket, res->ai_addr, res->ai_addrlen) < 0) 
         printf("Unsuccessful completion of connect()"); 

        int bytes_sent, bytes_recvd; 
        char recv_buff[1024]; 
        bytes_sent = send(html_socket, buffer, 256, 0); 
        cout << "bytes_sent: " << bytes_sent << endl; 

        //do{ 
         bytes_recvd = recv(html_socket, recv_buff, 1024, 0); 
         cout << "bytes_rcvd: " << bytes_recvd << endl; 
         cout << recv_buff << endl; 

         //FD_ZERO(&readfds); 
         //FD_SET(newsockfd, &writefds); 

         //n = write(newsockfd,"I got your message\n",20); 
         //n = write(newsockfd,recv_buff,bytes_recvd); 
         n = write(i,recv_buff,bytes_recvd); 
         if (n < 0) error("ERROR writing to socket"); 
         bzero(recv_buff, 1024); 
        //}while(bytes_recvd !=0); 
       } 
      } 
     } 

    } 

    close(newsockfd); 
    close(sockfd); 
    return 0; 
} 


string getHostString(const char *buf){ 
    string hoststring(buf); 
    hoststring = hoststring.substr(11); 

    cout << "hoststring: " << hoststring << endl; 
    int slashpos; 
    slashpos = hoststring.find("/"); 
    int suffixendpos = hoststring.find("H") - slashpos; 
    string suffix = hoststring.substr(slashpos, suffixendpos); 
    hoststring = hoststring.substr(0, slashpos); 

    cout << "hoststring: " << hoststring << endl; 
    cout << "suffix: " << suffix << endl; 

    return hoststring; 

} 

回答

0

你看了select(2)select_tut(2)手冊頁?

您是否閱讀過相關章節Advanced Linux ProgrammingAdvanced Unix Programming

事實上,因爲c10k問題和最大文件描述符的256(或1024)的限制,即到__FD_SETSIZE)時,系統調用select變得過時了,你應該使用poll(2)系統調用來代替。

應設置readfdsfor循環內,就在select調用之前,有明確FD_ZEROFD_SET(該fd_set類型可以是一個數組,所以你不能全部分配給它。)。系統調用select可以修改它。

不要忘記用gcc -Wall -g進行編譯並使用調試器。您還可以研究現有免費軟件HTTP客戶端庫或代理的源代碼。

1

我知道您正在嘗試編寫一個處理循環中多個連接的單個進程服務器,並使用select()來確定套接字描述符何時具有要讀取的數據。但有時另一種方法實際上更容易且更具可擴展性。

你有沒有考慮過使用一個multi-process服務器,在每個套接字連接完成之後,你需要fork()一個新的進程來處理請求?這消除了需要擔心select()如何工作和將請求映射到正確的套接字描述符。

查看Handle Multiple Connections here作爲一個合理的例子。

除了使用fork()之外,您還可以使用POSIX pthreads庫的線程,這可能會提高您的效率。這裏是a good multi-threaded tcp/ip server sample that uses pthreads.

+0

謝謝。是的,計劃是最終使用pthreads,我試圖保持簡單。我剛剛用select來實現它,現在我將嘗試修改它以使用pthread。 – user1354916