对本地端口发起Http请求,为什么会有两次连接请求?

发布于 2022-09-12 00:46:19 字数 5422 浏览 25 评论 0

我用以下程序创建了一个服务端,监听本地8888端口:

#include <iostream>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <unordered_map>
#include <fcntl.h>

using namespace std;

#define SERV_IP "0.0.0.0"
#define SERV_PORT 8888
#define MAX_CONN 1024

int main()
{
    sockaddr_in servaddr, clitaddr;
    unordered_map<int,sockaddr_in> fdmap;  //文件描述符与其地址信息

    epoll_event evt, evts[MAX_CONN];  

    char buf[1024];  //读缓冲区
    int lfd;      //监听描述符
    int connfd;   //连接描述符
    int readycnt;  //保存epoll_wait返回值
    int epfd;  //epoll描述符

    socklen_t addr_len = sizeof(clitaddr);

    if ((lfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        cout << "creat socket fault : " << strerror(errno) << endl;
        return 0;
    }

    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    servaddr.sin_addr.s_addr = inet_addr(SERV_IP);

    if (bind(lfd, (sockaddr *) &servaddr, sizeof(servaddr)) == -1)
    {
        cout << "bind fault : " << strerror(errno) << endl;
        return 0;
    }

    if (listen(lfd, 128) == -1)
    {
        cout << "listen fault : " << strerror(errno) << endl;
        return 0;
    }

    epfd = epoll_create(MAX_CONN);   //创建epoll
    if (epfd == -1)
    {
        cout << "epoll creat fault : " << strerror(errno) << endl;
        return 0;
    }

    //lfd监听读事件
    evt.events = EPOLLIN;
    evt.data.fd = lfd;

    if (epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &evt) == -1)  //将lfd加入epoll
    {
        cout << "epoll add error : " << strerror(errno) << endl;
        return 0;
    }

    cout << "Init Success ! " << endl;
    cout << "host ip : " << inet_ntoa(servaddr.sin_addr) << "  port : " << ntohs(servaddr.sin_port) << endl;

    cout << "Waiting for connections ... " << endl;

    while (1)
    {
        readycnt = epoll_wait(epfd, evts, MAX_CONN, -1);

        if (readycnt == -1)
        {
            cout << "epoll fault : " << strerror(errno) << endl;
            return 0;
        }

        for (int i = 0; i < readycnt; i++)  //遍历就绪描述符
        {
            if (evts[i].data.fd == lfd)   //有新连接请求
            {
                connfd = accept(lfd, (sockaddr *) &clitaddr, &addr_len);
                if (connfd == -1)
                {
                    cout << "accept fault : " << strerror(errno) << endl;
                    continue;
                }
                cout << inet_ntoa(clitaddr.sin_addr) << ":" << ntohs(clitaddr.sin_port) << " connected ...  " << endl;

                fdmap[connfd] = clitaddr;   //保存连接信息

                fcntl(connfd,F_SETFL,O_NONBLOCK);

                //添加新连接的监听
                evt.events = EPOLLIN | EPOLLET;
                evt.data.fd = connfd;

                if (epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &evt) == -1) 
                {
                    cout << "epoll add error : " << strerror(errno) << endl;
                    return 0;
                }
            }
            else  //有数据可读
            {
                int readcnt = 0;
                while(1) 
                {
                    readcnt = read(evts[i].data.fd, buf, sizeof(buf));
                    if (readcnt == 0)
                    {

                        if (epoll_ctl(epfd, EPOLL_CTL_DEL, evts[i].data.fd, NULL) == -1)
                        {
                            cout << "epoll del error : " << strerror(errno) << endl;
                            return 0;
                        }
                        cout << inet_ntoa(fdmap[evts[i].data.fd] .sin_addr) << ":" << ntohs(fdmap[evts[i].data.fd] .sin_port) << " exit ... " << endl;
                        close(evts[i].data.fd);//关闭描述符
                        fdmap.erase(evts[i].data.fd);
                    }
                    else if(readcnt>0)
                    {
                        cout << "(From " << inet_ntoa(fdmap[evts[i].data.fd].sin_addr) << ":" << ntohs(fdmap[evts[i].data.fd].sin_port) << ")";
                        for (int j = 0; j < readcnt; j++)cout << buf[j];
                        cout << endl;
                    }
                    else
                    {
                        if(errno == EAGAIN) continue;
                        else break;
                    }
                }
            }
        }
    }
    close(lfd);
    return 0;
}

然后开启服务端,用浏览器访问127.0.0.1:8888,此时服务端信息以及抓包如下所示:
发起请求.png
这里我有两个问题:
1.为什么会有两次连接进来?而且根据http请求报文的来源,真正有用的连接似乎只是46822?
2.从抓包结果我所理解的是,46822进行了三次握手,而46824只进行了一次握手,为什么服务端明明accept了但是没有SYN+ACK发回?

接下来我直接关闭了浏览器,此时服务端显示以及抓包如下所示:
关闭请求.png
根据我的理解,此时来自46822的连接,立刻发起四次挥手,服务端收到FIN后read返回0,然后服务端显示46822已经关闭了连接,
然后过了一会,才显示46824退出:
第二次关闭.png
这里我就觉得很奇怪:为什么46824并没有像46822那样在浏览器关闭时就发起主动关闭,而是过了一会才关闭呢?

问题比较多,麻烦了!谢谢!!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

素衣风尘叹 2022-09-19 00:46:19

这种我也遇到过,不过有两种情况:

  • 一种如上面描述的,浏览器去发起get请求,但复杂的浏览器并不单纯,自己会去尝试发起一次/favicon 的请求
  • 另一种是自己有一个web调试网站,发起一个请求,但一个请求,浏览器端会收到两次请求,这一个就是常见的遇见请求,即先预检(Options),再正常请求(Post,Put, Delete);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文