第一个客户端在多套接字winsock服务器中滞后

发布于 2024-10-30 19:41:22 字数 3996 浏览 2 评论 0原文

我有一个 WinSock 服务器设置,它可以正确接受客户端并转发适当的信息。服务器接收两个客户端,接收 256 字节的固定大小缓冲区,存储它,然后将另一个缓冲区中继到客户端。 (即客户端1发送其缓冲区,服务器保存它,然后将客户端2的缓冲区发送给客户端1)。

每当 client1 更改其缓冲区时,client2 大约需要 4 秒才能收到更改。如果 client2 进行更改,client1 几乎立即收到更新(不到 0.1 秒)。

Nagle 的算法被禁用,我尝试更改服务器处理请求的顺序,但 client1 总是滞后。数据总是完好无损地显示,但时间太长。下面是服务器处理数据的循环:

for(;;)
{   
    // check if more clients can join
    if (numClients < MAX_CLIENTS)
    {
        theClients[numClients] = accept(listeningSocket, NULL, NULL);
        if (theClients[numClients] == INVALID_SOCKET)
        {
            nret = WSAGetLastError();
            JBS::reportSocketError(nret, "server accept()");
            closesocket(listeningSocket);
            WSACleanup();
            exit(0);
        }
        // disable Nagle's algorithm
        int flag = 1;
        int result = setsockopt(theClients[numClients], IPPROTO_TCP, TCP_NODELAY, 
            (char *) &flag, sizeof(int));
        if (result < 0)
        {
            nret = WSAGetLastError();
            JBS::reportSocketError(nret, "client connect()");
            closesocket(theClients[numClients]);
            WSACleanup();
        }
        // make the socket non-blocking
        u_long iMode = 1;
        ioctlsocket(theClients[numClients],FIONBIO, &iMode);

        cout << "Client # " << numClients << " connected." << endl;
        numClients++;
        started = true;
    }
    else
    {
        // we've received all the connections, so close the listening socket
        closesocket(listeningSocket);
    }

    // process client2
    if (theClients[1] != INVALID_SOCKET)
    {
        memset(keys2, 0, 255);
        // receive the updated buffer
        nBytes = recv(theClients[1], keys2, sizeof(keys2), 0);
        receiveResult = WSAGetLastError();
        if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0))
        {
            JBS::reportSocketError(receiveResult, "server receive keys2()");
            shutdown(theClients[1],2);
            closesocket(theClients[1]);
            WSACleanup();
            exit(0);
            break;
        }
        // send client1's buffer to client2
        send(theClients[1],keys1,sizeof(keys1),0);
        sendResult = WSAGetLastError();
        if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0))
        {
            JBS::reportSocketError(sendResult, "server send keys1()");
            shutdown(theClients[1],2);
            closesocket(theClients[1]);
            WSACleanup();
            exit(0);
            break;
        }
    }

    // process client1
    if (theClients[0] != INVALID_SOCKET)
    {
        memset(keys1, 0, 255);
        // receive the updated buffer
        nBytes = recv(theClients[0], keys1, sizeof(keys1), 0);
        receiveResult = WSAGetLastError();
        if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0))
        {
            JBS::reportSocketError(receiveResult, "server receive keys1()");
            shutdown(theClients[0],2);
            closesocket(theClients[0]);
            WSACleanup();
            exit(0);
            break;
        }
        // send client2's buffer to client1
        send(theClients[0],keys2,sizeof(keys2),0);
        sendResult = WSAGetLastError();
        if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0))
        {
            JBS::reportSocketError(sendResult, "server send keys2()");
            shutdown(theClients[0],2);
            closesocket(theClients[0]);
            WSACleanup();
            exit(0);
            break;
        }
    }
    Sleep((float)(1000.0f / 30.0f));
}

客户端发送代码:

int nError, sendResult;

sendResult = send(theSocket, keys, sizeof(keys),0);
nError=WSAGetLastError();
if((nError != WSAEWOULDBLOCK) && (nError != 0))
{
    JBS::reportSocketError(sendResult, "client send()");
    shutdown(theSocket,2);
    closesocket(theSocket);
    WSACleanup();
}

