accept()函数在Winsock中的工作原理是怎样的?

huangapple go评论72阅读模式
英文:

How exactly does the accept() Function in Winsock work?

问题

我正在学习使用C++和Winsock。

一旦ListenSocket通过listen()开始工作,我应该如何以清晰的方式处理多个连接?

当前,每当建立一个新连接时,它都会被移到ConnectedSocket中,当建立新连接时,它会被覆盖。

    //监听和成功报告
    result = listen(ListenSocket, 2);
    if (result == 0)
    {
        PRINT("正在监听...")
    }
    else
    {
        closesocket(ListenSocket);
        PRINT("无法监听")
        return;
    }

    //接受连接
    ConnectedSocket = accept(m_Socket, nullptr, NULL);

我的第一个想法是创建一个包含套接字的数组,但我如何知道何时建立了新连接,因为我无法自己编写处理新连接的代码。目前我唯一想到的方法是每次运行我的代码时检查连接的套接字是否无效,然后将其移到数组中(如果它是有效的),这不是一个好的选择,因为当其他客户端尝试同时连接时,其中一个可能会被覆盖。

我相信有更好的方法,不是吗?

英文:

I am learning Winsock with c++ right now.

Once the ListenSocket has started with listen(), how am i supposed to handle more than one connection in a clean way?

Right now, everytime a new connection is established it is moved into the ConnectedSocket, and when a new connectiion is established, it gets overridden.

    //Listen & Success Report
    result = listen(ListenSocket, int(2));
    if (result == 0)
    {
        PRINT("Listening...")
    }
    else
    {
        closesocket(ListenSocket);
        PRINT("Failed to Listen")
        return;
    }

    //Accept
    ConnectedSocket = accept(m_Socket, nullptr, NULL);

My first Idea was to create an Array that holds the Sockets but how do i know when a new connection has been established, since i don't get to write my own code for handling new connections. The only way i can think of right now is to check everytime i run my code, wether the connected Socket is Invalid or not, and then move it into an Array(if it is valid), which is not a good alternative because when other Clients try to Connect at the same time, one of them might get overriden.

I am sure there is a better way, isn't it?

答案1

得分: 2

任何非平凡的服务器都会像这样循环调用accept

while (true) {
  SOCKET connected = accept(m_Socket, nullptr, nullptr);
  // 对连接的`connected`进行操作
}

请注意,accept的返回值被分配给了while循环中的局部变量。

一个典型的“对连接进行操作”的方式可以是创建一个新线程,接收connected中的套接字的副本。或者,您可以将套接字添加到一个全局集合中,然后使用select或类似的机制进行轮询。

英文:

Any non-trivial server will call accept in a loop like so:

while (true) {
  SOCKET connected = accept(m_Socket, nullptr, nullptr);
  // do something with connected
}

Note that the return value of accept is assigned to a variable that is local to the while loop.

A typical "do something" could be to spawn a new thread that receives a copy of the socket in connected.
Alternatively, you might want to add the socket to a global set that you poll with select or equivalent mechanism.

答案2

得分: 0

在单线程服务器中,通常将socket描述符放入容器中,如std::vector<SOCKET>,然后使用select或类似的函数等待所有套接字上的事件,同时处理多个套接字的事件。

示例:

std::vector<SOCKET> sockets;

sockets.push_back(m_Socket); // 服务器套接字

fd_set readfds{};
for(auto s : sockets) FD_SET(s, &readfds); // 将套接字放入fd_set

int ready_count = select(0 /*在WinSock中被忽略*/,
                         &readfds, nullptr, nullptr, nullptr);

// 用于新连接和关闭连接的簿记:
std::vector<bool> closed_sockets(sockets.size());
std::vector<SOCKET> new_sockets;

for(int i = 0; i < ready_count; ++i) {
    if(not FD_ISSET(sockets[i], &readfds) { // 未就绪
        ++ready_count;
        continue;
    }

    // sockets[i] 已准备好读取

    if(sockets[i] == m_Socket) {
        // 服务器
        SOCKET ConnectedSocket = accept(m_Socket, nullptr, NULL);

        if(ConnectedSocket == INVALID_SOCKET) {
            // 错误
        } else {
            // 保存此连接
            new_sockets.push_back(ConnectedSocket);
        }
    } else {
        // 客户端套接字之一

        // 如果从 `socket[i]` 读取指示客户端关闭连接:
        closed_sockets[i] = true;
    }
}

// 从`closed_sockets`中删除设置为`true`的套接字
// 从`new_sockets`中将新连接添加到`sockets`
英文:

In a single threaded server you typically put the socket descriptor in a container, like a std::vector&lt;SOCKET&gt;, and then wait for events on all the sockets using
select or a similar function that can wait on events on many sockets at the same time.

Example:

std::vector&lt;SOCKET&gt; sockets;

sockets.push_back(m_Socket); // the server socket

fd_set readfds{};
for(auto s : sockets) FD_SET(s, &amp;readfds); // put sockets in the fd_set

int ready_count = select(0 /*ignored in WinSock*/,
                         &amp;readfds, nullptr, nullptr, nullptr);

// for bookkeeping of new and closed connections:
std::vector&lt;bool&gt; closed_sockets(sockets.size());
std::vector&lt;SOCKET&gt; new_sockets;

for(int i = 0; i &lt; ready_count; ++i) {
    if(not FD_ISSET(sockets[i], &amp;readfds) { // not ready
        ++ready_count;
        continue;
    }

    // sockets[i] is ready to be read

    if(sockets[i] == m_Socket) {
        // server
        SOCKET ConnectedSocket = accept(m_Socket, nullptr, NULL);

        if(ConnectedSocket == INVALID_SOCKET) {
            // error
        } else {
            // save this connection
            new_sockets.push_back(ConnectedSocket);
        }
    } else {
        // one of the client sockets

        // if reading from `socket[i]` indicates that the client
        // closed the connection:
        closed_sockets[i] = true;
    }
}

// Remove sockets set to `true` in `closed_sockets`
// Add new connections to `sockets` from `new_sockets`

huangapple
  • 本文由 发表于 2023年2月23日 19:43:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/75544356.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定