我在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;
}

08-27 21:33
查看更多