问题描述
希望你今天过得愉快.另一个套接字问题,改天:)
我终于安装了 MicroSoft Visual C++ (MSVC++) IDE,加上 Platform SDK,所以我可以编译 winsock 应用程序了.
这里漏掉了一大块东西.在 ServerSocket::accept() 函数中,它创建了一个 ClientSocket 的新实例,并将它的套接字文件描述符设置为接受()ed 的那个,我也在那里检查过,它识别出描述符在那里也是有效的.
在我的 ClientSocket::recv() 函数中,我(显然)从 winsock 库中调用了 recv() 函数.我遇到的问题是我使用的套接字描述符被 recv() 识别为无效,但仅在从我的 ServerSocket::accept() 返回的服务器端 ClientSocket 实例上 - 客户端 ClientSocket 实例没有问题.我插入了多个调试语句,描述符有效.
最奇怪的是,如果我在 Windows 上使用 MinGW gcc/g++ 编译这个 完全相同的代码,它运行良好!只有使用MSVC++才会出现这个问题.
string ClientSocket::recv(int bufsize) {if (!isConnected()) throw SocketException("Not connected.");cout<<"SocketRecv:" <<(sockfd == INVALID_SOCKET) <<" " <<袜子<<结束;向量缓冲区(缓冲区大小+1,0);cout<<SocketRecv1:" <<(sockfd == INVALID_SOCKET) <<" " <<袜子<<结束;int ret = ::recv(sockfd, &buffer[0], bufsize, 0);cout<<SocketRecv2:" <<(sockfd == INVALID_SOCKET) <<" " <<袜子<<结束;//由于无效"套接字描述符,ret 显然是 -1,但是//以上语句在 (sockfd == INVALID_SOCKET) 上打印零(假)!:\如果 (ret
附加信息:套接字处于阻塞模式,即尚未设置为非阻塞.我已经成功调用了 WSAStartup().这发生在服务器端,在从我的 ServerSocket::accept() 返回的 ClientSocket 实例上(是的,我也检查了那里的描述符 - 很好).客户端声明WSACONNRESET (10054)"或WSAECONNABORTED (10053)".
我想不出还有什么可能是错误的.最糟糕的是,在 Windows 和 linux 上使用 MinGW gcc/g++ 都可以正常工作.
如果您想查看整个库,请粘贴在:(注意:600 多行!)
Socket.cxx - http://paste.pocoo.org/show/353725/
Socket.hxx - http://paste.pocoo.org/show/353726/>
谢谢!!!
更新 - 根据 Ben 的解决方案,我现在使用:void ServerSocket::accept(ClientSocket& sock);
,并实现为:ClientSocket mysock;server.accept(mysock);
非常感谢!!!
看起来您没有遵循 三规则.任何时候有析构函数时,都需要编写或禁用复制构造函数和赋值运算符.
在您的示例用法中:
ClientSocket 客户端 = server.accept();
变量 client
是从返回值复制构造的.然后析构函数在临时变量上运行,关闭套接字.
在 C++0x 中,您可以添加移动构造函数并解决此问题.现在,您应该实现 swap
并使用它:
ClientSocket 客户端;server.accept().swap(client);
或者通过client
作为server.accept
的参数:
ClientSocket 客户端;服务器.接受(客户端);
您可以按照 auto_ptr
的风格为 ClientSocket
编写移动复制构造函数,但我不建议这样做.人们不希望复制构造函数窃取资源.
hope you're having a good day. Another socket issue, another day :)
I finally got MicroSoft Visual C++ (MSVC++) IDE installed, plus the Platform SDK, so I can compile winsock applications.
Missed a chunk of stuff here. In the ServerSocket::accept() function, it creates a new instance of ClientSocket and sets it's socket file descriptor to the one that was accept()ed, I have also checked there and it's recognizing that the descriptor is valid there as well.
In my ClientSocket::recv() function, I call (obviously) the recv() function out of the winsock library. The issue I am having is that the socket descriptor I am using is being recognized by recv() as invalid, but only on the server-side ClientSocket instance returned from my ServerSocket::accept() - The client-side ClientSocket instance has no problems. I inserted multiple debug statements, the descriptor is valid.
The weirdest bit about this is that if I compile this exact same code with MinGW gcc/g++ on windows, it runs fine! It's only using MSVC++ that this problem occurs.
string ClientSocket::recv(int bufsize) {
if (!isConnected()) throw SocketException("Not connected.");
cout << "SocketRecv: " << (sockfd == INVALID_SOCKET) << " " << sockfd << endl;
vector<char> buffer(bufsize+1, 0);
cout << "SocketRecv1: " << (sockfd == INVALID_SOCKET) << " " << sockfd << endl;
int ret = ::recv(sockfd, &buffer[0], bufsize, 0);
cout << "SocketRecv2: " << (sockfd == INVALID_SOCKET) << " " << sockfd << endl;
// ret is apparently -1 because of "invalid" socket descriptor, but the
// above statements print zero (false) on the (sockfd == INVALID_SOCKET) ! :\
if (ret < 0) {
#ifdef _WIN32
switch((ret = WSAGetLastError())) {
#else
switch(errno) {
#endif
case DECONNREFUSED: // The 'd' prefix means _I_ defined it, i.e. from windows it's
// set to 'WSAECONNREFUSED', but from linux it's set to 'ECONNREFUSED'
throw SocketException("Connection refused on recover.");
break;
case DENOTCONN:
throw SocketException("Not connected.");
break;
case DECONNABORTED:
throw SocketException("Software caused connection abort.");
break;
case DECONNRESET:
throw SocketException("Connection reset by peer.");
break;
default:
//usually this itoa() and char/string stuff isn't here... needed it in
//order to find out what the heck the problem was.
char tmp[21];
string tmp4 = "Unknown error reading socket. ";
string tmp3 = tmp4 + itoa(ret, tmp, 10);
//this throw keeps throwing "Unknown error reading socket. 10038"
throw SocketException(tmp3);
break;
}
} else if (ret == 0) {
connected = false;
return "";
}
return &buffer[0];
}
Additional information: The socket is in blocking mode, i.e. has not been set to non-blocking. I have called WSAStartup() successfully. This is happening on the server side, on the ClientSocket instance returned from my ServerSocket::accept() (yes, I checked the descriptor there too - it's fine). The client side claims 'WSAECONNRESET (10054)' or 'WSAECONNABORTED (10053)'.
I can't think of anything else that could be wrong. The worst part is, it works fine using MinGW gcc/g++ on windows and linux both.
If you want to see the whole library, it's pasted at: (caution: 600+ lines!)
Socket.cxx - http://paste.pocoo.org/show/353725/
Socket.hxx - http://paste.pocoo.org/show/353726/
Thanks!!!
Update - As per Ben's solution, I am now using: void ServerSocket::accept(ClientSocket& sock);
, and implementing as: ClientSocket mysock; server.accept(mysock);
Thank you so much!!!
Looks like you're not following the Rule of Three. Any time you have a destructor, you need to write or disable both the copy-constructor and assignment operator.
In your example usage:
ClientSocket client = server.accept();
The variable client
is copy-constructed from the return value. Then the destructor runs on the temporary variable, closing the socket.
In C++0x, you can add a move-constructor and cure this problem. For now, you should implement swap
and use it:
ClientSocket client;
server.accept().swap(client);
Or pass client
as a parameter of server.accept
:
ClientSocket client;
server.accept(client);
You could write a moving copy-constructor for ClientSocket
, in the style of auto_ptr
, but I wouldn't recommend that. People don't expect a copy-constructor to steal resources.
这篇关于Winsock accept() 返回 WSAENOTSOCK(代码 10038)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!