Reputation: 68
I'm working on a server part of my app and I came across a problem I can't seem to solve. The server initialization function, which is a part of ConnectionManager
class, is as follows:
int ConnectionManager::init_server() {
// Log
OutputDebugString(L"> Initializing server...\n");
// Initialize winsock
WSAData wsa;
int code = WSAStartup(MAKEWORD(2, 2), &wsa);
if (code != 0) {
// Error initializing winsock
OutputDebugString(L"> Log: WSAStartup()\n");
output_error(code);
return -1;
}
// Get server information
struct addrinfo hints, *serverinfo, *ptr;
SOCKET sockfd = INVALID_SOCKET;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_protocol = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
if (getaddrinfo(NULL, PORT, &hints, &serverinfo) != 0) {
// Error when getting server address information
OutputDebugString(L"> Log: getaddrinfo()\n");
output_error(WSAGetLastError()); // Call Cleanup?
return -1;
}
for (ptr = serverinfo; ptr != NULL; ptr = ptr->ai_next) {
// Create socket
if ((sockfd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol)) == INVALID_SOCKET) {
// Error when creating a socket
OutputDebugString(L"> Log: socket()\n");
output_error(WSAGetLastError()); // Call Cleanup?
continue;
}
// Set options
const char enable = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) == SOCKET_ERROR) {
// Error when setting options
OutputDebugString(L"> log: setsockopt()\n");
output_error(WSAGetLastError()); // call cleanup?
if (closesocket(sockfd) != 0) {
output_error(WSAGetLastError());
}
return -1;
}
// Bind socket
if (bind(sockfd, ptr->ai_addr, ptr->ai_addrlen) == SOCKET_ERROR) {
// Error on binding
OutputDebugString(L"> Log: bind()\n");
output_error(WSAGetLastError()); // Call Cleanup?
if (closesocket(sockfd) != 0) {
output_error(WSAGetLastError());
}
continue;
}
break;
}
freeaddrinfo(serverinfo);
if (ptr == NULL) {
OutputDebugString(L"Error: Failed to launch server.\n");
return -1;
}
// Listen
if (listen(sockfd, BACKLOG) == SOCKET_ERROR) {
OutputDebugString(L"> Log: listen()\n");
output_error(WSAGetLastError()); // Call Cleanup?;
return -1;
}
// Accept
struct sockaddr_storage clientinfo;
int size = sizeof(struct sockaddr_storage);
m_exchfd = accept(sockfd, (struct sockaddr *)&clientinfo, &size);
if (m_exchfd = INVALID_SOCKET) {
// Error when accepting
OutputDebugString(L"> Log: accept()\n");
output_error(WSAGetLastError()); // Call Cleanup?
if (closesocket(sockfd) != 0) {
output_error(WSAGetLastError());
}
return -1;
}
m_isConnected = true;
return 0;
}
The output_error
function simply prints the message corresponding to the error using FormatMessage()
function. However, I get the following output:
> Log: listen()
> ERROR: The attempted operation is not supported for the type of object referenced.
Thus, the error should be caused by the call to listen()
which is confusing. Could anyone explain me what's the cause of the problem? I bet it should be something easy to fix but I just don't seem to see it.
Upvotes: 0
Views: 2103
Reputation: 596246
The root of your problem is when calling getaddrinfo()
, you are filling out the hints
structure incorrectly.
You are assigning SOCK_STREAM
to the ai_protocol
field, and leaving the ai_socktype
field set to 0. SOCK_STREAM
is defined as 1, which is the same value as IPPROTO_ICMP
, which is commonly used with SOCK_DGRAM
sockets. So, getaddrinfo()
is likely returning addrinfo
entries whose ai_socktype
field is set to SOCK_DGRAM
. You can't use listen()
on a datagram socket, thus the WSAEOPNOTSUPP
error you are seeing:
If no error occurs,
listen
returns zero. Otherwise, a value ofSOCKET_ERROR
is returned, and a specific error code can be retrieved by callingWSAGetLastError
....
WSAEOPNOTSUPP
The referenced socket is not of a type that supports thelisten
operation.
You need to assign SOCK_STREAM
to the hints.ai_socktype
field instead, and set the hints.ai_protocol
field to either 0 or IPPROTO_TCP
(preferably the latter).
Also, getaddrinfo()
returns an error code, just like WSAStartup()
does. Don't use WSAGetLastError()
to get its error code.
Aside from that, I see a number of other issues with your code.
SO_REUSEADDR
requires a BOOL
(4 byte integer), not a char
(1 byte). You are passing in a pointer to a single char
, but are telling setsockopt()
that you are passing in a pointer to an int
. setsockopt()
will end up trying to read a value from stack memory you don't own.
When your loop calls closesocket()
, you should reset sockfd
to INVALID_SOCKET
as well, and then after the loop you should check for that condition instead of checking ptr
for NULL.
You should be calling listen()
inside your loop instead of after it. Just because you bind()
a socket successfully does not guarantee you can open its assigned listening port. You should keep looping until you actually open a listening port successfully. You might also consider adding an extra log message after the loop to know which local IP/Port pair is actually listening, so you know what clients are then able to connect()
to.
When calling WSAGetLastError()
, always call it immediately after a failed Winsock call. If you call anything else beforehand, you risk resetting the error code, since WSAGetLastError()
is just an alias for GetLastError()
, which many APIs use.
When calling accept()
, you are using the =
assignment operator instead of the ==
comparison operator when checking if m_exchfd
is equal to INVALID_SOCKET
. Even after you fix that, if accept()
succeeds, you are leaking sockfd
since you lose track of it and don't call closesocket()
on it. If you are expecting only 1 client to connect, close the listening socket after the client is accepted. Otherwise, store the listening socket in your class and close it after you close the accepted client socket.
With all of that said, try something more like this:
void OutputWinsockError(LPCWSTR funcName, int errCode)
{
std::wostringstream msg;
msg << L"> Log: " << funcName << L"()\n";
OutputDebugStringW(msg.str().c_str());
output_error(errCode);
}
void OutputWinsockError(LPCWSTR funcName)
{
OutputWinsockError(funcName, WSAGetLastError());
}
int ConnectionManager::init_server() {
// Log
OutputDebugString(L"> Initializing server...\n");
// Initialize winsock
WSAData wsa;
int err = WSAStartup(MAKEWORD(2, 2), &wsa);
if (err != 0) {
// Error initializing winsock
OutputWinsockError(L"WSAStartup", err);
return -1;
}
// Get server information
struct addrinfo hints, *serverinfo, *ptr;
SOCKET sockfd = INVALID_SOCKET;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
err = getaddrinfo(NULL, PORT, &hints, &serverinfo);
if (err != 0) {
// Error when getting server address information
OutputWinsockError(L"getaddrinfo", err);
// Call Cleanup?
return -1;
}
for (ptr = serverinfo; ptr != NULL; ptr = ptr->ai_next) {
// Create socket
sockfd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (sockfd == INVALID_SOCKET) {
// Error when creating a socket
OutputWinsockError(L"socket");
continue;
}
// Set options
const BOOL enable = TRUE;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&enable, sizeof(BOOL)) == SOCKET_ERROR) {
// Error when setting options
OutputWinsockError(L"setsockopt");
if (closesocket(sockfd) == SOCKET_ERROR) {
// Error when closing socket
OutputWinsockError(L"closesocket");
}
sockfd = INVALID_SOCKET;
continue;
}
// Bind socket
if (bind(sockfd, ptr->ai_addr, ptr->ai_addrlen) == SOCKET_ERROR) {
// Error on binding
OutputWinsockError(L"bind");
if (closesocket(sockfd) == SOCKET_ERROR) {
// Error when closing socket
OutputWinsockError(L"closesocket");
}
sockfd = INVALID_SOCKET;
continue;
}
// Listen on port
if (listen(sockfd, BACKLOG) == SOCKET_ERROR) {
// Error on listening
OutputWinsockError(L"listen");
if (closesocket(sockfd) == SOCKET_ERROR) {
// Error when closing socket
OutputWinsockError(L"closesocket");
}
sockfd = INVALID_SOCKET;
continue;
}
break;
}
freeaddrinfo(serverinfo);
if (sockfd == INVALID_SOCKET) {
OutputDebugString(L"Error: Failed to launch server.\n");
// Call Cleanup?
return -1;
}
// Accept
struct sockaddr_storage clientinfo;
int size = sizeof(clientinfo);
m_exchfd = accept(sockfd, (struct sockaddr *)&clientinfo, &size);
if (m_exchfd == INVALID_SOCKET) {
// Error when accepting
OutputWinsockError(L"accept");
if (closesocket(sockfd) == SOCKET_ERROR) {
OutputWinsockError(L"closesocket");
}
// Call Cleanup?
return -1;
}
m_isConnected = true;
// is not storing sockfd, close it
// m_listenfd = sockfd;
if (closesocket(sockfd) == SOCKET_ERROR) {
OutputWinsockError(L"closesocket");
}
return 0;
}
Upvotes: 1