【问题标题】:advise for safely using remote client without exhausting ports建议在不耗尽端口的情况下安全使用远程客户端
【发布时间】:2016-02-16 07:55:52
【问题描述】:

我正在为 Windows 开发一个远程客户端程序,该程序通过以太网/互联网连接将数据从传感器单元发送到服务器应用程序。客户端使用用户名和密码登录服务器。根据协议,客户端然后以一种方式将数据发送到服务器,而无需期望来自服务器的响应。一切正常,直到客户端的无线互联网连接中断。在我的第一个客户端版本中,我尝试在每个循环中进行连接尝试,但由于 Windows 标准超时时间为 4 分钟,因此经常会耗尽端口。前几天我试着换了一个客户。我将 Windows 注册表设置为“TcpTimedWaitDelay”为 1,并重写客户端应用程序以在出错时关闭其套接字,然后等待 1 秒钟,然后再尝试重新连接。这似乎有效,但我今天确实失去了那个客户一段时间。我无法确定无线是否一直处于离线状态。 在使用这种可以可靠使用的客户端时,我正在寻找一种好的方法的建议。是否有另一种方法可以防止应用程序代码中的端口耗尽,或者几乎总是需要修改 Windows 注册表?我可以添加我的代码片段,但我已经在最近的一个问题中检查了我的代码本身。我在这里寻找更广泛的建议。

void checkConnect(NTRIP& server)
{
time_f functionTime = getTimePrecise();
//1st check for recv or gracefully closed socket
char databuf[SERIAL_BUFFERSIZE];
fd_set Reader, Writer, Err;
TIMEVAL Timeout;
Timeout.tv_sec = 1; // timeout after 1 seconds
Timeout.tv_usec = 0;
FD_ZERO(&Reader);
FD_ZERO(&Err);
FD_SET(server.socket, &Reader);
FD_SET(server.socket, &Err);
int iResult = select(0, &Reader, NULL, &Err, &Timeout);
if(iResult > 0)
{
    if(FD_ISSET(server.socket, &Reader) )
    {
        int recvBytes = recv(server.socket, databuf, sizeof(databuf), 0);
        if(recvBytes == SOCKET_ERROR)
        {
            cout << "socket error on receive call from server " << WSAGetLastError() << endl;
            closesocket(server.socket);
            server.connected_IP = false;
        }
        else if(recvBytes == 0)
        {
            cout << "server closed the connection gracefully" << endl;
            closesocket(server.socket);
            server.connected_IP = false;
        }
        else  //>0 bytes were received so read data if needed
        {
            cout << "received " << recvBytes << " bytes of data" << endl;
        }
    }
    if(FD_ISSET(server.socket, &Err))
    {
        cout << "ip thread select returned socket in error group" << endl;
        closesocket(server.socket); //what if dont close this socket, leave open for another loop
        server.connected_IP = false;
    }
}
else if(iResult == SOCKET_ERROR)
{
    cout << "ip thread select returned SOCKET_ERROR " << WSAGetLastError() << endl;
    closesocket(server.socket);
    server.connected_IP = false;
}
//2nd check hard disconnect
if(server.connected_IP == true && functionTime - timer_send_keepalive >= 15.0)
{
    timer_send_keepalive = functionTime;

    char buf1[] = "hello";
    cout << "checking send for error" << endl;

    iResult = send(server_main.socket, buf1, sizeof(buf1), 0);
    if(iResult == SOCKET_ERROR)
    {
        int lasterror = WSAGetLastError();
        if(lasterror == WSAEWOULDBLOCK)
        {
            cout << "server send WSAEWOULDBLOCK" << endl;
        }
        else if(lasterror != WSAEWOULDBLOCK)
        {
            cout << "server testing connection send function error " << lasterror << endl;
            closesocket(server.socket);
            server.connected_IP = false;
        }
    }
    else
    {
        cout << "sent out keep alive " << iResult << " bytes" << endl;
    }
}//end send keep alive
}

