C++ 代理服务器丢弃数据包

C++ 代理服务器丢弃数据包,c++,sockets,proxy,network-programming,C++,Sockets,Proxy,Network Programming,所以我一直在写一个代理服务器(遵循Beej的《网络编程指南》),现在我偶然发现了一个我似乎无法解决的问题。。快把我逼疯了 问题是,当它将数据包转发到webclient时,由于“由对等方重置连接”,它会丢弃一些数据包 这是完整的代码 #include <iostream> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #inclu

所以我一直在写一个代理服务器(遵循Beej的《网络编程指南》),现在我偶然发现了一个我似乎无法解决的问题。。快把我逼疯了

问题是,当它将数据包转发到webclient时,由于“由对等方重置连接”,它会丢弃一些数据包

这是完整的代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <string>
#include <unistd.h>

using namespace std;

string replace_string(string str, const string from, const string to) {
    size_t start_pos = str.find(from);
    if(start_pos == string::npos)
        return str;
    str.replace(start_pos, from.length(), to);
    return str;
}

void sigchld_handler(int s)
{
    while(waitpid(-1, NULL, WNOHANG) > 0);
}
class Server
{
private:
    string PORT;
    int MAXDATASIZE;
public:
    Server()
    {
        PORT = "3490";
        MAXDATASIZE = 4096;

    }



