【问题标题】:Winsock invalid received byte numberWinsock 接收到的字节数无效
【发布时间】:2015-04-26 11:11:20
【问题描述】:

在尝试验证接收到的字节数时,我目前面临Winsock 相关问题。

在我的应用程序中,我使用非阻塞套接字来抛出我自己的 Timeout 异常。这里是Winsock的初始化代码:

// Initialize Winsock
WSAData winSockData;
WORD dllVersion = MAKEWORD(2, 1);
long iResult = WSAStartup(dllVersion, &winSockData);
if (iResult != NO_ERROR)
    return;

addrinfo* serverAddress = nullptr;
addrinfo* ptr = nullptr, hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

iResult = getaddrinfo("127.0.0.1", "10011", &hints, &serverAddress);
if (iResult != 0)
    return;

// Create communication socket
SOCKET connectSocket = socket(AF_INET, SOCK_STREAM, /*IPPROTO_TCP*/0);
if (connectSocket == INVALID_SOCKET)
    return;

// Establish connection to server
iResult = connect(connectSocket, serverAddress->ai_addr, (int)serverAddress->ai_addrlen);
if (iResult == SOCKET_ERROR)
    return;

// Set socket to non-blocking
ULONG mode = 1;
m_iResult = ioctlsocket(m_connectSocket, FIONBIO, &mode);
if (m_iResult == SOCKET_ERROR)
    return;

// server address info not needed anymore
freeaddrinfo(serverAddress);

应用程序首先连接到给定的 telnet 服务器,发送请求,然后等待相应的响应消息。

传入的数据存储在一个适当的 char 数组中,称为 m_serverMsg(固定大小为 BUFSIZE,当前设置为 4200 字节)。

如果检索服务器响应消息需要很长时间(g_requestTimeout 中存储的最大允许时间),我自己的 TimeoutException 会被抛出。

如果在消息缓冲区中找到换行符 (\r\n),则给定服务器消息已结束。

但是,用于检索服务器响应消息的以下部分未按预期工作。预期的传入数据字节为1,040,但实际字节始终为3,435,973,836

unsigned long long startTime = getWallTimeInMs();
bool retry = true;
unsigned int receivedBytes = 0;
// Fetch server response
do
{

    try
    {

        m_iResult = fetchMessage(m_connectSocket, m_serverMsg, int(BUFSIZE - receivedBytes));
        if (m_iResult == SOCKET_ERROR)
            throw std::exception();

        if (m_iResult > 0)
        {
            for (unsigned int i = receivedBytes; i < receivedBytes + m_iResult; i++)
            {
                char* eol = strstr(m_serverMsg, "\r\n");
                if (eol)
                {
                    retry = false;
                    break;
                }
            }

            receivedBytes += m_iResult;
        }
    }
    catch (CommException e)
    {
        // in case there's a short delay in retrieving the server response message
        if (getWallTimeInMs() - startTime < g_requestTimeout)
            Sleep(20);
        else
            // response timeout exceeded; connection has been lost
            throw CommException("server not responding in time.", CommProblem::ConnectionLost);
    }
    catch (std::exception& e)
    {
        std::cout << "There was a socket related problem." << std::endl;
        std::cout << "Error code: " << WSAGetLastError() << std::endl;
    }

} while (retry);

这就是函数fetchMessage() 的样子:

int CommCenter::fetchMessage(SOCKET socket, char* buffer, int bufSize, int flags)
{
    int result = recv(socket, buffer, bufSize, flags);
    // if operation couldn't be executed (e.g. because server is down),
    // throw according exception
    if (result == SOCKET_ERROR)
    {
        if (WSAGetLastError() == WSAEWOULDBLOCK)
            throw CommException("delay in connection.", CommProblem::Delay);
        else throw;
    }
    return result;
}

【问题讨论】:

  • 显示fetchMessage。你在m_iResult 中有3,435,973,836?什么时候? break 只会停止你 for 循环,而不是 do-while 循环。
  • 您的错误在 fetchMessage 中。 3,435,973,836 是十六进制的 0xcccccccc。编译器在调试模式下使用此值来填充未初始化的变量。如果您需要帮助,请显示 fetchMessage。
  • 在调试窗口中显示m_serverMsgcontent
  • @NikolayKondratyev m_iResult 是一个类成员变量,每次在 try{} 块中都会被赋值:m_iResult = fetchMessage(...);
  • \r\n 不是“回车符”,它是两个字节的 CR/LF 序列。注意 EWOULDBLOCK 上没有“连接延迟”,将其与“连接丢失”相关联也是非常不正确的。

标签: c++ c++11 winsock


【解决方案1】:

这部分

for (unsigned int i = receivedBytes; i < receivedBytes + m_iResult; i++)
{
    if (m_serverMsg[i] == '\r\n')
    {
        retry = false;
        break;
    }
}

不正确,'\r\n'"\r\n"不一样,所以不能这样比较。

要纠正它,请使用 strstr 这样的函数

char* eol = strstr(m_serverMsg, "\r\n");
if(eol != NULL)
{
    retry = false;
    break;
}

或将m_serverMsg 转换为std::string 并使用find 方法

if(std::string(m_serverMsg, receivedBytes, m_iResult).find("\r\n") != std::string::npos)
{
    retry = false;
    break;
}

【讨论】:

  • 很高兴知道,谢谢。但是接收的字节数还是有问题。
  • @neuronal.bit 所以3,435,973,836fetchMessage调用后m_iResult的值?
  • 没错。如果fetchMessage() 本身抛出异常,它会在接收到的字节数可以分配给m_iResult 之前被捕获。因此m_iResult 应始终包含实际接收到的字节数,以防发生错误。
  • @neuronal.bit 尝试添加调试输出,例如printf("recv returned %d\n", result ); in fetchMessagerecv 调用之后。
  • 有趣的是,在fetchMessage() 中返回值是356,但是当分配给m_iResult 时它是3,435,973,836m_iResult 的类型为 int
猜你喜欢
  • 2021-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多