我有一个WinSock服务器设置,它正确地接受客户端并传递适当的信息。服务器接收两个客户机,接收一个256字节的固定大小的缓冲区,存储它,然后将另一个缓冲区转发给客户机。(即。Client1发送它的缓冲区,服务器保存它,然后向Client1发送client2的缓冲区)。
每当client1更改其缓冲区时,client2大约需要4秒才能接收到更改。如果client2进行了更改,client1几乎立即接收到更新(少于0.1s)。
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();
}
我将您的代码粘贴在下面,其中包含一些内联注释,主要是因为我无法将其全部合理地放入注释中。如何确定从client1到client2的更改需要4秒钟?目视检查吗?这是否意味着Client1 &Client2在同一台机器上运行(没有不同的网络延迟问题需要担心)?
我突出显示了一些看起来不对的块。他们可能不是,这可能是因为你试图简化你发布的代码,你错过了一些位。对于您可能希望在哪些地方添加日志记录,我还提出了一些建议。如果套接字确实是非阻塞的,那么您应该很快从所有调用中返回并且无法读取数据,除非客户端已经发送了数据。如果你有4秒的延迟,那么问题可能是:
- 客户端还没有发送…内格尔在客户端禁用了吗?如果是这样的话,我预计会发生连续的呼叫接收,没有数据。
- 接收呼叫耗时太长…套接字是否真的处于非阻塞模式?
- 发送呼叫耗时太长…套接字是否处于非阻塞模式,它是否被缓冲,客户端是否试图接收数据?
记录每段代码所花费的时间将有助于找到问题所在。
你可以得到时间,使用像这样的东西(从网上借来的):
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();
}