2014-01-14 60 views
4

我有C语言编写的代理服务器代码的程序接受参数,如google.com 9000 80 然后在浏览器中,你输入localhost:9000 GET google.com专页。但我希望能够一次创建多个隧道,但我不知道该怎么做,因为主要功能是无限循环,这是工作程序的基础。代理服务器 - 多

#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <netdb.h> 
#include <string.h> 
#include <signal.h> 
#include <assert.h> 
#include <syslog.h> 
#include <sys/types.h> 
#include <sys/select.h> 
#include <sys/file.h> 
#include <sys/ioctl.h> 
#include <sys/param.h> 
#include <sys/socket.h> 
#include <sys/stat.h> 
#include <sys/time.h> 
#include <sys/wait.h> 
#include <netinet/in.h> 
#include <arpa/ftp.h> 
#include <arpa/inet.h> 
#include <arpa/telnet.h> 
#define BUF_SIZE 4096 

extern int sys_nerr, errno; 

    char client_hostname[64]; 


    void set_nonblock(int fd) 
    { 
     int fl; 
     int x; 
     x = fcntl(fd, F_GETFL, &fl); 
     if (x < 0) { 
     exit(1); 
     } 
     fl |= O_NONBLOCK; 
     x = fcntl(fd, F_SETFL, &fl); 
     if (x < 0) { 
     exit(1); 
     } 
    } 


    int serwer_gniazdo(char *addr, int port) 
    { 
     int addrlen, s, on = 1, x; 
     static struct sockaddr_in client_addr; 

     s = socket(AF_INET, SOCK_STREAM, 0); 
     if (s < 0) 
     perror("socket"), exit(1); 

     addrlen = sizeof(client_addr); 
     memset(&client_addr, '\0', addrlen); 
     client_addr.sin_family = AF_INET; 
     client_addr.sin_addr.s_addr = inet_addr(addr); 
     client_addr.sin_port = htons(port); 
     setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4); 
     x = bind(s, (struct sockaddr *) &client_addr, addrlen); 
     if (x < 0) 
     perror("bind"), exit(1); 

     x = listen(s, 5); 
     if (x < 0) 
     perror("listen"), exit(1); 

     return s; 
    } 

    int otworz_host(char *host, int port) 
    { 
     struct sockaddr_in rem_addr; 
     int len, s, x; 
     struct hostent *H; 
     int on = 1; 

     H = gethostbyname(host); 
     if (!H) 
     return (-2); 

     len = sizeof(rem_addr); 

     s = socket(AF_INET, SOCK_STREAM, 0); 
     if (s < 0) 
     return s; 

     setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4); 

     len = sizeof(rem_addr); 
     memset(&rem_addr, '\0', len); 
     rem_addr.sin_family = AF_INET; 
     memcpy(&rem_addr.sin_addr, H->h_addr, H->h_length); 
     rem_addr.sin_port = htons(port); 
     x = connect(s, (struct sockaddr *) &rem_addr, len); 
     if (x < 0) { 
     close(s); 
     return x; 
     } 
     set_nonblock(s); 
     return s; 
    } 

    int sock_addr_info(struct sockaddr_in addr, int len, char *fqdn) 
    { 
     struct hostent *hostinfo; 

     hostinfo = gethostbyaddr((char *) &addr.sin_addr.s_addr, len, AF_INET); 
     if (!hostinfo) { 
     sprintf(fqdn, "%s", inet_ntoa(addr.sin_addr)); 
     return 0; 
     } 
     if (hostinfo && fqdn) 
     sprintf(fqdn, "%s [%s]", hostinfo->h_name, inet_ntoa(addr.sin_addr)); 
     return 0; 
    } 


    int czekaj_na_polaczenie(int s) 
    { 
     int newsock; 
    static struct sockaddr_in peer; 
    socklen_t len; 
    len = sizeof(struct sockaddr); 
    newsock = accept(s, (struct sockaddr *) &peer, &len); 
     if (newsock < 0) { 
     if (errno != EINTR) 
      perror("accept"); 
     } 
     sock_addr_info(peer, len, client_hostname); 
     set_nonblock(newsock); 
     return (newsock); 
    } 

    int zapis(int fd, char *buf, int *len) 
    { 
     int x = write(fd, buf, *len); 
     if (x < 0) 
      return x; 
     if (x == 0) 
      return x; 
     if (x != *len) 
      memmove(buf, buf+x, (*len)-x); 
     *len -= x; 
     return x; 
    } 

    void klient(int cfd, int sfd) 
    { 
     int maxfd; 
     char *sbuf; 
     char *cbuf; 
     int x, n; 
     int cbo = 0; 
     int sbo = 0; 
     fd_set R; 

     sbuf = (char *)malloc(BUF_SIZE); 
     cbuf = (char *)malloc(BUF_SIZE); 
     maxfd = cfd > sfd ? cfd : sfd; 
     maxfd++; 

     while (1) 
     { 
     struct timeval to; 
     if (cbo) 
      { 
      if (zapis(sfd, cbuf, &cbo) < 0 && errno != EWOULDBLOCK) { 
        exit(1); 
      } 
     } 
     if (sbo) { 
      if (zapis(cfd, sbuf, &sbo) < 0 && errno != EWOULDBLOCK) { 
        exit(1); 
      } 
     } 

     FD_ZERO(&R); 
     if (cbo < BUF_SIZE) 
      FD_SET(cfd, &R); 
     if (sbo < BUF_SIZE) 
      FD_SET(sfd, &R); 

     to.tv_sec = 0; 
     to.tv_usec = 1000; 
     x = select(maxfd+1, &R, 0, 0, &to); 
     if (x > 0) { 
      if (FD_ISSET(cfd, &R)) { 
      n = read(cfd, cbuf+cbo, BUF_SIZE-cbo); 
      if (n > 0) { 
       cbo += n; 
      } else { 
       close(cfd); 
       close(sfd); 
       _exit(0); 
      } 
      } 
      if (FD_ISSET(sfd, &R)) { 
      n = read(sfd, sbuf+sbo, BUF_SIZE-sbo); 
      if (n > 0) { 
       sbo += n; 
      } else { 
       close(sfd); 
       close(cfd); 
       _exit(0); 
      } 
      } 
     } else if (x < 0 && errno != EINTR) { 
      close(sfd); 
      close(cfd); 
      _exit(0); 
     } 
     } 
    } 


    int main(int argc, char *argv[]) 
    { 
     char *localaddr = (char *)"127.0.0.1"; 
     int localport = atoi(argv[1]); 
     char *remoteaddr = (char *)(argv[2]); 
     int remoteport = atoi(argv[3]); 
     int client, server; 
     int master_sock; 

     if (4 != argc) 
     { 
      fprintf(stderr, "usage: %s port host port\n", argv[0]); 
      exit(1); 
     } 

     assert(localaddr); 
     assert(localport > 0); 
     assert(remoteaddr); 
     assert(remoteport > 0); 

     master_sock = serwer_gniazdo(localaddr, localport); 

     for (;;) 
     { 
      if ((client = czekaj_na_polaczenie(master_sock)) < 0) 
       continue; 
      if ((server = otworz_host(remoteaddr, remoteport)) < 0) 
       continue; 
      if (!fork()) { 
       klient(client, server); 
      } 


      close(client); 
      close(server);   
     } 

     printf("Koniec programu"); 

     return 0; 
    } 

