2012-04-04 76 views
-1

我想用C程序映射兩個端口(任何事物都轉到端口x轉到端口y,反之亦然),我寫了這個程序,但它不起作用。 這是我的代碼:如何在Linux中使用C程序映射兩個端口

int recv_all_nonblock(int sockfd,char* buff,int buffersize) 
{ 
    int numbytes; 
    if ((numbytes = recv(sockfd, buff, buffersize-1, MSG_NOSIGNAL|MSG_DONTWAIT)) <= 0) 
    { 
    perror("recv"); 
    } 
    else if(numbytes>0) 
    { 
    buff[numbytes] = '\0'; 
    } 

    return numbytes; 
} 


int sendall(int sockfd, char *buf, int *len) 
{ 
    int total = 0;  // how many bytes we've sent 
    int bytesleft = *len; // how many we have left to send 
    int n=0; 
    while(total < *len) 
    { 
    n = send(sockfd, buf+total, bytesleft, MSG_NOSIGNAL|MSG_DONTWAIT); 
    if (n == -1) { break; } 
    total += n; 
    bytesleft -= n; 
    } 

    *len = total; // return number actually sent here 

    return n==-1?-1:0; // return -1 on failure, 0 on success 
} 


int Connect_To_Remote(char *Addr,char* PORT) 
{ 
    int sockfd; 
    struct addrinfo hints, *servinfo=NULL, *p=NULL; 
    int rv; 
    char s[INET6_ADDRSTRLEN]; 



    memset(&hints, 0, sizeof hints); 
    hints.ai_family = AF_UNSPEC; 
    hints.ai_socktype = SOCK_STREAM; 
    while (p==NULL) 
    { 
    sleep(5); 
    while((rv = getaddrinfo(Addr, PORT, &hints, &servinfo)) != 0) 
    { 
     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); 
     sleep(5); 
    } 

    for(p = servinfo; p != NULL; p = p->ai_next) 
    { 
     if ((sockfd = socket(p->ai_family, p->ai_socktype,p->ai_protocol)) == -1) 
     { 
     perror("client: socket"); 
     continue; 
     } 

     if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) 
     { 
     close(sockfd); 
     perror("client: connect"); 
     continue; 
     } 

     break; 
    } 

    if (p == NULL) 
    { 
     fprintf(stderr, "client: failed to connect\n"); 
    } 
    } 

    inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),s, sizeof s); 
    printf("client: connecting to %s\n", s); 

    freeaddrinfo(servinfo); // all done with this structure 
    return sockfd; 

} 
int recv_all(int sockfd,char* buff,int buffersize) 
{ 
    int numbytes; 
    fd_set readfd_set; 
    struct timeval recvwait; 

    recvwait.tv_sec=10; 
    recvwait.tv_usec=0; 
    FD_ZERO(&readfd_set); 
    FD_SET(sockfd, &readfd_set); 
    if(select(sockfd+1, &readfd_set, NULL, NULL, &recvwait) <= 0) 
    { 
    perror("wait for recieve error:"); 
    } 
    else 
    { 
    if ((numbytes = recv(sockfd, buff, buffersize-1, MSG_NOSIGNAL|MSG_DONTWAIT)) == -1) 
    { 
     perror("recv"); 
    } 
    else if(numbytes>0) 
    { 
     buff[numbytes] = '\0'; 
    } 

    return numbytes; 
    } 

    return 0; 
} 

