2012-11-28 55 views
1

我有這段代碼,我正在嘗試將它用作我的項目的一部分。該項目的細節現在並不重要,但我試圖做的是使用此端口轉發代理作爲瀏覽器和本地http服務器之間的代理。C:epoll端口轉發代理不會轉發所有數據

所以,如果我在我的瀏覽器中鍵入http://127.0.0.1:8999/我想從127.0.0.1:8888返回網頁。這僅適用於小型網頁(無圖像,小型html文件,...)。當我嘗試在具有少量圖片的網頁上執行此操作時,它們不會被傳輸或僅部分傳輸。

我也用telnet和一個網站測試了一個大html文件。我已連接到代理併發送HEAD方法請求。正如預期的那樣,我獲得了所有的元數據。當我嘗試GET方法時,我只獲得了網頁的部分html文件。

任何人都可以指向正確的方向嗎?我真的不確定我做錯了什麼。

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netdb.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <sys/epoll.h> 
#include <errno.h> 
#include <signal.h> 
#include <assert.h> 

#define FALSE  0 
#define TRUE  1 
#define EPOLL_QUEUE_LEN 256 
#define SERVER_PORT 7000 
#define LISTEN_PORT 8667  // Proxy Server Listens for this port 
#define FORWARD_PORT 8888  // Proxy Server forwards all port LISTEN_PORT data to this port 
#define BUFLEN 1024 
//Globals 
int fd_server; 
int sent = 0; // for how many requests were processed to client 
int forwardSockets[EPOLL_QUEUE_LEN]; 
int internalSockets[EPOLL_QUEUE_LEN]; 
// Function prototypes 
static void SystemFatal (const char* message); 
static int forwardData (int fd); 
void close_fd (int); 