发送函数

bool sendData(CHAR_MESSAGE& data)
{
bool check = false;
if(data.flag_toSend == true)
{
    if(WaitForSingleObject(data.mutex, 0) != WAIT_FAILED)
    {
        if(server_main.connected_IP == true)
        {
            int iResult = send(server_main.socket, data.buffer, data.bufferLength, 0);
            if(iResult == SOCKET_ERROR)
            {
                int lasterror = WSAGetLastError();
                if(lasterror == WSAEWOULDBLOCK)
                {
                    cout << "main server send WSAEWOULDBLOCK" << endl;
                    check = false;
                }
                if(lasterror != WSAEWOULDBLOCK)
                {
                    cout << "server main send error " << lasterror << endl;
                    closesocket(server_main.socket);
                    server_main.connected_IP = false;
                    check = false;
                }
            }
            else
            {
                check = true;
                server_main.lastDataSendTime = getTimePrecise();
                //{cout << "sent data to main   server" << endl;}
            }
        }
        if(server_backup.connected_IP == true)
        {
            int iResult = send(server_backup.socket, data.buffer, data.bufferLength, 0);
            if(iResult == SOCKET_ERROR)
            {
                int lasterror = WSAGetLastError();
                if(lasterror == WSAEWOULDBLOCK)
                {
                    cout << "backup server send WSAEWOULDBLOCK" << endl;
                    //check = false;
                }
                if(lasterror != WSAEWOULDBLOCK)
                {
                    cout << "server backup send error " << lasterror << endl;
                    closesocket(server_backup.socket);
                    server_backup.connected_IP = false;
                }
            }
            else
            {
                server_backup.lastDataSendTime = getTimePrecise();
                //{cout << "sent data to backup server" << endl;}
            }
        }
        data.flag_toSend = false;
        ReleaseMutex(data.mutex);
    }//end obtained mutex
}//end data flag to send is true

return check;
}

登录功能

bool loginServer(NTRIP& datasource )
{
std::string sLogin = buildLogin(datasource);
char databuf[1030];

int iResult = send(datasource.socket, sLogin.c_str(), sLogin.length(), 0);
if(iResult == SOCKET_ERROR)
{
    cout << "Send error = " << WSAGetLastError() << endl;
    closesocket(datasource.socket);
    return false;
}
else  //not socket error
{
    int flags = 0;
    fd_set Read, Err;
    TIMEVAL Timeout;
    FD_ZERO(&Read);
    FD_ZERO(&Err);
    FD_SET(datasource.socket, &Read);
    FD_SET(datasource.socket, &Err);
    Timeout.tv_sec = 1;
    Timeout.tv_usec = 0;
    iResult = select(0, &Read, NULL, &Err, &Timeout);
    if(iResult == 0)
    {
        cout << "loginServer function, select timeout" << endl;
        closesocket(datasource.socket);
        return false;
    }
    else if(FD_ISSET(datasource.socket, &Read) )
    {
        int recvBytes = recv(datasource.socket, databuf, sizeof(databuf), flags);
        if(recvBytes == SOCKET_ERROR)
        {
            cout << "loginServer function, Error recv call  " << WSAGetLastError() << endl;
            closesocket(datasource.socket);
            return false;
        }
        else if(recvBytes == 0) //server closed connection
        {
            cout << "loginServer function, server reclosed connection" << endl;
            closesocket(datasource.socket);
            return false;
        }
        else if(recvBytes > 0)  //process response
        {
            std::string tempString;
            for(int n=0; n<recvBytes; n++)
            {
                tempString += databuf[n];
            }
            if(tempString.compare(CONNECT_OK) || tempString.compare(HTTP_OK) )
            {
                cout << "server connected" << endl;
                return true;
            }
            else if(tempString.compare(ERROR_1))
            {
                MessageBox(NULL, ERROR_1, "NTRIP connect Error", MB_OK);
                closesocket(datasource.socket);
                return false;
            }
            else if(tempString.compare(ERROR_2))
            {
                MessageBox(NULL, ERROR_2, "NTRIP connect Error", MB_OK);
                closesocket(datasource.socket);
                return false;
            }
            else if(tempString.compare(ERROR_3))
            {
                MessageBox(NULL, ERROR_3, "NTRIP connect Error", MB_OK);
                closesocket(datasource.socket);
                return false;
            }
            else if(tempString.compare(ERROR_4))
            {
                MessageBox(NULL, ERROR_4, "NTRIP connect Error", MB_OK);
                closesocket(datasource.socket);
                return false;
            }
        }
    }
    else if(FD_ISSET(socket, &Err) )
    {
        cout << "loginServer function, select call error" << endl;
        closesocket(datasource.socket);
        return false;
    }
}//end not socket error on send login
closesocket(datasource.socket);
return false;
}

