我在Windows 8.1上的winsock遇到问题,其中recv一直随机返回0。我在同一台计算机上同时运行客户端和服务器(因此所有流量都指向回送地址),并且客户端和服务器上的任何语句都有断点,这会关闭套接字。但是,当问题发生时,服务器仍然可以正常运行,并且没有关闭任何东西,而客户端遇到的断点仅在recv返回0或更少时触发。
当服务器VS 2013项目设置为编译为Windows程序(而不是控制台,以便使其不产生任何窗口)时,客户端会不断从recv中随机返回0(尽管总是在我的代码中的同一点)。保持沉默)。当我将服务器编译为控制台应用程序时,似乎没有出现该错误,因为我一直在以该模式调试程序,并且仅在切换编译设置时才遇到此问题。
作为Windows应用程序编译时,有什么方法可以启动控制台窗口,以便我可以看到任何调试语句?
Winsock在为控制台进行编译时与在Windows应用程序中进行编译时行为是否有所不同?
当我没有从服务器发送关闭信号时,为什么客户端的recv返回0?
我的代码,询问是否还需要:
客户
void recvLoop()
{
int recievedBytes = 1;
while (running && recievedBytes > 0)
{
WORD dataSize;
WORD dataType;
int recievedBytesA = ConnectSock.Recieve(&dataSize, sizeof(WORD));
if (recievedBytesA <= 0)
{
closing = true; //breakpoint set here
attemptKillThreads();
continue;
}
int recievedBytesB = ConnectSock.Recieve(&dataType, sizeof(WORD));
if (recievedBytesB <= 0)
{
closing = true; //breakpoint set here
attemptKillThreads();
continue;
}
unique_ptr<char[]> data(new char[dataSize]);
int recievedBytesC = ConnectSock.Recieve(data.get(), dataSize);
if (recievedBytesC <= 0)
{
closing = true; //breakpoint set here - Always triggers here
attemptKillThreads();
continue;
}
//use the received data.....
}
}
如果中断,recipedBytesA = 2,recipedBytesB = 2,recipedBytesC = 0,dataType = 0,dataSize = 0
ConnectSock是ConnectSocket类型的全局变量。这是它的Recieve()
int ConnectSocket::Recieve(void *recvbuf, int recvbuflen)
{
if (sock != INVALID_SOCKET)
{
int i = recv(sock, (char *)recvbuf, recvbuflen, 0);
if ((i == SOCKET_ERROR))
{
int err = 0;
err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
}
return i;
}
return -2;
}
服务器:
void sendLoop()
{
int bytessent = 0;
QueuePack tosend;
while (running)
{
tosend = sendQueue.Dequeue();
if (tosend.packType == QueuePack::EXIT || tosend.packType == 0 || tosend.dSize == 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(&tosend.dSize, sizeof(WORD));
//cout used to see what exactly is being sent, even if it is garbage when converted to text
cout << tosend.dSize << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(&tosend.packType, sizeof(WORD));
cout << tosend.dSize << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(tosend.bufferPtr(), tosend.dSize);
cout << tosend.bufferPtr() << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
}
}
if (Connection->shutdownSock(SD_SEND) == SOCKET_ERROR)
{
Connection->closeSock();
}
}
SendData实际上是使用reinterpret_cast发送的包装器
int SendData(void * writeBuffer, int bufferSize)
{
return send(SocketManager.clientSocket, reinterpret_cast<char *>(writeBuffer), bufferSize, 0);
}
sendQueue是一个有界的阻塞队列,其中包含QueuePacks
QueuePack用于传输数据,线程的大小以及线程之间的数据类型。客户端和服务器都使用它,因为它可以确保数据到达客户端上正确的线程
Queuepack具有2个公共变量packType和WORD类型的dSize。
QueuePack::QueuePack() : packType(UND), dSize(0)
{
int h = 0; //debug statement to break on - never gets hit after initial collection construction occurs
}
QueuePack::QueuePack(const WORD type, WORD size, char * data) : packType(type), dSize(size)
{
//debug test and statement to break on
if (size == 0 || type == 0)
{
int h = 0; //breakpoint - never gets hit
}
dSize = (dSize < 1 ? 1 : dSize);
_buffer = make_unique<char[]>(dSize);
memcpy(_buffer.get(), data, dSize);
}
QueuePack::QueuePack(QueuePack &other) : packType(other.packType), dSize(other.dSize)
{
//debug test and statement to break on
if (other.dSize == 0 || other.packType == 0)
{
int h = 0; //breakpoint - never gets hit
}
if (&other == this)
{
return;
}
_buffer = make_unique<char[]>(dSize);
other.buffer(_buffer.get());
}
QueuePack QueuePack::operator= (QueuePack &other)
{
// check for self-assignment
if (&other == this)
{
return *this;
}
// reuse storage when possible
if (dSize != other.dSize)
{
_buffer.reset(new char[other.dSize]);
dSize = other.dSize;
}
packType = other.packType;
other.buffer(_buffer.get());
return *this;
}
QueuePack::~QueuePack()
{
}
HRESULT QueuePack::buffer(void* container)
{
try
{
memcpy(container, _buffer.get(), dSize);
}
catch (...)
{
return E_FAIL;
}
return S_OK;
}
char * QueuePack::bufferPtr()
{
return _buffer.get();
}
最佳答案
如果中断,recipedBytesA = 2,recipedBytesB = 2,recipedBytesC = 0,dataType = 0,dataSize = 0
当ConnectSock.Recieve()
为0时,您正在调用dataSize
。没有要读取的内容,因此Receive()
报告读取了0个字节。
您需要为此条件添加检查:
unique_ptr<char[]> data(new char[dataSize]);
if (dataSize != 0) // <-- add this
{
int recievedBytesC = ConnectSock.Recieve(data.get(), dataSize);
if (recievedBytesC <= 0)
{
closing = true;
attemptKillThreads();
continue;
}
}
您的代码还假定
Recieve()
读取了所有请求的字节,但未处理recv()
返回的字节数较少的可能性。因此,您需要在循环中使Recieve()
调用recv()
以确保实际上读取了所请求的所有内容:int ConnectSocket::Recieve(void *recvbuf, int recvbuflen)
{
if (sock == INVALID_SOCKET)
return -2;
char *buf = static_cast<char *>(recvbuf);
int total = 0;
while (recvbuflen > 0)
{
int i = recv(sock, buf, recvbuflen, 0);
if (i == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
return -1;
}
if (i == 0)
{
cout << "disconnected" << endl;
return 0;
}
buf += i;
recvbuflen -= i;
total += i;
}
return total;
}
与
SendData()
相同,因为send()
可能返回的字节数少于请求的字节数:int SendData(void * writeBuffer, int bufferSize)
{
if (SocketManager.clientSocket == INVALID_SOCKET)
return -2;
char *buf = static_cast<char *>(writeBuffer);
int total = 0;
while (bufferSize > 0)
{
int i = send(SocketManager.clientSocket, buf, bufferSize, 0);
if (i == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
return -1;
}
buf += i;
bufferSize -= i;
total += i;
}
return total;
}