Sockets in Win32The foundation of a computer's social life.
From experience, sockets in C/C++ can be very off-putting. This is "not so" in the case of (near post-) modern languages with a greater level of abstraction (Python, C#, VB.NET, ect, ect) but for good ol' Win32 – you've got your work cut out for you.
All this work is not without its rewards! You can create blocking mode clients, asynchronous load-balanced services, super-high throughput applications and seriously reliable programs using only a few simple functions in WinSock.
As a side note, it should be known that "WinSock" should seriously be called BSDSock as it was an outright copy from BSD – the windows source code even bears the BSD copyright notices (though I've heard through the grape-vine that Microsoft has rewritten the IP-Stack while in the process of building up support for IPv6 – but that is only speculation on my end).
I've highly commented and trimmed down this not-so-fully featured web server for your enjoyment!
(double-click to select in copy/paste friendly fashion)
#include "windows.h"
#include "winsock.h"
#include "stdio.h"
//be sure to include the linker library: wsock32.lib
#define MAX_CONNECTIONS 25
#define MAX_RECV_SIZE 8120
int main(int argc, char *argv[])
{
SOCKET listenSocket = NULL; //The socket that will listen for new connections.
SOCKET peerSockets[MAX_CONNECTIONS];
SOCKET peerBytesSent[MAX_CONNECTIONS];
struct timeval tTimeOut; // The TimeOut structure.
tTimeOut.tv_sec = 1;
tTimeOut.tv_usec = 0;
//These are used to identify if a socket is ready
// for reading, writing or is experiencing an exception.
fd_set fdRead;
fd_set fdWrite;
fd_set fdExcept;
WORD WSARequiredVersion = 0x0101; // Version 1.1
WSADATA WSAData;
WSADATA *WSAPointerData = &WSAData;
if(WSAStartup(WSARequiredVersion, WSAPointerData) != 0)
{
printf("Failed to initialize WinSock: %d\n", WSAGetLastError());
return 1;
}
// Fill In The Address Structure For Local Server
SOCKADDR_IN localSockAddress; // Socket address of local server
localSockAddress.sin_family = AF_INET; // Address Family.
localSockAddress.sin_addr.s_addr = INADDR_ANY; // Library assigned address.
localSockAddress.sin_port = htons(80); // Port Number.
// Create A TCP/IP Stream Socket To "Listen" with.
if((listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
printf("Failed to create socket: %d\n", WSAGetLastError());
WSACleanup();
return 1;
}
// Bind The Name To The Socket.
if(bind(listenSocket, (SOCKADDR*)&localSockAddress, sizeof(struct sockaddr)) == SOCKET_ERROR)
{
printf("Bind failed: %d\n", WSAGetLastError());
closesocket(listenSocket);
WSACleanup();
return 1;
}
// Set The Socket To Listen.
if(listen(listenSocket, MAX_CONNECTIONS) == SOCKET_ERROR)
{
printf("Listen failed: %d\n", WSAGetLastError());
closesocket(listenSocket);
WSACleanup();
return 1;
}
memset(&peerSockets, 0, sizeof(SOCKET) * MAX_CONNECTIONS);
memset(&peerBytesSent, 0, sizeof(int) * MAX_CONNECTIONS);
//---------------------------------------------------------
while(true)
{
// Zero the Read, Write and Except descriptors.
FD_ZERO(&fdRead);
FD_ZERO(&fdWrite);
FD_ZERO(&fdExcept);
//Add the listen socket to the descriptor array.
FD_SET(listenSocket, &fdRead);
FD_SET(listenSocket, &fdExcept);
//Add all connected sockets to the descriptor array.
for(int i = 0; i < MAX_CONNECTIONS; i++)
{
if(peerSockets[i])
{
FD_SET(peerSockets[i], &fdRead);
FD_SET(peerSockets[i], &fdWrite);
FD_SET(peerSockets[i], &fdExcept);
}
}
//Socket Select. This will figure out the disposition
// of all the sockets in the descriptor array.
if(select(-1, &fdRead, &fdWrite, &fdExcept, &tTimeOut) == SOCKET_ERROR)
{
printf("Select failed: %d\n", WSAGetLastError());
closesocket(listenSocket);
break;
}
//Are there any waiting connections?
if(FD_ISSET(listenSocket, &fdRead))
{
int iLength = 0;
SOCKET peerSocket = 0;
SOCKADDR_IN remoteSockAddr;
int iAddressLength = sizeof(SOCKADDR);
//Accept the connection.
peerSocket = accept(listenSocket, (SOCKADDR*)&remoteSockAddr, &iAddressLength);
if(peerSocket == SOCKET_ERROR)
{
printf("Accept failed: %d\n", WSAGetLastError());
closesocket(listenSocket);
break;
}
else if(peerSocket == 0)
{
printf("Accept failed (gracefully): %d\n", WSAGetLastError());
closesocket(listenSocket);
break;
}
bool bFoundFreeSocket = false;
//Find a free socket in the socket array.
for(int i = 0; i < MAX_CONNECTIONS; i++)
{
if(peerSockets[i] == NULL)
{
peerSockets[i] = peerSocket;
bFoundFreeSocket = true;
break;
}
}
if(!bFoundFreeSocket)
{
printf("No free sockets\n");
closesocket(listenSocket);
}
} //if(FD_ISSET(listenSocket, &fdRead))
//Check the disposition of each connected socket.
for(int i = 0; i < MAX_CONNECTIONS; i++)
{
if(peerSockets[i])
{
//Is data waiting to be received?
if(FD_ISSET(peerSockets[i], &fdRead))
{
char recvBuffer[MAX_RECV_SIZE];
int recvLength = recv(peerSockets[i],
recvBuffer, sizeof(recvBuffer), 0);
if(recvLength == SOCKET_ERROR)
{
//Disconnect the socket.
closesocket(peerSockets[i]);
peerSockets[i] = NULL;
peerBytesSent[i] = 0;
}
else if(recvLength == 0) // Gracefull disconnect.
{
//Disconnect the socket.
closesocket(peerSockets[i]);
peerSockets[i] = NULL;
peerBytesSent[i] = 0;
}
else{
recvBuffer[recvLength] = '\0';
printf("Received %d bytes: \"%s\"\n", recvLength, recvBuffer);
}
}
//Is the socket ready to accept data?
if(FD_ISSET(peerSockets[i], &fdWrite))
{
//Send some data back to the connected peer.
char *sendBuffer = "Hello World";
int bytesSent = send(peerSockets[i], sendBuffer, strlen(sendBuffer), 0);
if(bytesSent == SOCKET_ERROR)
{
int iLastError = WSAGetLastError();
if(iLastError == WSAEWOULDBLOCK || iLastError == WSAEINPROGRESS)
{
//Do nothing, try to send again on the the next go-round.
}
else{
//Disconnect the socket.
closesocket(peerSockets[i]);
peerSockets[i] = NULL;
peerBytesSent[i] = 0;
}
}
else if(bytesSent == 0)
{
//Disconnect the socket.
closesocket(peerSockets[i]);
peerSockets[i] = NULL;
peerBytesSent[i] = 0;
}
else {
//Data was sent successfully!
//Keep track of how much data we sent to each socket.
peerBytesSent[i] += bytesSent;
//Disconnect after sending more than 1KB od data.
if(peerBytesSent[i] > 1024)
{
//Disconnect the socket.
closesocket(peerSockets[i]);
peerSockets[i] = NULL;
peerBytesSent[i] = 0;
}
}
}
//Is the socket in an error state?
if(FD_ISSET(peerSockets[i], &fdExcept))
{
//Disconnect the socket.
closesocket(peerSockets[i]);
peerSockets[i] = NULL;
peerBytesSent[i] = 0;
}
}
}
Sleep(1); //Give the CPU some breathing time.
}
//---------------------------------------------------------
//Stop listening.
closesocket(listenSocket);
if(WSACleanup() == SOCKET_ERROR)
{
printf("Failed to cleanup WinSock: %d\n",
WSAGetLastError());
return 1;
}
return 0;
}
Now clearly there are more to sockets than this post covers (such
as non-blocking asynchronous sockets) but trust me, the above is easily 10 fold more
than my first socket application.
Is this code snippet, product or advice warrantied against ill-effect and/or technical malaise? No. No it's not! Not expressed - Not implied - not at all.