Download this source code in text format (Communication.cpp)
/***********************************************************
* WindowMud - Window Mud Server *
* File: Communication.cpp *
* Usage: Winsock tcp/ip telnet player communications *
* *
* No warranty is given or implied. *
* There is no license associated with this code. *
* There are no restrictions on the use of this code. *
* This code may be used in any way the reader wishes. *
* No credit or credits are given and none are required. *
* The reader may take this code and claim they wrote it. *
************************************************************/
/***********************************************************
* Includes *
************************************************************/
#include "stdafx.h" // precompiled headers
#include "Communication.h"
// Server
#include "Dnode.h"
#include "Descriptor.h"
#include "Log.h"
#include "Utility.h"
/***********************************************************
* Globals *
************************************************************/
extern CString ErrorMsg;
extern bool StateConnections;
extern bool StateRunning;
extern bool StateStopping;
extern bool WinSockCleanUp;
Dnode *pDnodeActor;
fd_set ExcSet;
fd_set InpSet;
int ListenSocket;
fd_set OutSet;
/***********************************************************
* Communication class constructor *
***********************************************************/
Communication::Communication()
{
}
/***********************************************************
* Communication class destructor *
***********************************************************/
Communication::~Communication()
{
}
////////////////////////////////////////////////////////////
// Public functions static //
////////////////////////////////////////////////////////////
/***********************************************************
* Check for new connections *
***********************************************************/
void Communication::SockCheckForNewConnections()
{
Dnode *pDnodeActor;
int SocketCount;
static struct timeval TimeOut;
TimeOut.tv_sec = 0;
TimeOut.tv_usec = 1;
FD_ZERO(&InpSet);
FD_ZERO(&OutSet);
FD_ZERO(&ExcSet);
FD_SET(ListenSocket, &InpSet);
// Check status of connections
Descriptor::SetpDnodeCursorFirst();
while (!Descriptor::EndOfDnodeList())
{ // Loop thru all connections
pDnodeActor = Descriptor::GetDnode();
FD_SET(pDnodeActor->DnodeFd, &InpSet);
FD_SET(pDnodeActor->DnodeFd, &OutSet);
FD_SET(pDnodeActor->DnodeFd, &ExcSet);
Descriptor::SetpDnodeCursorNext();
}
// Detect socket state (pg 159-161)
SocketCount = select(-1, &InpSet, &OutSet, &ExcSet, &TimeOut);
if (SocketCount == -1)
{ // Select failed
ErrorMsg.Format("%s", strerror(errno));
ErrorMsg = "Communication::SockCheckForNewConnections: select: " + ErrorMsg;
Utility::BlowUp();
}
if (FD_ISSET(ListenSocket, &InpSet))
{ // Process new connection
SockNewConnection();
}
}
/***********************************************************
* Close port *
***********************************************************/
void Communication::SockClosePort(int Port)
{
CString LogBuf;
int Result;
// Close the socket (pg 70)
Result = ::closesocket(ListenSocket);
if (Result!= 0)
{ // Sock error on close
ErrorMsg = "Communication::SockClosePort - Error: closesocket";
Utility::BlowUp();
}
LogBuf.Format("%d", Port);
LogBuf = "Closed port " + LogBuf;
Log::LogIt(LogBuf);
// Free WinSock resources
WinSockCleanUp = false;
Result = WSACleanup();
if (Result!= 0)
{ // WinSock cleanup failed
ErrorMsg = "Communication::SockClosePort - Error: WSACleanup";
Utility::BlowUp();
}
}
/***********************************************************
* Open port *
***********************************************************/
void Communication::SockOpenPort(int Port)
{
unsigned long FionbioParm;
struct linger ld;
CString LogBuf;
int OptionValue;
int Result;
struct sockaddr_in sa;
WORD VersionRequested;
WSADATA WsaData;
FionbioParm = 1;
ld.l_onoff = 0;
ld.l_linger = 0;
OptionValue = 1;
VersionRequested = MAKEWORD(1, 1);
// Initialize WinSock API (pg 320)
Result = WSAStartup(VersionRequested, &WsaData);
if (Result != 0)
{ // WinSock is not available
ErrorMsg.Format("%s", strerror(errno));
ErrorMsg = "Communication::SockOpenPort - WinSock not available: " + ErrorMsg;
Utility::BlowUp();
}
WinSockCleanUp = true;
// Establish a streaming socket (pg 51)
ListenSocket = socket(AF_INET, SOCK_STREAM, 0);
if (ListenSocket == SOCKET_ERROR)
{ // No socket, time to die
ErrorMsg.Format("%s", strerror(errno));
ErrorMsg = "Communication:SockOpenPort - Error: initializing socket: " + ErrorMsg;
Utility::BlowUp();
}
// Enable reuse of local socket name (pg 305)
Result = setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&OptionValue, sizeof(OptionValue));
if (Result != 0)
{ // Set socket option failed
ErrorMsg.Format("%s", strerror(errno));
ErrorMsg = "Communication:SockOpenPort - Error: setsockopt: SOL_SOCKET SO_REUSEADDR" + ErrorMsg;
Utility::BlowUp();
}
// Establish underlying TCP/IP buffer size (pg 305)
Result = setsockopt(ListenSocket, SOL_SOCKET, SO_SNDBUF, (char *) &OptionValue, sizeof(OptionValue));
if (Result != 0)
{ // Set socket option failed
ErrorMsg.Format("%s", strerror(errno));
ErrorMsg = "Communication:SockOpenPort - Error: setsockopt SNDBUF: " + ErrorMsg;
Utility::BlowUp();
}
// Disable linger and set timeout to zero (pg 301)
Result = setsockopt(ListenSocket, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld));
if (Result != 0)
{ // Set socket option failed
ErrorMsg.Format("%s", strerror(errno));
ErrorMsg = "Communication:SockOpenPort - Error: setsockopt SO_LINGER: " + ErrorMsg;
Utility::BlowUp();
}
// Initialize sockaddr structure (pg 53)
memset((char *)&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
sa.sin_port = htons(Port);
// Associate a local address with the socket (pg 56)
Result = bind(ListenSocket, (struct sockaddr *)&sa, sizeof(sa));
if (Result != 0)
{ // Bind socket failed
ErrorMsg.Format("%s", strerror(errno));
ErrorMsg = "Communication:SockOpenPort - Error: bind " + ErrorMsg;
Utility::BlowUp();
}
// Make socket nonblocking (pg 286)
Result = ioctlsocket(ListenSocket, FIONBIO, &FionbioParm);
if (Result != 0)
{ // Failed to make socket nonblocking
ErrorMsg.Format("%s", strerror(errno));
ErrorMsg = "Communication:SockOpenPort ioctlsocket: " + ErrorMsg;
Utility::BlowUp();
}
// Listen on port and limit pending connections (pg 60)
Result = listen(ListenSocket, 20);
if (Result != 0)
{ // Listen failed
ErrorMsg.Format("%s", strerror(errno));
ErrorMsg = "Communication:SockOpenPort - Error: listen " + ErrorMsg;
Utility::BlowUp();
}
LogBuf.Format("%d", Port);
LogBuf = "Listening on port " + LogBuf;
Log::LogIt(LogBuf);
}
/***********************************************************
* Receive player input, check player status, send output *
***********************************************************/
void Communication::SockRecv()
{
int ConnectionCount;
int DnodeFdSave;
char InpStr[MAX_INPUT_LENGTH];
CString LogBuf;
int RecvByteCount;
//************************
//* Is Game is stopping? *
//************************
if (StateStopping)
{ // Game is stopping
Descriptor::SetpDnodeCursorFirst();
while (!Descriptor::EndOfDnodeList())
{ // Loop thru all connections
pDnodeActor = Descriptor::GetDnode();
if (!pDnodeActor->PlayerStateBye)
{ // If player is not already going Bye Bye
pDnodeActor->PlayerStateBye = true;
pDnodeActor->PlayerStateSendBanner = false;
pDnodeActor->PlayerOut += "\r\n";
pDnodeActor->PlayerOut += "Game is stopping ... Bye Bye!";
pDnodeActor->PlayerOut += "\r\n";
LogBuf = "Unknown";
LogBuf += " will be force disconnected";
Log::LogIt(LogBuf);
}
Descriptor::SetpDnodeCursorNext();
}
}
//***********************
//* Service connections *
//***********************
Descriptor::SetpDnodeCursorFirst();
while (!Descriptor::EndOfDnodeList())
{ // Loop thru all connections
pDnodeActor = Descriptor::GetDnode();
//***************************
//* Check connection status *
//***************************
if (FD_ISSET(pDnodeActor->DnodeFd, &OutSet))
{ // This code don't do nothin', sample OutSet check
int dummy = 0;
}
if (FD_ISSET(pDnodeActor->DnodeFd, &ExcSet))
{ // Kick out connections with exceptions
pDnodeActor->PlayerStateBye = true;
}
else
{ // Good connection
if (FD_ISSET(pDnodeActor->DnodeFd, &InpSet))
{ // Receive
memset(InpStr, '\0', sizeof(InpStr));
RecvByteCount = ::recv(pDnodeActor->DnodeFd, InpStr, MAX_INPUT_LENGTH-1, 0);
if (RecvByteCount == 0)
{ // Should be input but there is none -- disconnected ??
pDnodeActor->PlayerStateBye = true;
}
if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINTR)
{ // Nothing worth processing
RecvByteCount = 0;
}
if (RecvByteCount > 0)
{ // Got something ... append it to player input
pDnodeActor->PlayerInp += InpStr;
}
}
}
//*****************************
//* Banner for new connection *
//*****************************
if (pDnodeActor->PlayerStateSendBanner)
{ // New connection
pDnodeActor->PlayerStateSendBanner = false;
// Send greeting
pDnodeActor->PlayerOut += "\r\n";
pDnodeActor->PlayerOut += "Create a new character Y-N?";
pDnodeActor->PlayerOut += "\r\n";
}
//**********************
//* Send player output *
//**********************
if (pDnodeActor->PlayerOut.GetLength() > 0)
{ // 'send' resquires a standard c string
SockSend((LPCTSTR) pDnodeActor->PlayerOut);
}
//**********************
//* Is player quiting? *
//**********************
if (pDnodeActor->PlayerStateBye)
{ // Player leaving the game ... disconnect them
DnodeFdSave = pDnodeActor->DnodeFd;
if (Descriptor::DeleteNode())
{ // When connection is deleted from list, log it
LogBuf.Format("%d", DnodeFdSave);
LogBuf = "Closed connection on descriptor " + LogBuf;
Log::LogIt(LogBuf);
ConnectionCount = Dnode::GetCount();
if (ConnectionCount == 1)
{ // Connection count is one means no players are connected
if (StateStopping)
{ // OMugs is stopping
StateRunning = false;
}
}
}
// Skip to next dnode, this player's dnode has been deleted
Descriptor::SetpDnodeCursorNext();
continue;
}
// Get the next Dnode to process
Descriptor::SetpDnodeCursorNext();
}
}
////////////////////////////////////////////////////////////
// Private functions static //
////////////////////////////////////////////////////////////
/***********************************************************
* New connection *
***********************************************************/
void Communication::SockNewConnection()
{
unsigned long FionbioParm;
CString LogBuf;
int Result;
struct sockaddr_in Sock;
int SocketHandle;
int SocketSize;
CString IpAddress;
CString TmpStr;
FionbioParm = 1;
SocketSize = sizeof(Sock);
// Return a new socket for a newly created connection (pg 63)
SocketHandle = accept(ListenSocket, (struct sockaddr *)&Sock, &SocketSize);
if (!SocketHandle)
{ // Accept failed
ErrorMsg.Format("%s", strerror(errno));
ErrorMsg = "Communication::SockNewConnection - Error: accept: " + ErrorMsg;
Utility::BlowUp();
}
IpAddress = inet_ntoa(Sock.sin_addr);
// Make socket nonblocking (pg 286)
Result = ioctlsocket(SocketHandle, FIONBIO, &FionbioParm);
if (Result != 0)
{ // Failed to make socket nonblocking
ErrorMsg.Format("%s", strerror(errno));
ErrorMsg = "Communication::SockNewConnection - Error: ioctlsocket " + ErrorMsg;
Utility::BlowUp();
}
TmpStr.Format("%d", SocketHandle);
LogBuf = "New connection with socket handle ";
LogBuf += TmpStr;
LogBuf += " and address ";
LogBuf += IpAddress;
Log::LogIt(LogBuf);
pDnodeActor = new Dnode(SocketHandle, IpAddress);
Descriptor::AppendIt();
StateConnections = true;
}
/***********************************************************
* Send message *
***********************************************************/
void Communication::SockSend(const char *SendBuffer)
{
int Length;
int Written;
if (!SendBuffer || SendBuffer[0] == '\0')
{ // Nothing to send
return;
}
Length = strlen(SendBuffer);
Written = ::send(pDnodeActor->DnodeFd, SendBuffer, Length, 0);
if (Written == SOCKET_ERROR)
{ // Send failed
ErrorMsg.Format("%s", strerror(errno));
ErrorMsg = "Communication::SockSend - Error: send: " + ErrorMsg;
Utility::BlowUp();
}
if (Written == Length)
{ // Everything was sent
pDnodeActor->PlayerOut = "";
}
else
{ // Some was not sent
pDnodeActor->PlayerOut = pDnodeActor->PlayerOut.Right(Length-Written);
}
}