打开一个新的套接字

bool clientOpenSocket_connectServer(SOCKET& Socket, const char* serverADDR, const char* serverPORT)
{
int check;

if(Socket != INVALID_SOCKET)  //if not closed then close socket
{
    closesocket(Socket);
    Socket = INVALID_SOCKET;
}
time_f timer = getTimePrecise();

while(getTimePrecise() - timer < 5.0){} //wait 5 second before reconnect attempt
//set Windows reg to 1 second TcpTimedWaitDelay

ZeroMemory(&hints, sizeof(hints));
hints.ai_family   = AF_INET;  //use IPv4
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags    = AI_PASSIVE;
//get socket memory address info
check = getaddrinfo(serverADDR , serverPORT, &hints, &result);
if ( check != 0)
{
    printf("Winsock getaddrinfo failed. %d\n", WSAGetLastError());
    return false;
}
//prepare socket, sets IP4 or IP6, sock type and protocol used
for(ptr=result; ptr!=NULL; ptr=ptr->ai_next)
{
    Socket = socket( result->ai_family, result->ai_socktype, result->ai_protocol);
    if (Socket == INVALID_SOCKET)
    {
        printf("Socket failed initializing %d %d.\n", Socket, WSAGetLastError());
        freeaddrinfo (result);
        return false;
    }
    //now have a valid socket
    check = ioctlsocket(Socket, FIONBIO, &NonBlock);
    if (check == SOCKET_ERROR)
    {
        printf("client socket could not set nonblocking, with error: %d\n", WSAGetLastError());
        closesocket(Socket);
        freeaddrinfo(result);
        return false;
    }
    //set sockets to no-linger on close
    char value = 0;
    check = setsockopt( Socket, SOL_SOCKET, SO_DONTLINGER, &value, sizeof( value ) );
    if (check == SOCKET_ERROR)
    {
        cout << "client socket could not set options no-linger " << WSAGetLastError() << endl;
    }
    //disable nagle algorithym
    if(disableNagleSockets == true)
    {
        value = 1;
        check = setsockopt( Socket, IPPROTO_TCP, TCP_NODELAY, &value, sizeof( value ) );
        if (check == SOCKET_ERROR)
        {
            cout << "client socket could not set options  " << WSAGetLastError() << endl;
        }
        else{cout << "Nagle Sockets set disabled" << endl;}
        value = 0;
        check = setsockopt( Socket, IPPROTO_TCP, SO_SNDBUF, &value, sizeof( value ) );
        if (check == SOCKET_ERROR)
        {
            cout << "client socket could not set options  " << WSAGetLastError() << endl;
        }
    }
    //attempt connect
    cout << "attempting connect" << endl;
    check = connect(Socket, ptr->ai_addr, ptr->ai_addrlen);
    if(check == SOCKET_ERROR )
    {
        check = WSAGetLastError();
        if(check == WSAEWOULDBLOCK) // then set a timeout
        {
            fd_set Write, Err;
            TIMEVAL Timeout;
            int TimeoutSec = 10; // timeout after 10 seconds
            FD_ZERO(&Write);
            FD_ZERO(&Err);
            FD_SET(Socket, &Write);
            FD_SET(Socket, &Err);
            Timeout.tv_sec = TimeoutSec;
            Timeout.tv_usec = 0;
            check = select(0, NULL, &Write, &Err, &Timeout);
            if(check == 0)
            {
                printf("connect call to server, select call timeout elapsed\r\n");
                closesocket(Socket);
                freeaddrinfo(result);
                return false;
            }
            else
            {
                if(FD_ISSET(Socket, &Write) )
                {
                    freeaddrinfo(result);
                    cout << "socket opened to server, after wait" << endl;
                    return true;
                }
                if(FD_ISSET(Socket, &Err) )
                {
                    printf("connect call to server, select call error state\r\n");
                    closesocket(Socket);
                    freeaddrinfo(result);
                    return false;
                }
            }
        }
        else if(check == WSAECONNREFUSED)
        {
            cout << "no server program at requested address " << serverADDR << endl;
            closesocket(Socket);
            freeaddrinfo(result);
            return false;
        }
        else if(check == WSAEHOSTDOWN || check == WSAETIMEDOUT)
        {
            cout << "no server present at requested address " << serverADDR << endl;
            closesocket(Socket);
            freeaddrinfo(result);
            return false;
        }
        else
        {
            cout << "connect call WSA error code " << check << endl;
            closesocket(Socket);
            freeaddrinfo(result);
            return false;
        }
    }//end socket error
    //else instant connection is good
    cout << "socket opened to server" << endl;
    freeaddrinfo(result);
    return true;
}//end search for a socket address to use
freeaddrinfo(result); //no socket opened here
return false;
}//end of setup TCP port

