只接受来自特定子网的连接

Accept connections only from specific subnet

本文关键字:子网 连接      更新时间:2023-10-16

使用winsock,我希望我的服务器应用程序只接受来自某个(假设是192.168.0.0/24)子网的连接。

我在考虑两个选择:

  • 每次accept连接时检查客户端地址

    while (true) {
        SOCKET clientSocket = accept(serverSocket, 
                                     (SOCKADDR *)&clientAddress, 
                                     &addressLenght);
        if (clientSocket == INVALID_SOCKET
        || clientAddress.sin_addr.S_un.S_un_b.s_b1 != 192 
        || clientAddress.sin_addr.S_un.S_un_b.s_b2 != 168 
        || clientAddress.sin_addr.S_un.S_un_b.s_b3 != 0) {
            closesocket(clientSocket);
            continue;
        }
        ...
    }
    
  • 找出相应网络的本地地址,作为bind中的name参数

    SOCKADDR_IN serverAddress;
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_port = htons(13666);
    serverAddress.sin_addr.S_un.S_addr = inet_addr("192.168.0.1");
    result = bind(serverSocket, (SOCKADDR *)&serverAddress, 
                  sizeof(serverAddress));
    

我还有其他选择吗?

注:有人告诉我有一个更好的解决办法。但是我不明白。

如果您只想要服务器所在的同一子网的连接(如果是这种情况,则从您的问题中不清楚),您可以使用setsockopt()将TTL设置为1。该套接字的数据包(包括连接握手)不应该穿过路由器。

如果您只想监听一个子网上的客户端,正确的解决方案是bind()单个监听套接字到与该子网对应的适配器的本地IP。让操作系统为你做过滤。

如果你需要监听多个子网的客户端,你可以根据需要bind()一个单独的监听套接字到每个子网的本地IP,或者你可以bind()一个单一的套接字到INADDR_ANY(0.0.0.0),然后手动过滤客户端。

如果你手动过滤,你应该使用WSAAccept()而不是accept(),这样你就可以利用它的CONDITIONPROC回调。不同之处在于,当使用accept()时,客户端被操作系统无条件地接受到队列中,并在代码访问它们之前由accept()完全连接。当使用WSAAccept()时,您可以更早地访问客户端远程ip,并且可以在将单个客户端放入要连接的队列之前选择是否接受或拒绝它们。