【问题标题】:Multipe Send()'s and Recv()'s using Winsock2使用 Winsock2 的多个 Send() 和 Recv()
【发布时间】:2011-03-20 08:22:32
【问题描述】:

我正在使用 Winsock2 进行一个小型网络项目。我正在使用 TCP 连接,实际上以 IRC 为例,因为 IRC 相当简单。我正在做的是连接到服务器并发送一个初始缓冲区,以便服务器识别连接。这很好用。

我担心的是我无法再次写入套接字。如果我在发送初始缓冲区后不使用 shutdown()(在 SD_SEND 上),我的程序似乎会挂起。

所以我要发送的下一个数据(基于 RFC 1459)是 USER 和 NICK 信息,但是,我觉得使用 shutdown() 是导致我当前问题的原因。有没有办法重新初始化写套接字?

谢谢!

添加代码

请注意,这些都位于一个类中,所以它仍然可能会被稍微模糊。我正在使用我拥有的元素将它写成一个更简单的示例。一切都已正确定义,因此如果我忘记定义事物,我深表歉意,但我的许多重复变量都是针对类的范围定义的。

int main(int argc,char *argv[])
{
        int iResult;
        SOCKET Connection;
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if(iResult != 0)
        throw "Startup failed!";

    // Prep stuff
    ZeroMemory(&hints,sizeof(hints)); // This struct is defined addrinfo
    hints.ai_family   = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // Now resolve server addr
    iResult = getaddrinfo(argv[1],argv[2],&hints,&result);
    if(iResult != 0)
        throw "getaddrinfo() failed!";

    // Now try to connect
    for(ptr=result;ptr != NULL;ptr = ptr->ai_next)
    {
        Connection = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); // defined in that "hints" struct. argument number 2
        if(Connection == INVALID_SOCKET)
        {
            freeaddrinfo(result);
            WSACleanup();
            throw "Error at socket();";
        }

        // Connect to server
        iResult = connect(Connection, ptr->ai_addr, (int)ptr->ai_addrlen);
        if(iResult != 0)
        {
            closesocket(Connection);
            Connection = INVALID_SOCKET;
            continue;
        }
        break;
    }

    freeaddrinfo(result);

    // Send initial buffer so server know you're there :)
    iResult = send(Connection, "", 1, 0);
    if(iResult == SOCKET_ERROR)
    {
        close();
        throw "Could not send initial buffer!";
    }

    // Close this connection for the inital buffer
    iResult = shutdown(Connection, SD_SEND);
    if(iResult == SOCKET_ERROR)
    {
        close();
        throw "Could not close initial buffer socket!";
    }

        bool connected = true;

        // This is taken from my read function within the class
        // BEGIN READ FUNCTION
    iResult = 0; // Reset
    std::string data = ""; // Capture the output and send it all at once!

    // This only works if we're connected sweet cakes <3
    if(connected)
    {
        do
        {
            iResult = recv(socket, recvbuf, BUFLEN, 0);
            if(iResult > 0)
            {
                // Working properly
                // Save all data even if there is more than BUFLEN sent
                continue;
            }
            else if(iResult == 0)
                // Connection closed properly
                break;
            else
                printf("ERROR!");
        } while(iResult > 0);
    }
    data += recvbuf;
    ZeroMemory(&recvbuf,sizeof(recvbuf));
        // Function returns std::string but essentially this is what happens
        printf("%s",data.c_str());
        // END READ FUNCTION 

        // BEGIN WRITE FUNCTION
    iResult = 0; // Reset
        SOCKET socket = Connection; // Write function arg 1
        char *data; // Write function arg 2

    iResult = send(socket,data,(int)strlen(data),0);
    if(iResult == SOCKET_ERROR)
    {
        close();
        printf("Could not write data: %ld",WSAGetLastError()); 
                return 1;
    }

    // Data sent, let's close the write socket
    iResult = shutdown(socket, SD_SEND);
    if(iResult != 0)
    {
        close();
        printf("Could not close write socket!");
                return 1;
    }

    //return iResult;
        // END WRITE FUNCTION

        // Now that will produce "Could not write data: 0" for any value of data
        // So realistically I want to send the USER and NICK data, then read 
        // and probably process a PING string from the server and send my PONG response

        return 0;
}

