使用 Winsock2 进行多重 Send() 和 Recv()

发布于 2024-09-11 08:33:22 字数 4560 浏览 4 评论 0原文

我正在使用 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,但是,我无法发送PONG 没有首先处理 PING 请求,这意味着我需要能够在连接关闭之前读取输出。有什么想法吗?

I am working on a small networking project using Winsock2. I am using a TCP connection and actually am working with IRC as an example since IRC is fairly simple. What I am doing is connecting to the server and sending an initial buffer so the server recognizes a connection. This works fine.

What concerns me is that I cannot write to the socket again. It seems my program hangs if I do not use shutdown() (on SD_SEND) after I send the initial buffer.

So the next data (based on RFC 1459) I want to send is the USER and NICK information, however, I feel like using shutdown() is what is causing my current issue. Is there a way to reinitialize the write socket?

Thanks!

ADDED CODE

Note that these are located within a class so it still may be slightly obscured. I am writing it into a simpler example using the elements I have. Everything is properly defined, so if I forget to define things, I apologize, but many of my recurring variables are defined for the scope of the class.

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

I hope that clarifies things!

EDIT

I think I have figured out what is going wrong. I made the corrections listed below to my code; thanks guys. However, it's my read loop which is messing with things. Even after it has all the information it seems that it is waiting for the connection to be closed before it sends the output. Any ideas? My output currently looks like this (the bytes written/total is something I added to make sure everything was going down the wire correctly)

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

So it appears to have timed out because the PING did not receive PONG in time, however, I cannot send the PONG without first processing the PING request which means I would need to be able to read the output before the connection is closed. Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

拿命拼未来 2024-09-18 08:33:22

我可以推荐一份关于这个主题的有趣文件吗? Beej网络编程指南的第6章和第7章

有几个例子。

May I suggest a fun document on the subject? Chapter's 6 and 7 of Beej's Guide to Network Programming

It has several examples.

风筝有风,海豚有海 2024-09-18 08:33:22

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

shutdown() 的调用非常可疑。您为什么预计需要这样做?关闭套接字是在连接完成时执行的操作,而不是在刚开始连接时执行的操作。您应该完全删除它。

我不确定 recvbuf 是什么类型,但看起来您使用的方式不正确。可以附加到 std::string 的东西可能也不能调用 ZeroMemory(),除非其中之一出错。您也没有使用iResult,它是从服务器接收到的实际字节数。

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

There shouldn't be any need to send an "initial buffer" like you've done. The server will receive notification when a client connects, it doesn't depend on the client actually sending anything. (And in particular, the IRC protocol says that the server will start sending you stuff as soon as you connect.)

The call to shutdown() is highly suspicious. Why did you expect to need to do this? Shutting down a socket is something you do when you're done with the connection, not when you're just starting. You should remove this completely.

I'm not sure what type recvbuf is, but it looks like you're using it incorrectly. Something that can be appended to a std::string probably can't also have ZeroMemory() called on it, without one or the other of those being wrong. You also aren't using iResult which is the actual number of bytes received from the server.

Your write function also contains a call to shutdown(), which you should remove.

甲如呢乙后呢 2024-09-18 08:33:22

根据 man 发送(2)

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

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

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

我强烈建议使用 Wireshark< /a> 这样你就可以检查线路上发生了什么

According to man send(2)

On success, these calls return the
number of characters sent. On error,
-1 is returned, and errno is set appropriately.

What happens is probably that send does not send the full buffer at once, you must use a loop around it.

This might not be your actual problem however since you,re sending an empty string...

I'd highly recommend using Wireshark so you can check what goes down to the wire

椵侞 2024-09-18 08:33:22
data += recvbuf;

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

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

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

data += recvbuf;

This can't work. There's no way string::operator+= to know how many bytes have been received. This function expects a C-style string, not an arbitrary chunk of bytes.

But you also have a very fundamental design problem. You're expecting your program to speak the IRC protocol, but it contains no implementation of that protocol whatsoever. For example, the IRC protocol specifies a particular way that messages are delimited, and you have no code whatsoever to parse those messages.

As a result, your transition from reading to writing occurs at essentially a random time determined by the vagaries of TCP timing and how the server chooses to segment its output. Since the server is permitted to segment its output however it pleases (the protocol is clear that the client cannot rely on segmentation to parse the protocol but instead must rely on the line-oriented nature), your program's behavior is unpredictable.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文