I have a WinSock server setup, which is properly accepting clients and relaying the appropriate information. The server takes two clients, receives a fixed size buffer of 256 bytes, stores it, and then relays the other buffer to the client. (Ie. client1 sends its buffer, server saves it, then sends client1 the buffer for client2).

Anytime client1 changes its buffer, it takes roughly 4 seconds for client2 to receive the changes. If client2 makes a change, client1 receives the update almost instantly (less than 0.1s).

Nagle's algorithm is disabled and I've tried changing the order which the server processes the requests, but client1 always lags. The data always shows up intact, but takes too long. Below is the loop the server uses to process the data:

for(;;)
{   
    // check if more clients can join
    if (numClients < MAX_CLIENTS)
    {
        theClients[numClients] = accept(listeningSocket, NULL, NULL);
        if (theClients[numClients] == INVALID_SOCKET)
        {
            nret = WSAGetLastError();
            JBS::reportSocketError(nret, "server accept()");
            closesocket(listeningSocket);
            WSACleanup();
            exit(0);
        }
        // disable Nagle's algorithm
        int flag = 1;
        int result = setsockopt(theClients[numClients], IPPROTO_TCP, TCP_NODELAY, 
            (char *) &flag, sizeof(int));
        if (result < 0)
        {
            nret = WSAGetLastError();
            JBS::reportSocketError(nret, "client connect()");
            closesocket(theClients[numClients]);
            WSACleanup();
        }
        // make the socket non-blocking
        u_long iMode = 1;
        ioctlsocket(theClients[numClients],FIONBIO, &iMode);

        cout << "Client # " << numClients << " connected." << endl;
        numClients++;
        started = true;
    }
    else
    {
        // we've received all the connections, so close the listening socket
        closesocket(listeningSocket);
    }

    // process client2
    if (theClients[1] != INVALID_SOCKET)
    {
        memset(keys2, 0, 255);
        // receive the updated buffer
        nBytes = recv(theClients[1], keys2, sizeof(keys2), 0);
        receiveResult = WSAGetLastError();
        if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0))
        {
            JBS::reportSocketError(receiveResult, "server receive keys2()");
            shutdown(theClients[1],2);
            closesocket(theClients[1]);
            WSACleanup();
            exit(0);
            break;
        }
        // send client1's buffer to client2
        send(theClients[1],keys1,sizeof(keys1),0);
        sendResult = WSAGetLastError();
        if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0))
        {
            JBS::reportSocketError(sendResult, "server send keys1()");
            shutdown(theClients[1],2);
            closesocket(theClients[1]);
            WSACleanup();
            exit(0);
            break;
        }
    }

    // process client1
    if (theClients[0] != INVALID_SOCKET)
    {
        memset(keys1, 0, 255);
        // receive the updated buffer
        nBytes = recv(theClients[0], keys1, sizeof(keys1), 0);
        receiveResult = WSAGetLastError();
        if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0))
        {
            JBS::reportSocketError(receiveResult, "server receive keys1()");
            shutdown(theClients[0],2);
            closesocket(theClients[0]);
            WSACleanup();
            exit(0);
            break;
        }
        // send client2's buffer to client1
        send(theClients[0],keys2,sizeof(keys2),0);
        sendResult = WSAGetLastError();
        if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0))
        {
            JBS::reportSocketError(sendResult, "server send keys2()");
            shutdown(theClients[0],2);
            closesocket(theClients[0]);
            WSACleanup();
            exit(0);
            break;
        }
    }
    Sleep((float)(1000.0f / 30.0f));
}

Client sending code:

int nError, sendResult;

sendResult = send(theSocket, keys, sizeof(keys),0);
nError=WSAGetLastError();
if((nError != WSAEWOULDBLOCK) && (nError != 0))
{
    JBS::reportSocketError(sendResult, "client send()");
    shutdown(theSocket,2);
    closesocket(theSocket);
    WSACleanup();
}

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

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

发布评论

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

