【问题标题】:Proxy server in C - MultipleC中的代理服务器 - 多个
【发布时间】:2014-02-03 04:54:40
【问题描述】:

我有一个用 C 编写的代理服务器代码。该程序接受参数,例如 google.com 9000 80 然后在你的浏览器中输入 localhost:9000 得到 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;
    }

【问题讨论】:

    标签: c linux sockets select


    【解决方案1】:

    答案很简单:使用线程!

    这里有一个教程如何做到这一点: http://www.binarytides.com/server-client-example-c-sockets-linux/

    如果您不喜欢线程,其他一些处理多个连接的服务器示例: http://martinbroadhurst.com/server-examples.html

    如果您不想搞砸(这在多线程代码中总是很容易),我建议您阅读这个问题的答案: Tips to write thread-safe UNIX code?

    长话短说:您需要注意线程之间共享的任何变量,例如全局变量、静态变量和指针传递的参数。您必须避免这样的情况,即两个线程尝试在同一个位置写入(例如 client_hostname 全局变量)然后尝试从中读取,因为您可能会遇到一种情况,当您丢失其中一个值并拥有两个线程时来自共享相同主机名的两个不同客户端。

    还要记住一件事:我见过的三位最优秀的 C 程序员都认为多线程编程是他们工作中最困难的部分。您现在正在处理复杂而复杂的问题。如果你一开始失败了也不要气馁,每个人一开始都失败了。

    另外,还有一点建议:不要混淆两种不同的语言来命名变量。既然你不能摆脱英语(因为图书馆是英文的),我建议你停止使用波兰语单词。通常,大多数公司的标准是无论如何只在其源代码中使用英语——即使它们位于非英语国家。

    【讨论】:

    • 谢谢!我会努力做到的
    • 没问题。如果您发现语言障碍,请随时给我写信,波兰语恰好是我的母语 :)
    • 如果你愿意,你可以在没有线程的情况下完成整个事情(并且就此而言没有分叉)。维护打开连接的链接列表,select() 在每个的面向客户端和面向服务器的 FD 以及您的主套接字上。我的意思是在每个相应的 fds 上执行 FD_SET(当您有缓冲数据要写入时在写入端执行,当您有缓冲数据要读取时在读取站点执行)。还要在您的收听 fd 上选择。在select() 调用之后,遍历每个FD,将数据读入缓冲区或写出。更容易调试恕我直言,但对于许多 FD 来说并不是最优的。
    猜你喜欢
    • 1970-01-01
    • 2010-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-23
    • 2022-12-09
    相关资源
    最近更新 更多