// This is the main function which handles the epoll loop and 
// accepts connections 
// Also handles the data processing back to the client. 
int main (int argc, char* argv[]) 
{ 
    int i, arg, src_port, forwardSD, dest_port; 
    int num_fds, fd_new, epoll_fd; 
    int linenum=0; 
    static struct epoll_event events[EPOLL_QUEUE_LEN], event; 
    struct sockaddr_in addr, remote_addr; 
    socklen_t addr_size = sizeof(struct sockaddr_in); 
    struct sigaction act; 
    struct hostent *hp; 
    struct sockaddr_in server_fwd; 
    char *host; 
     char line[256], ip[256]; 
    FILE *fp; 

    fp=fopen("port_forward_config", "r"); 
    //src_port = LISTEN_PORT;   // Use the default listen port 


     while(fgets(line, 256, fp) != NULL) 
     { 
       linenum++; 
       if(line[0] == '#') continue; 

       sscanf(line, "%s %d %d", &ip, &src_port, &dest_port); 
     { 
        fprintf(stderr, "Syntax error, line %d\n", linenum); 
       continue; 
     } 
    } 
    printf("Reading Config File...\n"); 
    printf("IP %s LPORT %d DPORT %d\n", ip, src_port, dest_port); 
    host = ip; 

    // set up the signal handler to close the server socket when CTRL-c is received 
    act.sa_handler = close_fd; 
    act.sa_flags = 0; 
    if ((sigemptyset (&act.sa_mask) == -1 || sigaction (SIGINT, &act, NULL) == -1)) 
    { 
      perror ("Failed to set SIGINT handler"); 
      exit (EXIT_FAILURE); 
    } 
//-------------------------------------------------------------------------------------------- 
    // Create the listening socket 
    fd_server = socket (AF_INET, SOCK_STREAM, 0); 
    if (fd_server == -1) 
    { 
     SystemFatal("socket"); 
    } 
    // set SO_REUSEADDR so port can be resused imemediately after exit, i.e., after CTRL-c 
    arg = 1; 
    if (setsockopt (fd_server, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) == -1) 
    { 
     SystemFatal("setsockopt"); 
    } 
    // Make the server listening socket non-blocking 
    if (fcntl (fd_server, F_SETFL, O_NONBLOCK | fcntl (fd_server, F_GETFL, 0)) == -1) 
    { 
     SystemFatal("fcntl"); 
    } 
    // Bind to the specified listening port 
    memset (&addr, 0, sizeof (struct sockaddr_in)); 
    addr.sin_family = AF_INET; 
    addr.sin_addr.s_addr = htonl(INADDR_ANY); 
    addr.sin_port = htons(src_port); 
    if (bind (fd_server, (struct sockaddr*) &addr, sizeof(addr)) == -1) 
    { 
     SystemFatal("bind"); 
    } 
    // Listen for fd_news; SOMAXCONN is 128 by default 
    if (listen (fd_server, SOMAXCONN) == -1) 
    { 
     SystemFatal("listen"); 
    } 
//--------------------------------------------------------------------------------------------- 
    // Create the epoll file descriptor 
    epoll_fd = epoll_create(EPOLL_QUEUE_LEN); 
    if (epoll_fd == -1) 
    { 
     SystemFatal("epoll_create"); 
    } 
    // Add the server socket to the epoll event loop 
    event.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLET; 
    event.data.fd = fd_server; 
    if (epoll_ctl (epoll_fd, EPOLL_CTL_ADD, fd_server, &event) == -1) 
    { 
     SystemFatal("epoll_ctl"); 
    } 
    // Execute the epoll event loop 
    while (TRUE) 
    { 
     //struct epoll_event events[MAX_EVENTS]; 
     num_fds = epoll_wait (epoll_fd, events, EPOLL_QUEUE_LEN, -1); 
     if (num_fds < 0) 
     { 
      SystemFatal ("Error in epoll_wait!"); 
     } 
     for (i = 0; i < num_fds; i++) 
     { 
       // Case 1: Error condition 
       if (events[i].events & (EPOLLHUP | EPOLLERR)) 
      { 
        fputs("epoll: EPOLLERR", stderr); 
        //close(events[i].data.fd); 
        continue; 
       } 
       assert (events[i].events & EPOLLIN); 
//----------------------------------------------------------------------------------------- 
       // Case 2: Server is receiving a connection request 
       if (events[i].data.fd == fd_server) 
      { 
       //socklen_t addr_size = sizeof(remote_addr); 
       fd_new = accept (fd_server, (struct sockaddr*) &remote_addr, &addr_size); 
       if (fd_new == -1) 
       { 
         if (errno != EAGAIN && errno != EWOULDBLOCK) 
        { 
         perror("accept"); 
         } 
         continue; 
       } 
//------------------------------------------------------------------------------------------------ 
       // Make the fd_new non-blocking 
       if (fcntl (fd_new, F_SETFL, O_NONBLOCK | fcntl(fd_new, F_GETFL, 0)) == -1) 
       { 
        SystemFatal("fcntl"); 
       } 

       // Add the new socket descriptor to the epoll loop 
       event.data.fd = fd_new; 
       if (epoll_ctl (epoll_fd, EPOLL_CTL_ADD, fd_new, &event) == -1) 
       { 
        SystemFatal ("epoll_ctl"); 
       } 
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
       printf(" Remote Address: %s\n", inet_ntoa(remote_addr.sin_addr)); 
       //close(fd_new); 
       dest_port = FORWARD_PORT;  // Used the default forward port 
       // create internal connection 
       printf("Trying to create forward socket\n");   
       if ((forwardSD = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 
        perror("Cannot create forward socket."); 
        exit(1); 
       } 

       printf("Binding...\n"); 
       bzero((char *)&server_fwd, sizeof(struct sockaddr_in)); 
       server_fwd.sin_family = AF_INET; 
       server_fwd.sin_port = htons(dest_port); 
       //host = "192.168.0.10";  
       if ((hp = gethostbyname(host)) == NULL) { 
        printf("Failed to get host name"); 
       } 

       bcopy(hp->h_addr, (char *)&server_fwd.sin_addr, hp->h_length); 

       printf("Connecting to interal machine.\n"); 
       printf("Server Forward Port: %d\n", ntohs(server_fwd.sin_port)); 
       printf("Server Forward IP: %s\n", inet_ntoa(server_fwd.sin_addr)); 

       // Connecting to interal machine 
       if (connect (forwardSD, (struct sockaddr *)&server_fwd, sizeof(server_fwd)) == -1) { 
        perror("connect failed"); 
        exit(1); 
       } 

       // Add the new socket descriptor to the epoll loop 
       event.data.fd = forwardSD; 
       if (epoll_ctl (epoll_fd, EPOLL_CTL_ADD, forwardSD, &event) == -1) 
       { 
        SystemFatal ("epoll_ctl"); 
       } 
       printf ("Connected: Server: %s\n", hp->h_name); 
       forwardSockets[fd_new] = forwardSD; 
       internalSockets[forwardSD] = fd_new; 
      // end internal connection 
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
       continue; 
       } 
       // Case 3: One of the sockets has read data 
       if (!forwardData(events[i].data.fd)) 
      { 
       // epoll will remove the fd from its set 
       // automatically when the fd is closed 
       close (events[i].data.fd);  
      } 
     } 
    } 
    close(fd_server); 
    exit (EXIT_SUCCESS); 
} 

/* 
    This function clears a socket if it has data waiting to be processed. 
    It takes the string sent from the client and parses it. After it has 
    done that it will send back to the client the amount of data it requested 
    for however many requests it defined as well. 
*/ 
static int forwardData (int fd) 
{ 
    int n, bytes_to_read; 
    char *bp, buf[BUFLEN]; 
    int  forwardData; 
    printf ("Forwarding :\n"); 

    //check if internal or external connection to send data back to. 
    if(forwardSockets[fd] != 0){ 
     forwardData = forwardSockets[fd]; 
    } 
    if(internalSockets[fd] != 0){ 
     forwardData = internalSockets[fd]; 
    } 

    bp = buf; 
     bytes_to_read = BUFLEN; 
     while ((n = recv (fd, bp, bytes_to_read, 0)) > 0) { 
       bp += n; 
       bytes_to_read -= n; 
     send (forwardData, buf, n, 0); 
     return TRUE; 
     } 
    return TRUE; 

} 

// Prints the error stored in errno and aborts the program. 
static void SystemFatal(const char* message) 
{ 
    perror (message); 
    exit (EXIT_FAILURE); 
} 
// close fd 
void close_fd (int signo) 
{ 
     close(fd_server); 
    exit (EXIT_SUCCESS); 
} 
+2

'send(forwardData,buf,n,0);'您應該測試返回值,並可能循環,並可能在發送緩衝區時返回-1/EWOULDBLOCK – wildplasser

回答

2

好像你正在使用的EPOLLET(邊沿觸發)標誌在所有epoll的事件。此標誌將導致epoll僅在文件描述符從不可用變爲可用時返回事件。

此外,在您的forwardData函數中,您收到BUFLEN字節,然後返回TRUE。如果有更多的字節可用,您將永遠不會轉發它們,因爲您的事件是邊緣觸發的並且不會被重新發送。

嘗試修改forwardData(也許刪除循環中的return TRUE),以便在返回之前轉發所有可讀數據。

+0

謝謝。我更改了forwardData函數,以便讀取直到讀取所有數據。它現在有效。 :) – jureso