评论(1

小耗子 2024-11-06 19:41:22

我已将您的代码粘贴在下面,其中包含一些内联注释,主要是因为我无法将其全部合理地放入注释中。您如何确定从 client1 到 client2 的更改需要四秒钟?目视检查?这是否意味着 Client1 & Client2 在同一台机器上运行(无需担心不同的网络延迟问题)?

我突出显示了一些看起来错误的块。它们可能不是,这可能是因为您尝试简化您发布的代码并且错过了一些位。我还针对您可能想要添加一些日志记录的位置提出了一些建议。如果套接字确实是非阻塞的,那么您应该很快从所有调用中返回并且无法读取数据,除非客户端已发送数据。如果您有 4 秒的延迟,那么问题可能是:

  • 客户端尚未发送...客户端上是否禁用了 Nagle?如果是这种情况,我预计会连续调用 recv,但没有数据。
  • recv 调用花费的时间太长...套接字真的处于非阻塞模式吗?
  • 发送调用花费的时间太长...套接字是否处于非阻塞模式,是否已缓冲,客户端是否正在尝试接收数据?

掌握每段代码所花费的时间将有助于找出问题所在。

您可以使用类似以下内容(从网络借用)来获取时间:

 struct timeval tv;
 struct timezone tz;
 struct tm *tm;
 gettimeofday(&tv, &tz);
 tm=localtime(&tv.tv_sec);
 printf(" %d:%02d:%02d %d \n", tm->tm_hour, tm->tm_min,
          m->tm_sec, tv.tv_usec);

您的代码:

for(;;)
{   

/* This block of code is checking the server socket and accepting
 * connections, until two? (MAX_CLIENTS isn't defined in visible code)
 * connections have been made.  After this, it is attempting to close
 * the server socket everytime around the loop.  This may have side
 * effects (although probably not), so I'd clean it up, just in case
 */
/* LOG TIME 1 */
    // check if more clients can join
    if (numClients < MAX_CLIENTS)
    {
        theClients[numClients] = accept(listeningSocket, NULL, NULL);
        if (theClients[numClients] == INVALID_SOCKET)
        {
            nret = WSAGetLastError();
            JBS::reportSocketError(nret, "server accept()");
            closesocket(listeningSocket);
            WSACleanup();
            exit(0);
        }
        // disable Nagle's algorithm
        int flag = 1;
        int result = setsockopt(theClients[numClients], IPPROTO_TCP, TCP_NODELAY, 
            (char *) &flag, sizeof(int));
        if (result < 0)
        {
            nret = WSAGetLastError();
            JBS::reportSocketError(nret, "client connect()");
            closesocket(theClients[numClients]);
            WSACleanup();
        }
        // make the socket non-blocking
        u_long iMode = 1;
        ioctlsocket(theClients[numClients],FIONBIO, &iMode);

        cout << "Client # " << numClients << " connected." << endl;
        numClients++;
/* This started variable isn't used, is it supposed to be wrapping 
 * this server code in an if statement?
 */
        started = true;
    }
    else
    {
        // we've received all the connections, so close the listening socket
        closesocket(listeningSocket);
    }
/* LOG TIME 2 */

    // process client2
    if (theClients[1] != INVALID_SOCKET)
    {
        memset(keys2, 0, 255);
        // receive the updated buffer
/* LOG TIME 3 */
        nBytes = recv(theClients[1], keys2, sizeof(keys2), 0);
/* LOG TIME 4 */
        receiveResult = WSAGetLastError();
        if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0))
        {
            JBS::reportSocketError(receiveResult, "server receive keys2()");
            shutdown(theClients[1],2);
            closesocket(theClients[1]);
            WSACleanup();
            exit(0);
            break;
        }
        // send client1's buffer to client2
/* LOG TIME 5 */
        send(theClients[1],keys1,sizeof(keys1),0);
/* LOG TIME 6 */
        sendResult = WSAGetLastError();
        if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0))
        {
            JBS::reportSocketError(sendResult, "server send keys1()");
            shutdown(theClients[1],2);
            closesocket(theClients[1]);
            WSACleanup();
            exit(0);
            break;
        }
    }

    // process client1