所以到目前为止,它会在最后一次连接失败后等待 5 秒,然后再尝试另一个连接。我的早期版本在每个循环中都尝试了一个新连接,直到它连接为止。现在看来,它可能正在处理 5 秒延迟并将 Windows 注册表设置为在 1 秒后关闭端口。

【问题讨论】:

  • 运行此代码并查看 netstat 我发现发生了以下情况。每次连接断开时,新的连接端口都会从先前的端口号增加。当 Windows 达到可用用户端口号的上限时,它只是停止允许连接发生,直到某个本地时钟时间出现所有端口都被操作系统重置。

标签: c++ windows client port winsock2


【解决方案1】:

使用连接直到它断开,然后再创建一个。

如果它经常中断以至于您遇到端口耗尽的情况,那么您是网络问题,而不是编程问题。

如果您遇到连接错误,您应该在每次失败时休眠更多时间,例如1,2,4,8,... 秒。

【讨论】:

  • 这就是客户端所做的,但在我的第一个版本中,由于 Windows 操作系统默认关闭端口的时间,每次线程循环都会在不到一秒的时间内导致端口耗尽时尝试打开端口并连接。这就是为什么在第二个版本中我将其限制为每秒 1 次尝试并编辑了 Windows 注册表。
  • 我发现在本地 PC 时钟时间下午 12:00,Windows 会重置所有端口,因为此时所有断开连接的客户端都将重新连接到服务器。我在任何地方都没有发现这一点。赢得 XP、Vista、8.1。
  • '尝试打开一个端口并每次连接'与我所说的完全相反。请阅读我写的内容。
  • 我读过你写的。您是否建议我在连接函数返回套接字错误状态时不关闭套接字?也许您还没有阅读我所写内容的具体内容?
  • 'Connect every time' 向我建议您每次发送都使用一个新连接,而不是使用相同的连接直到它中断。您显然仍然遇到端口耗尽的事实也是如此。除了你所说的,我无法知道你的意思。在这个阶段你需要发布一些代码。
猜你喜欢
  • 1970-01-01
  • 2011-01-24
  • 1970-01-01
  • 1970-01-01
  • 2012-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多