回答

7

答案很简单:使用线程!

这里是一个教程如何做到这一点: http://martinbroadhurst.com/server-examples.html

如果你不想乱: http://www.binarytides.com/server-client-example-c-sockets-linux/

处理多个连接,如果你不喜欢线程服务器的一些其它实例(在多线程代码中总是很容易),我推荐阅读以下问题的答案: Tips to write thread-safe UNIX code?

为了长话短说:您需要注意任何共享的变量线程之间,像全局变量,静态和由指针传递的参数。当两个线程尝试在同一位置写入数据(例如client_hostname全局变量)并尝试从中读取数据时,必须避免出现这种情况,因为最终会出现这种情况,当您释放其中一个值并且有两个线程来自共享相同主机名的两个不同客户端。

也请记住一两件事:我亲自见过三个最好的C程序员考虑多线程编程作为他们工作中最困难的部分。你现在正在处理复杂而复杂的问题。如果你一开始失败,不要气馁,每个人一开始都会这样做。

此外,一些建议:千万别把两种不同的语言来命名变量。既然你不能摆脱英语(因为图书馆是英文的),我建议你停止使用波兰语。通常这是大多数公司的标准,即使它们位于非英语国家,也只在源代码中使用英语。

+0

谢谢!我会尽力去做 – lukassz

+0

没问题。如果你发现一些语言上的障碍,随时给我写信,波兰正好是我的母语:) –

+1

你可以做整个事情没有线程(和不分叉此事),如果你想要的。维护一个打开连接的链表,并在面向客户端和面向服务器的FD上以及主套接字上选择“select()”。我的意思是在每个相应的fds上执行FD_SET(当你有缓冲数据写入的时候在写入端做,而当你有缓冲数据读取的时候在读取站点上)。另外选择你的听fd。在select()调用之后,遍历每个FD,并将数据读入缓冲区或将其写出。更容易调试恕我直言,但不是许多许多FD的最佳选择。 – abligh