#include<stdio.h>#include<winsock2.h>#include"ServerWS.hxx"#include"Chat.hxx"// Public Functions //////////////////////////////////////////////////////// Constructor ///////////////////////////////////////////////////////////
//
// Description: Sets up the class for usage for either connecting to// or hosting a game. Defaults are filled in.//////////////////////////////////////////////////////////////////////////ServerWS::ServerWS(){// Must be done at the beginning of every WinSock program// Used to store information about WinSock versionWSADATAw;
interror = WSAStartup(0x0202, &w); // Fill in wif(error){// There was an error; don't have to call WSACleanup because Winsock// wasn't initialized. Since isConnected = false, we prevent problems.return;
}elseif(w.wVersion != 0x0202){// Wrong WinSock version!WSACleanup();
return;
}players = 1; // OurselvesisHost = false;
isConnected = false;
myIndex = 0;
// Allocate message buftheMesg = (WS_STRING_MSG_PTR)GlobalAllocPtr(GHND, MAX_MESGSIZE);
// Set all players disconnected, all names NULLZeroMemory(player_list, sizeof(player_list));
}// Destructor ////////////////////////////////////////////////////////////
//
// Description: Calls Disconnect() and quits.//////////////////////////////////////////////////////////////////////////ServerWS::~ServerWS(){Disconnect();
// Free our global message bufferGlobalFreePtr(theMesg);
}// CheckIP() /////////////////////////////////////////////////////////////
//
// Description: Checks to make sure the given IP string is valid. // Returns false on error, and copies the error string// into reason.//////////////////////////////////////////////////////////////////////////boolServerWS::CheckIP(char *ip, char *reason)const{intret, one, two, three, four;
// Check for NULLif(ip == NULL)returnfalse;
// Check for numbers and .'s onlyfor(unsignedinti = 0; i < strlen(ip); i++){if(!isdigit(ip[i]) && ip[i] != '.'){strcpy(reason, "Error: Bad char.");
returnfalse;
}}ret = sscanf(ip, "%d.%d.%d.%d", &one, &two, &three, &four);
// Check that four numbers were scannedif(ret != 4){strcpy(reason, "Error: Bad format.");
returnfalse;
}// Check numeric boundsif(one < 0 || two < 0 || three < 0 || four < 0){strcpy(reason, "Error: Negative number.");
returnfalse;
}if(one > 255 || two > 255 || three > 255 || four > 255){strcpy(reason, "Error: Number too big.");
returnfalse;
}// Passes all checksreturntrue;
}// Connect() /////////////////////////////////////////////////////////////
//
// Description: Given an IP address and a port, tries to connect to the// game server and sets up asynchronous communication on// the new socket.//////////////////////////////////////////////////////////////////////////boolServerWS::Connect(HWNDhwnd, UINTmsg, char *ip, intport, char *play_name){intret;
// Create the socketsock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == INVALID_SOCKET){strcpy(lasterr, "Could not create socket.");
returnfalse;
}// Set up asynchronous recepit of read/close messages at this socket...if(WSAAsyncSelect(sock, hwnd, msg, (FD_READ | FD_CLOSE | FD_CONNECT))){// Error occurredsprintf(lasterr, "WSAAsyncSelect() failed; error code %d", WSAGetLastError());
returnfalse;
}// Connect to the hostsockaddr_intarget;
target.sin_family = AF_INET;
target.sin_port = htons(port);
target.sin_addr.s_addr = inet_addr(ip);
while(true){if(connect(sock, (sockaddr*)&target, sizeof(target)) == SOCKET_ERROR){ret = WSAGetLastError();
if(ret == WSAEWOULDBLOCK)continue;
elseif(ret == WSAEISCONN)break;
// Actual errorsprintf(lasterr, "Connect() failed; Error code (%d)", GetLastError());
returnfalse;
}elsebreak;
}isConnected = true;
isHost = false;
// Set our namestrcpy(player_name, play_name);
Sleep(20);
SendNameMesg(0, 1, player_name);
returntrue;
}// Disconnect() //////////////////////////////////////////////////////////
//
// Description: Simply shuts down our socket, closes it, and sets// the "connected" attribute to false.//////////////////////////////////////////////////////////////////////////voidServerWS::Disconnect(){// Shutdown socket, close socket, disconnectshutdown(sock, SD_BOTH);
closesocket(sock);
isConnected = false;
}// GetIndex() ////////////////////////////////////////////////////////////
//
// Description: Looks through the player list and returns the index of// your own computer.//////////////////////////////////////////////////////////////////////////intServerWS::GetIndex()const{returnmyIndex;
}// GetPlayerName() ///////////////////////////////////////////////////////
//
// Description: Copies the correct player's name into the given string.//////////////////////////////////////////////////////////////////////////boolServerWS::GetPlayerName(char *name, intindex){if(index >= 0 && index < players){strcpy(name, player_list[index].name);
returntrue;
}else{strcpy(lasterr, "GetPlayerName(): Index Invalid");
returnfalse;
}}// HandleEvent() /////////////////////////////////////////////////////////
//
// Description: The application must call this when it receives a // socket message with the appropriate hwnd, msg, wparam,// and lparam. Also, mesg & type is used when a FD_READ// event has occurred--otherwise they are not changed.//////////////////////////////////////////////////////////////////////////voidServerWS::HandleEvent(HWNDhwnd, UINTmsg, WPARAMsocket, LPARAMevent,
char *mesg, int& type, int& from){if(isHost){for(inti = 0; i < players; i++)if(player_list[i].sock == socket)from = i;
}elsefrom = 0;
if(WSAGETSELECTERROR(event)){// Ignore the errorreturn;
}switch(WSAGETSELECTEVENT(event)){caseFD_CONNECT:
{if(myIndex == 0){SendNameMesg(0, 1, player_name);
Sleep(200);
PostMessage(hwnd, msg, socket, event);
}else{for(inti = 0; i < myIndex; i++){if(player_list[i].connected == false){//SendNameMesg(0, 1, player_name);break;
}}}break;
}caseFD_ACCEPT:
{// Only let maxplayers connect.if(players >= max_players)return;
// Size of sockaddr structureintaddr_size = sizeof(sockaddr_in);
// Get the next clientplayer_list[players].sock = accept(socket, (sockaddr*)&player_list[players].info, &addr_size);
// Error handlingif(player_list[players].sock != INVALID_SOCKET){// Client connected successfullyplayer_list[players].connected = true;
player_list[players].name[0] = 0;
// Increase player countplayers++;
// Send this client WS_MSG_NAME messages for players they missed// BEFORE they send one to us.for(inti = 0; i < players-1; i++){SendNameMesg(players-1, i, player_list[i].name);
Sleep(300);
}// Set up Asynchronous receipt of messages on new socketWSAAsyncSelect(player_list[players-1].sock, hwnd, msg, (FD_READ | FD_CLOSE));
}break;
}caseFD_READ:
{ReceiveMesg(socket, mesg, type, from);
break;
}caseFD_CLOSE:
{if(isHost){// Set the appropriate player's socket to INVALID_SOCKETfor(inti = 0; i < players; i++){if(player_list[i].sock == socket){player_list[i].sock = INVALID_SOCKET;
player_list[i].connected = false;
from = i;
}}// Loop again and send a DROP message to other clientsfor(i = 0; i < players; i++){if(i != from)SendDropMesg(from, i);
}// Any other handling must be done in main application code}elseisConnected = false;
break;
}default: break;
}}// HostGame() ////////////////////////////////////////////////////////////
//
// Description: Called by the main window handle to start hosting a// networked game. msg is used to specify the windows// message to send to hwnd when an asynchronous event occurs// on a socket. Also sets up a max_players limit and the// player's name.//////////////////////////////////////////////////////////////////////////boolServerWS::HostGame(HWNDhwnd, UINTmsg, intport, char *play_name, intmaxplayers){max_players = maxplayers;
myIndex = 0;
// Create socketsock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == INVALID_SOCKET){strcpy(lasterr, "Could not create socket.");
returnfalse;
}if(WSAAsyncSelect(sock, hwnd, msg, FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR){sprintf(lasterr, "Async error (%d)", WSAGetLastError());
returnfalse;
}// The address structure for a TCP socketsockaddr_inaddr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY); // No destination// Now bind the socketif(bind(sock, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR){// Error in bindstrcpy(lasterr, "Error binding port.");
returnfalse;
}isConnected = true;
isHost = true;
// Set up player_list[0] to be ourselvesstrncpy(player_list[0].name, play_name, NAMELEN);
player_list[0].name[NAMELEN-1] = 0;
player_list[0].sock = sock;
player_list[0].info = addr;
player_list[0].connected = true;
strncpy(player_name, play_name, NAMELEN);
player_name[NAMELEN-1] = 0;
// Listen to port!if(listen(sock, max_players)){// An error occurred; set lasterrsprintf(lasterr, "listen() failed; Winsock Error %d", WSAGetLastError());
isConnected = false;
returnfalse;
}returntrue;
}// IsConnected() /////////////////////////////////////////////////////////
//
// Description: Returns the current value of isConnected.//////////////////////////////////////////////////////////////////////////boolServerWS::IsConnected()const{returnisConnected;
}// IsConnected() /////////////////////////////////////////////////////////
//
// Description: Checks the player list to see if a certain client is// connected or not.//////////////////////////////////////////////////////////////////////////boolServerWS::IsConnected(intindex)const{if(index >= 0 && index < players)returnplayer_list[index].connected;
elsereturnfalse;
}// IsHost() //////////////////////////////////////////////////////////////
//
// Description: Returns the current value of isHost.//////////////////////////////////////////////////////////////////////////boolServerWS::IsHost()const{returnisHost;
}// LastError() ///////////////////////////////////////////////////////////
//
// Description: Copies the last error message set to the string specified.//////////////////////////////////////////////////////////////////////////voidServerWS::LastError(char *err)const{strcpy(err, lasterr);
}// NumPlayers() //////////////////////////////////////////////////////////
//
// Description: Returns the number of players currently in the server//////////////////////////////////////////////////////////////////////////intServerWS::NumPlayers()const{returnplayers;
}// SendChatMesg() ////////////////////////////////////////////////////////
//
// Description: Given a destination socket (on the host), a string, and// a everyone flag, will send a chat message to the server.// If everyone flag is set, then the server will relay it// to all. Otherwise the server must relay to the right // party in ReceiveMesg.//////////////////////////////////////////////////////////////////////////boolServerWS::SendChatMesg(intindexTo, LPSTRlpstr, booleveryone){// Return codeboolret = true;
// Send a pointer to a WS_STRING_MSGWS_STRING_MSG_PTRmesg;
DWORDdwMsgSize;
// Get message sizedwMsgSize = sizeof(WS_STRING_MSG)+lstrlen(lpstr);
// Allocate spacemesg = (WS_STRING_MSG_PTR)GlobalAllocPtr(GHND, dwMsgSize);
// Copy the actual messagelstrcpy(mesg->szMsg, lpstr);
// Set the source location & typemesg->dwType = WS_MSG_CHATSTRING;
mesg->dwSize = dwMsgSize;
mesg->bcast = everyone;
mesg->src = GetIndex();
mesg->dest = indexTo;
// Send the messageif(isHost){if(everyone){// Send to everyonefor(inti = 1; i < players; i++){if(player_list[i].sock != INVALID_SOCKET)if(send(player_list[i].sock, (char*)mesg, dwMsgSize, 0) == SOCKET_ERROR){sprintf(lasterr, "send() failed in SendChatMesg(); Error %d", WSAGetLastError());
ret = false;
}}}else{// Make sure socket is validif(indexTo >= 0 && indexTo < players)if(send(player_list[indexTo].sock, (char*)mesg, dwMsgSize, 0) == SOCKET_ERROR){sprintf(lasterr, "send() failed in SendChatMesg(), Error %d", WSAGetLastError());
ret = false;
}}}else// Not Hostif(send(sock, (char*)mesg, dwMsgSize, 0) == SOCKET_ERROR){sprintf(lasterr, "send() failed in SendChatMesg(), Error %d", WSAGetLastError());
ret = false;
}// Free memoryGlobalFreePtr(mesg);
returnret;
}// SendGameMesg() ////////////////////////////////////////////////////////
//
// Description: Given a destination socket (on the host), a string, and// a everyone flag, will send a chat message to the server.// If everyone flag is set, then the server will relay it// to all. Otherwise the server must relay to the right // party in ReceiveMesg.//////////////////////////////////////////////////////////////////////////boolServerWS::SendGameMesg(intindexTo, LPSTRlpstr){// Return codeboolret = true;
// Send a pointer to a WS_STRING_MSGWS_STRING_MSG_PTRmesg;
DWORDdwMsgSize;
// Get message sizedwMsgSize = sizeof(WS_STRING_MSG)+lstrlen(lpstr);
// Allocate spacemesg = (WS_STRING_MSG_PTR)GlobalAllocPtr(GHND, dwMsgSize);
// Copy the actual messagelstrcpy(mesg->szMsg, lpstr);
// Set the source location & typemesg->dwType = WS_MSG_GAMEPLAY;
mesg->dwSize = dwMsgSize;
mesg->bcast = false;
mesg->src = GetIndex();
mesg->dest = indexTo;
// Send the messageif(isHost){// Make sure socket is validif(indexTo >= 0 && indexTo < players)if(send(player_list[indexTo].sock, (char*)mesg, dwMsgSize, 0) == SOCKET_ERROR){sprintf(lasterr, "send() failed in SendGameMesg(); Error %d", WSAGetLastError());
ret = false;
}}else// Just send it to serverif(send(sock, (char*)mesg, dwMsgSize, 0) == SOCKET_ERROR){sprintf(lasterr, "send() failed in SendGameMesg(); Error %d", WSAGetLastError());
ret = false;
}// Free memoryGlobalFreePtr(mesg);
returnret;
}// Private Functions //////////////////////////////////////////////////////////// ReceiveMesg() /////////////////////////////////////////////////////////
//
// Description: If hosting, will relay message to the appropriate party.// Otherwise just returns a message back to the app.//////////////////////////////////////////////////////////////////////////boolServerWS::ReceiveMesg(SOCKETs, char *mesg, int& type, int& from){boolret = true;
// Get a copy of the messagerecv(s, (char*)theMesg, MAX_MESGSIZE, 0);
// Set valuable fields for main windowtype = theMesg->dwType;
strcpy(mesg, theMesg->szMsg);
// Do something with the message...if(isHost){// Don't trust senderfor(inti = 0; i < players; i++)if(player_list[i].sock == s)from = i;
if(theMesg->dwType == WS_MSG_NAME){if(player_list[from].name[0] == 0)// Only process first message{// Copy player's name into right spotstrncpy(player_list[from].name, theMesg->szMsg, NAMELEN);
player_list[from].name[NAMELEN-1] = 0;
// Relay to other playerstheMesg->src = from;
for(inti = 1; i < players; i++){theMesg->dest = i;
if(send(player_list[i].sock, (char*)theMesg, theMesg->dwSize, 0) == SOCKET_ERROR){sprintf(lasterr, "send() failed in ReceiveMesg(); Error %d", WSAGetLastError());
ret = false;
}else
;
}}else{// This client lost his own WS_MSG_NAME message; resend them all playerstheMesg->dest = from;
theMesg->src = from;
if(send(player_list[from].sock, (char*)theMesg, theMesg->dwSize, 0) == SOCKET_ERROR){sprintf(lasterr, "send() failed in ReceiveMesg(); Error %d", WSAGetLastError());
ret = false;
}type = -1;
}}elseif(theMesg->bcast && theMesg->dwType == WS_MSG_CHATSTRING){// Broadcast chat message; relay & processfor(inti = 1; i < players; i++)if(player_list[i].sock != s)if(send(player_list[i].sock, (char*)theMesg, theMesg->dwSize, 0) == SOCKET_ERROR){sprintf(lasterr, "send() failed in ReceiveMesg(); Error %d", WSAGetLastError());
ret = false;
}}elseif(theMesg->dest == 0){// It's for usstrcpy(mesg, theMesg->szMsg);
type = theMesg->dwType;
}elseif(theMesg->dwType == WS_MSG_CHATSTRING){// Relay to someone elseif(theMesg->dest > 0 && theMesg->dest < players)if(send(player_list[theMesg->dest].sock, (char*)theMesg, theMesg->dwSize, 0) == SOCKET_ERROR){sprintf(lasterr, "send() failed in ReceiveMesg(); Error %d", WSAGetLastError());
ret = false;
}// Don't process in main windowtype = -1;
}}else// Client{from = theMesg->src;
if(theMesg->dwType == WS_MSG_NAME){if(player_list[from].connected == false/* || player_list[from].name[0] == 0*/){// Copy name, set player connected, increment player countstrncpy(player_list[from].name, theMesg->szMsg, NAMELEN);
player_list[from].name[NAMELEN-1] = 0;
if(player_list[from].connected == false)players++;
player_list[from].connected = true;
}// The NAME message is from ourselves; set our indexif(theMesg->dest == from && !myIndex)myIndex = from;
elsetype = -1;
}elseif(theMesg->dwType == WS_MSG_DROP){// Make sure we know this client has droppedplayer_list[from].connected = false;
}else{// We are the client; let main window process mesg. strcpy(mesg, theMesg->szMsg);
type = theMesg->dwType;
}}returnret;
}// SendDropMesg() /////////////////////////////////////////////////////////////
//
// Description: Only the host should call this, and it calls it when it gets// an FD_CLOSE message. A drop message is then sent to all other// clients to notify them of this.///////////////////////////////////////////////////////////////////////////////boolServerWS::SendDropMesg(intwho, intindexTo){boolret = true;
// Send a pointer to a WS_STRING_MSGWS_STRING_MSG_PTRmesg;
DWORDdwMsgSize;
// Get message sizedwMsgSize = sizeof(WS_STRING_MSG)+1;
// Allocate spacemesg = (WS_STRING_MSG_PTR)GlobalAllocPtr(GHND, dwMsgSize);
// Copy the actual messagelstrcpy(mesg->szMsg, "");
// Set the source location & typemesg->dwType = WS_MSG_DROP;
mesg->dwSize = dwMsgSize;
mesg->bcast = false;
mesg->src = who;
mesg->dest = indexTo;
// Send the messageif(isHost){// Make sure socket is validif(indexTo >= 0 && indexTo < players)if(send(player_list[indexTo].sock, (char*)mesg, dwMsgSize, 0) == SOCKET_ERROR){sprintf(lasterr, "send() failed in SendNameMesg(); Error %d", WSAGetLastError());
ret = false;
}}// Free memoryGlobalFreePtr(mesg);
returnret;
}// SendNameMesg() ////////////////////////////////////////////////////////
//
// Description: Given a destination index (on the host), and a string,// will send a NAME message to the specified index.//////////////////////////////////////////////////////////////////////////boolServerWS::SendNameMesg(intindexTo, intindexFrom, LPSTRlpstr){boolret = true;
// Send a pointer to a WS_STRING_MSGWS_STRING_MSG_PTRmesg;
DWORDdwMsgSize;
// Get message sizedwMsgSize = sizeof(WS_STRING_MSG)+lstrlen(lpstr);
// Allocate spacemesg = (WS_STRING_MSG_PTR)GlobalAllocPtr(GHND, dwMsgSize);
// Copy the actual messagelstrcpy(mesg->szMsg, lpstr);
// Set the source location & typemesg->dwType = WS_MSG_NAME;
mesg->dwSize = dwMsgSize;
mesg->bcast = false;
mesg->src = indexFrom;
mesg->dest = indexTo;
// Send the messageif(isHost){// Make sure socket is validif(indexTo >= 0 && indexTo < players){if(send(player_list[indexTo].sock, (char*)mesg, dwMsgSize, 0) == SOCKET_ERROR){sprintf(lasterr, "send() failed in SendNameMesg(); Error %d", WSAGetLastError());
ret = false;
}}}else{// Send to hostif(send(sock, (char*)mesg, dwMsgSize, 0) == SOCKET_ERROR){sprintf(lasterr, "send() failed in SendNameMesg(); Error %d", WSAGetLastError());
ret = false;
}}// Free memoryGlobalFreePtr(mesg);
returnret;
}
All material Copyright (c) 2001-2010 Hexar
Please direct all inquiries, love letters, and hate mail to
.