/* If the client has been accepted (note that because this
 * is part of the same block of code, and there's no protection
 * around it, the first connection will process it's first
 * receive/send combination before the second socket has been accepted)
 */
    if (theClients[0] != INVALID_SOCKET)
    {
        memset(keys1, 0, 255);
        // receive the updated buffer
/* You're trying a receive against a non-blocking socket.  I would expect this
 * to fail with WSAEWOULDBLOCK, if nothing has been sent by the client, but
 * this block of data will still be sent to the client
 */
/* LOG TIME 7 */
        nBytes = recv(theClients[0], keys1, sizeof(keys1), 0);
/* LOG TIME 8 */
        receiveResult = WSAGetLastError();
        if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0))
        {
            JBS::reportSocketError(receiveResult, "server receive keys1()");
            shutdown(theClients[0],2);
            closesocket(theClients[0]);
            WSACleanup();
            exit(0);
            break;
        }
        // send client2's buffer to client1
/* The first time around the loop, you're sending the buffer to the
 * first connected client, even though the second client hasn't connected yet.
 * This will continue 30 times a second, until the second client connects.  Does
 * the client handle this correctly?
 */
/* LOG TIME 9 */
        send(theClients[0],keys2,sizeof(keys2),0);
/* LOG TIME 10 */
        sendResult = WSAGetLastError();
        if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0))
        {
            JBS::reportSocketError(sendResult, "server send keys2()");
            shutdown(theClients[0],2);
            closesocket(theClients[0]);
            WSACleanup();
            exit(0);
            break;
        }
    }
    Sleep((float)(1000.0f / 30.0f));
}

客户端发送代码:

int nError, sendResult;
/* There's no recv / loop in this section
 */
sendResult = send(theSocket, keys, sizeof(keys),0);
nError=WSAGetLastError();
if((nError != WSAEWOULDBLOCK) && (nError != 0))
{
    JBS::reportSocketError(sendResult, "client send()");
    shutdown(theSocket,2);
    closesocket(theSocket);
    WSACleanup();
}

I've pasted your code below, with some inline comments in it, mostly because I can't fit it all reaonsably in a comment. How are you determining that it's taking four seconds for changes to get from client1 to client2? Visual inspection? Does this mean that Client1 & Client2 are running on the same machine (no different network latency issues to worry about)?

I've highlighted some blocks that look wrong. They may not be, it may be because you've tried to simplify the code that you've posted and you've missed some bits. I've also made some suggestions for where you might want to add some logging. If the sockets are really non-blocking you should be coming back from all of the calls very quickly and failing to read data, unless the client has sent it. If you've got a 4 second delay, then the problem could be:

  • the client hasn't sent it... is Nagle disabled on the client? If this were the case, I'd expect successive calls to recv to happen, with no data.
  • The recv call is taking too long... is the socket really in non-blocking mode?
  • The send call is taking too long... is the socket in non-blocking mode, is it buffered, is the client trying to receive the data?

Having the times each section of code takes will help to track down where your problem is.

You can get the time, using something like this (borrowed from the web):

 struct timeval tv;
 struct timezone tz;
 struct tm *tm;
 gettimeofday(&tv, &tz);
 tm=localtime(&tv.tv_sec);
 printf(" %d:%02d:%02d %d \n", tm->tm_hour, tm->tm_min,
          m->tm_sec, tv.tv_usec);

Your code:

for(;;)
{   

/* This block of code is checking the server socket and accepting
 * connections, until two? (MAX_CLIENTS isn't defined in visible code)
 * connections have been made.  After this, it is attempting to close
 * the server socket everytime around the loop.  This may have side
 * effects (although probably not), so I'd clean it up, just in case
 */
/* LOG TIME 1 */
    // check if more clients can join
    if (numClients < MAX_CLIENTS)
    {
        theClients[numClients] = accept(listeningSocket, NULL, NULL);
        if (theClients[numClients] == INVALID_SOCKET)
        {
            nret = WSAGetLastError();
            JBS::reportSocketError(nret, "server accept()");
            closesocket(listeningSocket);
            WSACleanup();
            exit(0);
        }
        // disable Nagle's algorithm
        int flag = 1;
        int result = setsockopt(theClients[numClients], IPPROTO_TCP, TCP_NODELAY, 
            (char *) &flag, sizeof(int));
        if (result < 0)
        {
            nret = WSAGetLastError();
            JBS::reportSocketError(nret, "client connect()");
            closesocket(theClients[numClients]);
            WSACleanup();
        }
        // make the socket non-blocking
        u_long iMode = 1;
        ioctlsocket(theClients[numClients],FIONBIO, &iMode);

        cout << "Client # " << numClients << " connected." << endl;
        numClients++;
/* This started variable isn't used, is it supposed to be wrapping 
 * this server code in an if statement?
 */
        started = true;
    }
    else
    {
        // we've received all the connections, so close the listening socket
        closesocket(listeningSocket);
    }
/* LOG TIME 2 */

    // process client2
    if (theClients[1] != INVALID_SOCKET)
    {
        memset(keys2, 0, 255);
        // receive the updated buffer
/* LOG TIME 3 */
        nBytes = recv(theClients[1], keys2, sizeof(keys2), 0);
/* LOG TIME 4 */
        receiveResult = WSAGetLastError();
        if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0))
        {
            JBS::reportSocketError(receiveResult, "server receive keys2()");
            shutdown(theClients[1],2);
            closesocket(theClients[1]);
            WSACleanup();
            exit(0);
            break;
        }
        // send client1's buffer to client2
/* LOG TIME 5 */
        send(theClients[1],keys1,sizeof(keys1),0);
/* LOG TIME 6 */
        sendResult = WSAGetLastError();
        if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0))
        {
            JBS::reportSocketError(sendResult, "server send keys1()");
            shutdown(theClients[1],2);
            closesocket(theClients[1]);
            WSACleanup();
            exit(0);
            break;
        }
    }

    // process client1
/* If the client has been accepted (note that because this
 * is part of the same block of code, and there's no protection
 * around it, the first connection will process it's first
 * receive/send combination before the second socket has been accepted)
 */
    if (theClients[0] != INVALID_SOCKET)
    {
        memset(keys1, 0, 255);
        // receive the updated buffer
/* You're trying a receive against a non-blocking socket.  I would expect this
 * to fail with WSAEWOULDBLOCK, if nothing has been sent by the client, but
 * this block of data will still be sent to the client
 */
/* LOG TIME 7 */
        nBytes = recv(theClients[0], keys1, sizeof(keys1), 0);
/* LOG TIME 8 */
        receiveResult = WSAGetLastError();
        if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0))
        {
            JBS::reportSocketError(receiveResult, "server receive keys1()");
            shutdown(theClients[0],2);
            closesocket(theClients[0]);
            WSACleanup();
            exit(0);
            break;
        }
        // send client2's buffer to client1
/* The first time around the loop, you're sending the buffer to the
 * first connected client, even though the second client hasn't connected yet.
 * This will continue 30 times a second, until the second client connects.  Does
 * the client handle this correctly?
 */
/* LOG TIME 9 */
        send(theClients[0],keys2,sizeof(keys2),0);
/* LOG TIME 10 */
        sendResult = WSAGetLastError();
        if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0))
        {
            JBS::reportSocketError(sendResult, "server send keys2()");
            shutdown(theClients[0],2);
            closesocket(theClients[0]);
            WSACleanup();
            exit(0);
            break;
        }
    }
    Sleep((float)(1000.0f / 30.0f));
}

Client sending code:

int nError, sendResult;
/* There's no recv / loop in this section
 */
sendResult = send(theSocket, keys, sizeof(keys),0);
nError=WSAGetLastError();
if((nError != WSAEWOULDBLOCK) && (nError != 0))
{
    JBS::reportSocketError(sendResult, "client send()");
    shutdown(theSocket,2);
    closesocket(theSocket);
    WSACleanup();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文