    // get sockaddr, IPv4 or IPv6:
    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);
    }

    int start_server()
    {

        // first, load up address structs with getaddrinfo():
        int socketfd, new_socketfd;
        struct addrinfo hints, *servinfo, *p;
        struct sockaddr_storage their_addr;
        socklen_t their_addr_size, addr_size;
        struct sigaction sa;

        char s[INET6_ADDRSTRLEN];

        int rv;
        int yes = 1;
        int BACKLOG = 10;
        int numbytes;
        unsigned char buf[MAXDATASIZE];

        memset(&hints, 0, sizeof hints); // Make sure it is cleared
        hints.ai_family = AF_UNSPEC;     // use IPv4 or IPv6, whichever
        hints.ai_socktype = SOCK_STREAM; // TCP socket
        hints.ai_flags = AI_PASSIVE;     // fill in my IP for me

        if ((rv = getaddrinfo(NULL, PORT.c_str(), &hints, &servinfo)) != 0) {
            cerr << "getaddrinfo: " << gai_strerror(rv) << endl;
            return -1;
        }

        // make a socket, bind it asap
        for(p = servinfo; p != NULL; p = p->ai_next)
        {
            if ((socketfd = socket(p->ai_family, p->ai_socktype,
                                   p->ai_protocol)) == -1) {
                //perror("server: socket");
                cerr << "ERROR: Server: socket" << endl;
                continue;
            }

            if (setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &yes,
                           sizeof(int)) == -1) {
                //perror("setsockopt");
                cerr << "ERROR: setsockopt" << endl;
                exit(1);
            }

            if (bind(socketfd, p->ai_addr, p->ai_addrlen) == -1) {
                close(socketfd);
                //perror("server: bind");
                cerr << "ERROR: server: bind" << endl;
                continue;
            }
            break;  
        }
        //If the binding failed
        if ( p == NULL)
        {
            cerr << "Server: failed to bind" << endl;
            return -1;
        }

        //Free space, we do not need it anymore.
        freeaddrinfo(servinfo);

        //Listen to the socket (port)
        if ( listen(socketfd, BACKLOG) == -1)
        {
            cerr << "ERROR: listen" << endl;
        }

        // killing zombie processs (all dead processes)
        sa.sa_handler = sigchld_handler; 
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_RESTART;
        if (sigaction(SIGCHLD, &sa, NULL) == -1) {
            perror("sigaction");
            exit(1);
        }

        cout << "Server: waiting for connections ...\n"<<endl;

        //Main loop 
        for(;;)
        {  
            their_addr_size = sizeof their_addr;          
            //Create a new socket when we got a connection
            if ((new_socketfd = accept(socketfd, (struct sockaddr *)&their_addr, &their_addr_size))== -1) {
                continue;
            }

            //Converts IP address to 127.0.0.1 (or IPv6 format..) format instead of binary 
            inet_ntop(their_addr.ss_family,get_in_addr((struct sockaddr *)&their_addr), s, sizeof s);

            if ((numbytes = recv(new_socketfd, buf, MAXDATASIZE-1, 0)) == -1) {
                cerr << "ERROR: Failed to receive from user-agent" << endl;
                exit(1);
            }
            else
            {
                /*Parse the header and change some things.*/

                string buffer_str(reinterpret_cast<char*>(buf), MAXDATASIZE);   // Convert to unsigned char array to make it easier to handle                              
                buffer_str = replace_string(buffer_str, "Proxy-Connection: keep-alive","Connection: close"); // Make it connection Close instead of Keep-Alive
                buffer_str = replace_string(buffer_str, "Connection: keep-alive","Connection: close"); // Make it connection Close instead of Keep-Alive
                // removes host from GET, and saves it
                int first = buffer_str.find_first_of("/");                      
                int last =  buffer_str.find_first_of("/", first+2);
                int size_of_buf=buffer_str.size();
                unsigned char host_request[MAXDATASIZE];
                for (int a=0;a<=size_of_buf;a++)
                    host_request[a]=buffer_str[a];
                host_request[size_of_buf]='\0';
                string host_name = "";
                for(int i=first+2;i<last;++i) 
                    host_name += buffer_str[i];
                buffer_str.erase(4, buffer_str.find_first_of("/", first+2)-4);



                /*Set up the socket for the proxy-host */

                int host_sockfd, host_numbytes;  
                unsigned char host_buf[MAXDATASIZE];
                struct addrinfo host_hints, *host_servinfo, *host_p;
                int host_rv;
                char host_s[INET6_ADDRSTRLEN];


                memset(&host_hints, 0, sizeof(host_hints));
                host_hints.ai_family = AF_UNSPEC;
                host_hints.ai_socktype = SOCK_STREAM;

                if ((rv = getaddrinfo(host_name.c_str(), "80", &host_hints, &host_servinfo)) != 0) {
                    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
                    return 1;
                }

                // loop through all the results and connect to the first we can
                for(host_p = host_servinfo; host_p != NULL; host_p = host_p->ai_next) {
                    if ((host_sockfd = socket(host_p->ai_family, host_p->ai_socktype,
                                              host_p->ai_protocol)) == -1) {
                        perror("client: socket");
                        continue;
                    }

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

                    break;
                }

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

                inet_ntop(host_p->ai_family, get_in_addr((struct sockaddr *)host_p->ai_addr),s, sizeof s); //Converts the IP address from binary to IPv4/6-format
                //printf("client: connecting to %s\n", s);

                freeaddrinfo(host_servinfo); // all done with this structure

                /*Send the GET request to the server*/
                send_message(host_sockfd, host_request, sizeof(host_request), "Webserver");
                if (!fork())
                {
                    memset(&host_buf, 0, MAXDATASIZE);
                    while (recv(host_sockfd, &host_buf, MAXDATASIZE, 0) > 0 ) {
                        close(socketfd);

                        send_message(new_socketfd, (unsigned char *)host_buf, MAXDATASIZE, "Browser");
                        memset(&host_buf, 0, MAXDATASIZE);
                    }

                }

                close(host_sockfd);
            }


            //cout << "server: got connection from " << s << endl;

            close(new_socketfd);  // parent doesn't need this

        }

        return 1;
    }
    int send_message(int &socket,unsigned char msg[], int length, string too)
    {
        usleep(10);
        if (send(socket, msg, length, 0) == -1)
        {
            cerr << "ERROR: sending to " << too << endl;
            cerr << "We will lose a packet due to" << endl;
            perror("send");
            cerr << "-------------------MEDDELANDE-------------------\n\n" << msg << endl << 
                "--------------------------------------------------\n\n";
            return -1;
        }
        return 1;
    }
};

