Multipe Send()和Recv()使用Winsock2

我正在使用Winsock2开展一个小型networking项目。 我正在使用TCP连接,实际上正在使用IRC作为例子,因为IRC非常简单。 我正在做的是连接到服务器并发送初始缓冲区,以便服务器识别连接。 这工作正常。

我担心的是我不能再次写入套接字。 看来我的程序挂起,如果我没有使用shutdown()(在SD_SEND)发送初始缓冲区后。

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

谢谢!

添加的代码

请注意,这些是位于一个类,所以它仍然可能会被略微模糊。 我正在使用我拥有的元素将其写入一个更简单的示例。 一切都是正确的定义,所以如果我忘记定义的东西,我很抱歉,但我的循环variables的许多定义为类的范围。

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

我希望澄清事情!

编辑

我想我已经找出了错在哪里。 我将下面列出的更正作为我的代码; 多谢你们。 但是,这是我的阅读循环与事情搞乱。 即使它有所有的信息,似乎它正在等待连接被closures,然后才发送输出。 有任何想法吗? 我的输出目前看起来像这样(写入的字节数/总数是我添加的东西,以确保一切正确的线下)

 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,这意味着在连接closures之前我需要能够读取输出。 有任何想法吗?

Solutions Collecting From Web of "Multipe Send()和Recv()使用Winsock2"

不需要像你所做的那样发送一个“初始缓冲区”。 服务器会在客户端连接时收到通知,不依赖于实际发送任何东西的客户端。 (特别是,IRC协议说服务器会在你连接的时候开始发送你的东西。)

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

我不知道什么类型的recvbuf是,但它看起来像你不正确地使用它。 一些可以追加到std::string可能不能有ZeroMemory()调用,没有一个或那些错误的。 您也没有使用从服务器收到的实际字节数iResult

你的写函数也包含了一个对shutdown()的调用,你应该删除它。

我可以建议一个关于这个问题的有趣文件吗? Beej的“网络编程指南”第6章和第7章

它有几个例子。

据男子发(2)

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

发生什么事情可能是发送不会立即发送完整的缓冲区,您必须使用一个循环。

这可能不是你真正的问题,但是因为你重发一个空字符串…

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

 data += recvbuf; 

这不能工作。 没有办法string::operator+=知道有多少字节已经收到。 这个函数需要一个C风格的字符串,而不是任意的字节块。

但是你也有一个非常基本的设计问题。 你期待你的程序说IRC协议,但它不包含任何协议的实现。 例如,IRC协议指定了一个特定的消息分隔方式,并且没有任何代码可以解析这些消息。

因此,从阅读到写作的过渡基本上是随机的,由TCP时间的变化决定,以及服务器如何选择分段输出。 由于允许服务器对输出进行分段,所以它很令人满意(协议很明显,客户端不能依靠分段来解析协议,而是依赖于面向行的特性),所以程序的行为是不可预知的。