void *Port_Mapper() 
{ 
    int sockfd,newfd; // listen on sock_fd, new connection on new_fd 
    struct addrinfo hints, *servinfo, *tmpaddrinfo; 
    struct sockaddr_storage their_addr; // connector's address information 
    socklen_t sin_size; 
    int rv; 
    int yes=1; 
    int ret=0; 

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

    while (1) 
    { 
    if ((rv = getaddrinfo(NULL, MapPort, &hints, &servinfo)) != 0) 
    { 
     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); 
     sleep(2); 
    } 
    else 
    { 
     break; 
    } 
    } 

    while (1) 
    { 
    for(tmpaddrinfo = servinfo; tmpaddrinfo != NULL; tmpaddrinfo = tmpaddrinfo->ai_next) 
    { 
     if ((sockfd = socket(tmpaddrinfo->ai_family,tmpaddrinfo->ai_socktype,tmpaddrinfo->ai_protocol)) == -1) 
     { 
     perror("server: socket"); 
     continue; 
     } 
     if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,sizeof(int)) == -1) 
     { 
      perror("setsockopt"); 
      exit(1); 
     } 
     if (bind(sockfd, tmpaddrinfo->ai_addr, tmpaddrinfo->ai_addrlen) == -1) { 
     close(sockfd); 
     perror("server: bind"); 
     continue; 
     } 

     break; 
    } 
    if (tmpaddrinfo == NULL) 
    { 
     fprintf(stderr, "server: failed to bind\n"); 
     sleep(1); 
    } 
    else 
    { 
     break; 
    } 
    } 
    freeaddrinfo(servinfo); // all done with this structure 
    if (listen(sockfd, MAXLISTENQ) == -1) 
    { 
    perror("listen"); 
    exit(1); 
    } 
    int bufpoint=0; 
    printf("server: waiting for connections...\n"); 
    while(1) 
    { // main accept() loop 
    char buff[MAX_SOCK_BUFFER]; 
    char buff2[MAX_SOCK_BUFFER]; 
    ret=0; 
    int sockfdweb=0; 
    sin_size = sizeof (their_addr); 
    printf("mapping server: going to accept connections...\n"); 
    newfd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); 
    printf("\n\nmapping server: connections accepted:%d\n",newfd); 

    sockfdweb=Connect_To_Remote("192.168.1.10","80"); 
    if (sockfdweb<0) 
    { 
     printf("can not connect to %s\n","192.168.1.10"); 
     break; 
    } 
     while(1) 
    { 



     memset(buff,0,sizeof(buff)); 
     ret=recv_all_nonblock(newfd,buff,MAX_SOCK_BUFFER); 
printf("recved from client1,%d\n",ret); 
     if (ret<=0) 
     { 
     perror("recieve error from browser:"); 
     close(newfd); 
     sin_size = sizeof (their_addr); 
     printf("mapping server: going to accept connections...\n"); 
     newfd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); 
      ret=recv_all_nonblock(newfd,buff,MAX_SOCK_BUFFER); 
    printf("recved from client10,%d\n",ret); 
     if (ret<=0) 
     { 
      perror("recieve error from browser:"); 
      close(newfd); 
      sin_size = sizeof (their_addr); 
      printf("mapping server: going to accept connections...\n"); 
      newfd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); 
     } 
     else if(ret>0) 
     { 
    printf("recved from client0\n"); 
    printf("%s\n",buff); 
      if (sendall(sockfdweb,buff,&bufpoint)<0) 
      { 
      printf("can not send data to %s\n","192.168.1.10"); 
      } 
    printf("send to 80,0\n"); 

     } 

     } 
     else if(ret>0) 
     { 
printf("recved from client\n"); 
    printf("%s\n",buff); 
     if (sendall(sockfdweb,buff,&bufpoint)<0) 
     { 
      printf("can not send data to %s\n","192.168.1.10"); 
     } 
    printf("send to 80\n"); 

     } 

     memset(buff2,0,sizeof(buff2)); 
     ret=recv_all_nonblock(sockfdweb,buff2,MAX_SOCK_BUFFER); 