我希望能澄清一些事情!

编辑

我想我已经弄清楚出了什么问题。我对我的代码进行了下面列出的更正;多谢你们。但是,这是我的阅读循环弄乱了事情。即使在它拥有所有信息之后,它似乎也在等待连接关闭,然后再发送输出。有任何想法吗?我的输出目前看起来像这样(写入的字节数/总字节数是我添加的,以确保一切正常)

Bytes Written: 41
Bytes Total: 41
Data: ERROR :Closing Link: raged123[127.0.0.1] 6667 (Ping timeout)
...
:irc.foonet.com NOTICE AUTH :*** Found your hostname (cached)
PING :2ED39CE5
[A bunch of funny characters]WinSock 2.0

所以它似乎已经超时,因为 PING 没有及时收到 PONG,但是,如果不先处理 PING 请求,我就无法发送 PONG,这意味着我需要能够在连接关闭之前读取输出.有什么想法吗?

【问题讨论】:

  • 请展示一个简短的示例来演示您遇到的问题。否则,我们就没有全貌。

标签: c++ windows sockets winsock


【解决方案1】:

我可以推荐一个关于这个主题的有趣文件吗? Beej's Guide to Network Programming第6、7章

它有几个例子。

【讨论】:

    【解决方案2】:
    data += recvbuf;
    

    这行不通。 string::operator+= 无法知道收到了多少字节。这个函数需要一个 C 风格的字符串,而不是任意的字节块。

    但是你也有一个非常基本的设计问题。您希望您的程序使用 IRC 协议,但它不包含该协议的任何实现。例如,IRC 协议指定了一种特定的消息分隔方式,而您没有任何代码来解析这些消息。

    因此,您从读取到写入的转换基本上发生在随机时间,这取决于 TCP 时序的变幻莫测以及服务器如何选择分段其输出。由于服务器可以随意分段其输出(协议很清楚,客户端不能依靠分段来解析协议,而是必须依靠面向行的性质),因此您的程序的行为是不可预测的。

    【讨论】:

      【解决方案3】:

      根据 man send(2)

      成功时,这些调用返回 发送的字符数。出错时, 返回-1,并适当设置errno。

      可能发生的情况是 send 不会立即发送完整的缓冲区,您必须在它周围使用循环。

      这可能不是您的实际问题,但是因为您正在发送一个空字符串...

      我强烈建议您使用Wireshark,这样您就可以检查线路发生了什么

      【讨论】:

        【解决方案4】:

        应该不需要像您所做的那样发送“初始缓冲区”。当客户端连接时,服务器将收到通知,它不依赖于客户端实际发送任何内容。 (特别是,IRC 协议规定服务器将在您连接后立即开始向您发送内容。)

        shutdown() 的调用非常可疑。为什么您期望需要这样做?关闭套接字是您完成连接后所做的事情,而不是刚开始时。您应该完全删除它。

        我不确定recvbuf 是什么类型,但您似乎使用不正确。可以附加到std::string 的东西可能也不能同时调用ZeroMemory(),而其中一个或另一个是错误的。您也没有使用iResult,这是从服务器接收到的实际字节数。

        您的 write 函数还包含对 shutdown() 的调用,您应该删除它。

        【讨论】:

        • 啊,你是对的。我将 recvbuf 从 char recvbuf[LENGTH] 更改为 char recvbuf,抱歉。我删除了“shutdown()'s”,但得到了相同的输出。现在程序不会无限期地挂起,但是,它需要大约 20 秒才能输出一条读取消息 10053(软件导致连接中止)。服务器已启动;我正在使用 std IRC 客户端。我还按照下面最后一条评论中的建议添加了一个循环 while(written
        猜你喜欢
        • 2016-07-27
        • 2014-05-05
        • 2019-01-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-17
        • 2020-01-08
        • 1970-01-01
        相关资源
        最近更新 更多