int main()
{
    Server srv;
    srv.start_server();
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
字符串替换_字符串(字符串str、常量字符串from、常量字符串to){
size\u t start\u pos=str.find(from);
if(start_pos==string::npos)
返回str;
str.replace(起始位置,从.length()到);
返回str;
}
void sigchld_处理程序(int s)
{
而(waitpid(-1,NULL,WNOHANG)>0);
}
类服务器
{
私人:
字符串端口;
int-MAXDATASIZE;
公众:
服务器()
{
PORT=“3490”;
MAXDATASIZE=4096;
}
//获取sockaddr、IPv4或IPv6:
void*get\u in\u addr(结构sockaddr*sa)
{
如果(sa->sa_族==AF_INET){
return&(((struct sockaddr_in*)sa)->sin_addr);
}
返回((结构sockaddr_in6*)sa)->sin6_addr);
}
int start_server()
{
//首先,使用getaddrinfo()加载地址结构:
int socketfd、new_socketfd;
结构addrinfo提示,*servinfo,*p;
结构sockaddr\u存储它们的地址;
他们的地址大小,地址大小;
struct-sigaction-sa;
字符s[INET6_ADDRSTRLEN];
int-rv;
int yes=1;
int BACKLOG=10;
整数单位;
无符号字符buf[MAXDATASIZE];
memset(&hints,0,sizeof-hints);//确保已清除它
hits.ai_family=AF_unsec;//使用IPv4或IPv6,以两者中的任何一个为准
hints.ai_socktype=SOCK_STREAM;//TCP套接字
hits.ai_flags=ai_PASSIVE;//为我填写我的IP
if((rv=getaddrinfo(NULL,PORT.c_str(),&hints,&servinfo))!=0){
cerr ai_socktype,
p->ai_协议)=-1){
//perror(“服务器:套接字”);

Celr

代码看起来不像BeJS指南,C++不是我最强的部分,但对我来说,似乎你没有跟踪从套接字中写入或读取的数据量。难怪数据丢失。 while(为什么?)中有一个close(),send_message中有一个sleep()等等。每当你发现自己在做sleep()来让事情顺利进行的时候,通常都是一个bug

编辑:为了更具建设性,我建议您从Beejs指南中复制sendall()。跟踪recv()提供给您的数据量,并将其与sendall一起发送

为了可能提高速度,请跟踪接收和发送的数据量,并缓冲一些数据,这样您就不会阻止等待发送缓冲区变为可用


EDIT2:还向子进程添加了一个出口。现在,当recv&send循环完成时,它会运行到父代码中。看起来你想让它留在
if(!fork())
中,但目前它没有。

在这里一些人的帮助下,我已经解决了这个问题

我必须改变的第一件事是在recv()中用strlen替换
sizeof

其次,我没有存储recv接收到的数据的大小,因此当我将接收到的数据从Web服务器转发到webbrowser时,我说数据包包含
MAXDATASIZE
。解决此问题的方法是存储接收到的数据,然后在
send()中将该大小作为参数指定

上一次,我不得不将每个未签名的字符更改为仅字符


感谢所有帮助我找到此解决方案的人!尤其是Thouvila!

“对等重设连接”可能意味着用户关闭了浏览器窗口或单击了“停止”按钮。我正在本地主机上运行它。我没有关闭浏览器或单击stop@thang你会推荐多大的数据包?你为什么不用wireshark嗅探它并发布结果..是当另一方发送类似的RST()时?它仍然不能正常工作:(我现在没有时间查看所有代码,但有两个快速观察结果。1)
recv()
可以返回0。这意味着没有更多的内容要写入客户端,您应该有序地退出子级。有关返回值的详细信息,请参阅手册页。2)获取
recv()的返回值< /代码>直接进入变量。现在您正在接收缓冲区上执行STRLLN。这将不可靠地工作。发送的数据可能包含等于StLLN的“NULL”。ExtSe()似乎位于Okayy的地方,尽管第一个条件也应该考虑0(如前所述)。我很高兴您理解了。最后一个代码。(在pastebin.org上)太长了,我从来没有时间详细研究它。至于recv(),我仍然建议获得如下数据长度:
int bytes_received=recv(…)
并处理0和<0的值。这就是我现在正在做的。我正在稍微调整代码,将其划分为函数等等。完成后我将发布代码。再次感谢您的帮助!