printf("recv from 80...%d\n",ret); 
     if (ret<=0) 
     { 
     close(sockfdweb); 
     sockfdweb=Connect_To_Remote("192.168.1.10","80"); 
     if (sockfdweb<0) 
     { 
      printf("can not connect to %s\n","192.168.1.10"); 
      break; 
     } 
     ret=recv_all_nonblock(sockfdweb,buff2,MAX_SOCK_BUFFER); 
    printf("recv from 80.9..%d\n",ret); 
     if (ret<=0) 
     { 
      close(sockfdweb); 
      sockfdweb=Connect_To_Remote("192.168.1.10","80"); 
      if (sockfdweb<0) 
      { 
      printf("can not connect to %s\n","192.168.1.10"); 
      break; 
      } 

     } 
     else if (ret>0) 
     { 
    printf("recved from 809\n"); 
      if (sendall(newfd,buff2,&ret)<0) 
      { 
      printf("can not send data to %s\n","192.168.1.10"); 
      } 
    printf("send to client9\n"); 

     } 

     } 
     else if (ret>0) 
     { 
printf("recved from 80\n"); 
     if (sendall(newfd,buff2,&ret)<0) 
     { 
      printf("can not send data to %s\n","192.168.1.10"); 
     } 
printf("send to client\n"); 

     } 


    } 

    } 
    return 0; 
} 

int main() 
{ 
. 
. 
. 
. 
    pthread_attr_init (&attr); 
    pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); 
    pthread_create(&portmapper_threadid,&attr,Port_Mapper,NULL); 
    pthread_attr_destroy (&attr); 
. 
. 
. 
. 
} 

我的推杆在Port_Mapper功能有一定的「printf」,並做了一些改動,以跟蹤,當我在我的瀏覽器請求的web這樣會發生什麼:

http://127.0.0.1:8090/

,並把瀏覽器的清爽兩個三次節目出來是這樣的:

映射服務器:連接接受:5
客戶端:連接到192.168.1.10
從client1,360
recved從客戶recved
GET/1/HTTP/1.1
主機:127.0.0.1:8090
用戶代理:Mozilla/5.0(X11; Linux i686; rv:6.0.2)Gecko/20100101 Firefox/6.0.2
Accept:text/html,application/xhtml + xml,application/xml; q = 0.9,/; q = 0.8
Accept-Language:en -us,連接; q = 0.5
接受編碼:gzip,放氣
接收字符集:ISO-8859-1,utf-8; q = 0.7,*; q = 0.7
連接:保活
緩存控制:最大年齡= 0

發送到80
的recv:資源暫時不可
的recv從80 ...- 1
客戶端:連接到192.168.1.10
的recv:資源暫時不可
從80.9 ..- 1個
客戶端的recv:連接到192.168.1.10
的recv:資源暫時不可
從recved客戶端1,-1
從瀏覽器收到錯誤::資源暫時不可用
映射服務器:要接受連接...
從client10,360
從client0
recved recvedGET/1/HTTP/1.1
Host:127.0.0.1:8090
User-Agent:Mozilla/5.0(X11; Linux i686; rv:6.0.2)Gecko/20100101 Firefox/6.0.2
Accept:text/html,application/xhtml + xml,application/xml; q = 0.9,/; q = 0.8
Accept-Language:en -us,en; q = 0.5
Accept-Encoding:gzip,deflate
Accept-Charset:ISO-8859-1,utf-8; q = 0.7,*; q = 0。7
連接:保持活躍
緩存控制:最大年齡= 0

發送到80,0
的recv:資源暫時不可用80
的recv ... 0
客戶端:連接到192.168.1.10
的recv:資源暫時不可從80.9 ..- 1
客戶
的recv:連接到192.168.1.10
的recv:資源暫時不可用
從客戶端1 recved,-1
從瀏覽器收到錯誤::資源暫時不可用
映射服務器:要接受連接...
的recv:從資源暫時不可用
從client10 recved,-1
收到錯誤瀏覽器::資源暫時不可用
映射服務器:要接受連接...
的recv:資源暫時不可用80
的recv ... 0
客戶端:連接到192.168.1.10
的recv:從80.9 ..- 1
客戶資源暫時不可
的recv:連接到192.168.1.10
從client1,332
recved從客戶recved
GET/HTTP/1.1
主機:127.0.0.1 :8090
User-Agent:Mozilla/5.0(X11; Linux i686; RV:6.0.2)壁虎/ 20100101火狐/ 6.0.2
接受:text/html的,應用/ XHTML + xml的,應用/ XML; Q = 0.9,/; Q = 0.8
接受語言:EN -us,連接; q = 0.5
接受編碼:gzip,放氣
接收字符集:ISO-8859-1,utf-8; q = 0.7,*; q = 0.7
連接:保活

發送到80
的recv:資源暫時不可
的recv從80 ...- 1
客戶端:連接爲192.168.1.10
的recv:資源暫時不可
從80.9 ..- 1個
客戶端的recv:連接到192.168.1.10
的recv:資源暫時不可
從客戶端1 recved,-1
從瀏覽器收到錯誤::資源暫時不可用
映射服務器:正在接受連接...

,你可以看到程序從瀏覽器正確地接收數據,但是當它發送到web服務器的數據,並希望接收來自Web服務器的數據總是得到錯誤,並且沒有收到來自Web服務器的任何數據:

發送到80
的recv:資源暫時不可用
的recv從80 ...- 1

我不得不說,網絡服務器工作正常 沒問題。
任何機構可以告訴我什麼是我的問題?

+1

你能縮短你的代碼嗎?這是通過瀏覽很多方法。見http://sscce.org/ – 2012-04-04 11:17:40

回答

0

對於如此簡單的任務,您的代碼非常複雜。它可以簡化引用了很多,看到這個(僞)代碼:[其餘的我留下作爲一個練習]

void mapper() 
{ 
    int server_socket = create_server_socket(); 

    for (;;) 
    { 
     int incomming_socket = accept(server_socket); 

     int web_server_socket = connect_to_webserver(); 

     make_socket_nonblocking(incomming_socket); 
     make_socket_nonblocking(web_server_socket); 

     for (;;) 
     { 
      int disconnected = 0; 

      FD_ZERO(&read_set); 

      FD_SET(incomming_socket, &read_set); 
      FD_SET(web_server_socket, &read_set); 

      select(max_socket + 1, &read_set, NULL, NULL, NULL); 

      if (FD_ISSET(incomming_socket, &read_set)) 
      { 
       disonnected = recv_and_send(incomming_socket, web_server_socket); 
      } 
      if (!disconnected && FD_ISSET(web_server_socket, &read_set)) 
      { 
       disconnected = recv_and_send(web_server_socket, incomming_socket); 
      } 

      if (disconnected) 
       break; 
     } 

     close(web_server_socket); 
     close(incomming_socket); 
    } 
} 

int recv_and_send(from_socket, to_socket) 
{ 
    char buffer[1024]; 

    for (;;) 
    { 
     len = recv_all(from_socket, buffer, sizeof(buffer)); 
     if (len < 0) 
      return 1; /* Disconnect */ 
     if (len == 0) 
      break; /* No more to read for now */ 

     if (send_all(to_socket, buffer, len) < 0) 
      return 1; /* Disconnect */ 
    } 

    return 0; 
} 

int recv_all(socket_fd, char *buffer, const size_t buflen) 
{ 
    size_t total_recv = 0; 
    size_t remaining_len = buflen; 

    while (remaining_len > 0) 
    { 
     ssize_t len = recv(socket_fd, buffer + total_recv, remaining_len); 
     if (len < 0) 
     { 
      if (errno == EWOULDBLOCK) 
       break; /* No more to read at the moment */ 
      return -1; /* An error */ 
     } 
     if (len == 0) 
      break; /* Connection closed */ 

     total_recv += len; 
     remaining_len -= len; 
    } 

    return total_recv; 
} 

與您的代碼的主要區別是,在主循環很多更簡單。您不需要多個accept調用(您爲什麼需要它?)或幾種不同的讀寫功能。我的代碼部分可以進一步簡化,如recv_all函數。

還應當指出的是,我上面的代碼只處理一次一個連接。解決此問題的一種方法是爲每個傳入連接創建一個新線程,或者使用更先進的方法使用連接和緩衝區列表,並在它們與偵聽套接字之間進行多路複用。

+0

非常感謝它的工作 – 2012-04-08 08:22:29